{$IFDEF FPC} {$DEFINE NOT_USE_KOL_ERR} {$MODE Delphi} {$ASMMODE intel} {$GOTO ON} {$ENDIF} unit KOLGraphicColor; // This file is part of the image library GraphicEx (www.lischke-online.de/Graphics.html). // // GraphicColor contains the implementation of the color conversion manager. // This class is responsible for converting between these color schemes/formats: // - RGB(A) // - BGR(A) // - CMY(K) // - CIE L*a*b* // - PhotoYCC, standard YCbCr // - indexed // - grayscale (with alpha, which is ignored currently) // // Additional tasks are: // - coversions between bit depths (1,2,4,8,16 bits) // - palette creation // - gamma tables creation and application // - masked pixel transfer for interlaced images // - big endian swap // - plane (planar) -> interleaved (interlaced) conversion // // Notes: // - Throughout the entire unit I used the terms BPS and SPP for "bits per sample" and // "samples per pixel", respectively. A sample is one component per pixel. For indexed color schemes // there's only 1 sample per pixel, for RGB there are 3 (red, green and blue) and so on. // - The bit depth of multi sample formats like RGB must be equal for each color component. // - Because of the large amount of possible combinations (color schemes, sample depth, gamma, byte swap) // I limited the accepted combinations to pratical ones. This leaves currently out: // + gamma correction for 16 bit values // + conversion to 16 bit (target) grayscale with alpha // + samples sizes less than 8 bits for multi-sample schemes (RGB etc.) // + indexed schemes with planes (e.g. 16 colors indexed as 4 planes each with one bit per sample) // - For now there is no conversion between indexed and non-indexed formats. Also between grayscale // and any other scheme is no conversion possible. // // (c) Copyright 1999, 2000 Dipl. Ing. Mike Lischke (public@lischke-online.de). All rights reserved. // // This package is freeware for non-commercial use only. // Contact author for licenses (shareware@lischke-online.de) and see License.txt which comes with the package. ////////////////////////////////////////////////// // Converted to KOL by Dimaxx (dimaxx@atnet.ru) // ////////////////////////////////////////////////// interface {$ALIGN OFF} {$I KOLDEF.INC} uses Windows, KOL, Errors, {$IFDEF NOT_USE_KOL_ERR}sysutils {$ELSE}Err {$ENDIF}; const // this is the value for average CRT monitors, adjust it if your monitor differs DefaultDisplayGamma = 2.2; type TColorScheme = ( csUnknown, // not (yet) defined color scheme csIndexed, // any palette format csG, // gray scale csGA, // gray scale with alpha channel csRGB, // red, green, blue csRGBA, // RGB with alpha channel csBGR, // RGB in reversed order (used under Windows) csBGRA, // BGR with alpha channel (alpha is always the last component) csCMY, // cyan, magenta, yellow (used mainly for printing processes) csCMYK, // CMY with black csCIELab, // CIE color format using luminance and chromaticities csYCbCr, // another format using luminance and chromaticities csPhotoYCC // a modified YCbCr version used for photo CDs ); TConvertOptions = set of ( coAlpha, // alpha channel is to be considered (this value is // usually automatically set depending on the color scheme) coApplyGamma, // target only, gamma correction must take place coNeedByteSwap, // endian switch needed coLabByteRange, // CIE L*a*b* only, luminance range is from 0..255 instead 0..100 coLabChromaOffset); // CIE L*a*b* only, chrominance values a and b are given in 0..255 instead -128..127 // format of the raw data to create a color palette from TRawPaletteFormat = ( pfInterlaced8Triple, // rgb triple with 8 bits per component pfInterlaced8Quad, // rgb quad with 8 bits per component (fourth entry is reserved as in Windows' logical palette) pfPlane8Triple, // 3 separate planes of data with 8 bits per component pfPlane8Quad, pfInterlaced16Triple, // rgb triple with 16 bits per component pfInterlaced16Quad, pfPlane16Triple, // 3 separate planes of data with 16 bits per component pfPlane16Quad); // TConversionMethod describes the general parameter list to which each implemented conversion method conforms. // Note: Source is defined as open array parameter to allow plane and interlaced source data. TConversionMethod = procedure(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte) of object; PColorManager = ^TColorManager; TColorManager = object(TObj) private FChanged: boolean; // set if any of the parameters changed FSourceBPS, // bits per sample of source data (allowed values are 1, 2, 4, 8, 16) FTargetBPS, // bits per sample of target data (allowed values are 1, 2, 4, 8, 16) FSourceSPP, // samples per source pixel (allowed values are 1, 3, 4) FTargetSPP: byte; // samples per target pixel (allowed values are 1, 3, 4) FMainGamma, // primary gamma value which is usually read from a file (default is 1) FDisplayGamma: single; // (constant) gamma value of the current monitor (default is 2.2) FGammaTable: array[Byte] of byte; // contains precalculated gamma values for each possible component value // (range is 0..255) FYCbCrCoefficients: array[0..2] of single; FHSubsampling, FVSubSampling: byte; // additional parameters used for YCbCr conversion FCrToRedTable, // lookup tables used for YCbCr conversion FCbToBlueTable, FCrToGreenTable, FCbToGreenTable: array of integer; FSourceScheme,FTargetScheme: TColorScheme; FRowConversion: TConversionMethod; // procedure variable for the actual conversion method used FSourceOptions, FTargetOptions: TConvertOptions; // options to control conversion protected // Low level conversion helper used to convert one pixel component. function ComponentGammaConvert(Value: byte): byte; function ComponentNoConvert16(Value: word): word; function ComponentNoConvert8(Value: byte): byte; function ComponentScaleConvert(Value: word): byte; function ComponentScaleGammaConvert(Value: word): byte; function ComponentSwapScaleGammaConvert(Value: Word): byte; function ComponentSwapScaleConvert(Value: word): byte; function ComponentSwapConvert(Value: word): word; // row conversion routines procedure RowConvertBGR2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertBGR2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertCIELAB2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertCIELAB2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertCMYK2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertCMYK2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertGray(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertIndexed8(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertIndexedBoth16(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertIndexedSource16(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertIndexedTarget16(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertRGB2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertRGB2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertPhotoYCC2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertPhotoYCC2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertYCbCr2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure RowConvertYCbCr2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // other general routines procedure CreateYCbCrLookup; function GetPixelFormat(Index: integer): TPixelFormat; procedure PrepareConversion; procedure SetSourceBitsPerSample(const Value: byte); procedure SetSourceColorScheme(const Value: TColorScheme); procedure SetSourceSamplesPerPixel(const Value: byte); procedure SetTargetBitsPerSample(const Value: byte); procedure SetTargetColorScheme(const Value: TColorScheme); procedure SetTargetSamplesPerPixel(const Value: byte); public // constructor Create; procedure ConvertRow(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); procedure CreateColorPalette(BMP: PBitmap; Data: array of pointer; DataFormat: TRawPaletteFormat; ColorCount: cardinal; RGB: boolean); procedure CreateGrayscalePalette(BMP: PBitmap; MinimumIsWhite: boolean); procedure Error(Code: integer); procedure SetGamma(MainGamma: single; DisplayGamma: single = DefaultDisplayGamma); procedure SetYCbCrParameters(Values: array of single; HSubSampling,VSubSampling: byte); property SourceBitsPerSample: byte read FSourceBPS write SetSourceBitsPerSample; property SourceColorScheme: TColorScheme read FSourceScheme write SetSourceColorScheme; property SourceOptions: TConvertOptions read FSourceOptions write FSourceOptions; property SourcePixelFormat: TPixelFormat index 0 read GetPixelFormat; property SourceSamplesPerPixel: byte read FSourceSPP write SetSourceSamplesPerPixel; property TargetBitsPerSample: byte read FTargetBPS write SetTargetBitsPerSample; property TargetColorScheme: TColorScheme read FTargetScheme write SetTargetColorScheme; property TargetOptions: TConvertOptions read FTargetOptions write FTargetOptions; property TargetPixelFormat: TPixelFormat index 1 read GetPixelFormat; property TargetSamplesPerPixel: byte read FTargetSPP write SetTargetSamplesPerPixel; end; function NewColorManager: PColorManager; function ClampByte(Value: integer): byte; function MulDiv16(Number,Numerator,Denominator: word): word; //---------------------------------------------------------------------------------------------------------------------- implementation uses {$IFDEF NOT_USE_KOL_ERR}math{$ELSE}KolMath{$ENDIF}; type PCMYK = ^TCMYK; TCMYK = packed record C,M,Y,K: byte; end; PCMYK16 = ^TCMYK16; TCMYK16 = packed record C,M,Y,K: word; end; PCMY = ^TCMY; TCMY = packed record C,M,Y: byte; end; PCMY16 = ^TCMY16; TCMY16 = packed record C,M,Y: word; end; PRGB = ^TRGB; TRGB = packed record R,G,B: byte; end; PRGB16 = ^TRGB16; TRGB16 = packed record R,G,B: word; end; PRGBA = ^TRGBA; TRGBA = packed record R,G,B,A: byte; end; PRGBA16 = ^TRGBA16; TRGBA16 = packed record R,G,B,A: word; end; PBGR = ^TBGR; TBGR = packed record B,G,R: byte; end; PBGR16 = ^TBGR16; TBGR16 = packed record B,G,R: word; end; PBGRA = ^TBGRA; TBGRA = packed record B,G,R,A: byte; end; PBGRA16 = ^TBGRA16; TBGRA16 = packed record B,G,R,A: word; end; //----------------- helper functions ----------------------------------------------------------------------------------- function ClampByte(Value: integer): byte; // ensures Value is in the range 0..255, values<0 are clamped to 0 and values > 255 are clamped to 255 asm OR EAX,EAX JNS @@positive XOR EAX,EAX RET @@positive: CMP EAX,255 JBE @@OK MOV EAX,255 @@OK: end; //---------------------------------------------------------------------------------------------------------------------- function MulDiv16(Number,Numerator,Denominator: word): word; // faster equivalent to Windows' MulDiv function // Number is passed via AX // Numerator is passed via DX // Denominator is passed via CX // Result is passed via AX // Note: No error checking takes place. Denominator must be > 0! asm MUL DX DIV CX end; //----------------- TColorManager -------------------------------------------------------------------------------------- function NewColorManager: PColorManager; // set some default values begin New(Result,Create); Result.FSourceBPS:=8; Result.FTargetBPS:=8; Result.FSourceSPP:=3; // 24 bit format Result.FTargetSPP:=3; // 24 bit format Result.SetGamma(1,DefaultDisplayGamma); Result.FSourceScheme:=csRGB; Result.FTargetScheme:=csBGR; // defaults are from CCIR Recommendation 601-1 Result.FYCbCrCoefficients[0]:=0.299; Result.FYCbCrCoefficients[1]:=0.587; Result.FYCbCrCoefficients[2]:=0.114; Result.FHSubSampling:=1; Result.FVSubSampling:=1; Result.FChanged:=True; end; //----------------- low level conversion routines ---------------------------------------------------------------------- // These routines are used for conversions from 16 to 8 bit values, either with gamma correction or byte swap (or both). function TColorManager.ComponentNoConvert8(Value: byte): byte; begin Result:=Value; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentNoConvert16(Value: word): word; begin Result:=Value; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentGammaConvert(Value: byte): byte; begin Result:=FGammaTable[Value]; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentScaleConvert(Value: word): byte; begin Result:=MulDiv16(Value,255,65535); end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentScaleGammaConvert(Value: word): byte; begin Result:=FGammaTable[MulDiv16(Value,255,65535)]; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentSwapScaleGammaConvert(Value: word): byte; begin Result:=FGammaTable[MulDiv16(System.Swap(Value),255,65535)]; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentSwapScaleConvert(Value: word): byte; begin Result:=MulDiv16(System.Swap(Value),255,65535); end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.ComponentSwapConvert(Value: word): word; begin Result:=System.Swap(Value); end; //----------------- row conversion routines ---------------------------------------------------------------------------- // Notes: Each method takes parameters for source and target data as well as the count of pixels to work on. This count // determines the number of pixels in the target buffer. The actual source count may differ for special color // schemes (like YCbCr) or interlaced lines. // Mask is a parameter which determines (in a repeative manner) which source pixel should actually be transferred // to the target buffer. A 1 in the corresponding bit (MSB is leftmost pixel) causes the transfer to happen. // Usually, this parameter is $FF to transfer all pixels, but for interlaced images (e.g. as in PNG format) // this will differ to limit pixel transfers. The bit mask only describes which target pixel is to skip. Source // pixel must be packed. // Windows DIBs are always byte aligned, so we don't need checks for byte alignments (in target). procedure TColorManager.RowConvertBGR2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // same as ConvertBGR2RGB but for BGR target schemes var SourceR16,SourceG16,SourceB16,SourceA16: PWord; SourceR8,SourceG8,SourceB8,SourceA8: PByte; TargetRun16: PBGR16; TargetRunA16: PBGRA16; TargetRun8: PBGR; TargetRunA8: PBGRA; BitRun: byte; Convert8_8: function(Value: byte): byte of object; Convert16_8: function(Value: word): byte of object; Convert16_8Alpha: function(Value: word): byte of object; Convert16_16: function(Value: word): word of object; SourceIncrement,TargetIncrement: cardinal; CopyAlpha: boolean; begin BitRun:=$80; // determine alpha handling once CopyAlpha:=False; if coAlpha in FSourceOptions then begin SourceIncrement:=sizeof(TRGBA); TargetIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then CopyAlpha:=True; end else begin SourceIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then TargetIncrement:=sizeof(TRGBA) else TargetIncrement:=sizeof(TRGB); end; // in planar mode source increment is always 1 if Length(Source)>1 then SourceIncrement:=1; case FSourceBPS of 8: begin if Length(Source)=1 then begin // interleaved mode SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; case FTargetBPS of 8: begin // 888 to 888 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert8_8(SourceR8^); TargetRunA8.G:=Convert8_8(SourceG8^); TargetRunA8.B:=Convert8_8(SourceB8^); // alpha values are never gamma corrected TargetRunA8.A:=SourceA8^; Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert8_8(SourceR8^); TargetRun8.G:=Convert8_8(SourceG8^); TargetRun8.B:=Convert8_8(SourceB8^); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 888 to 161616 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRunA16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRunA16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); TargetRunA16.A:=Convert16_16(MulDiv16(SourceA8^,65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRun16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRun16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Pword(TargetRun16),TargetIncrement); end; end; end; end; end; 16: begin if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; case FTargetBPS of 8: begin // 161616 to 888 if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleGammaConvert else Convert16_8:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleConvert else Convert16_8:=ComponentScaleConvert; end; // since alpha channels are never gamma corrected we need a separate conversion routine if coNeedbyteSwap in FSourceOptions then Convert16_8Alpha:=ComponentSwapScaleConvert else Convert16_8Alpha:=ComponentScaleConvert; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert16_8(SourceR16^); TargetRunA8.G:=Convert16_8(SourceG16^); TargetRunA8.B:=Convert16_8(SourceB16^); TargetRunA8.A:=Convert16_8Alpha(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert16_8(SourceR16^); TargetRun8.G:=Convert16_8(SourceG16^); TargetRun8.B:=Convert16_8(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Pbyte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 161616 to 161616 // no gamma correction for 16 bit samples yet if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(SourceR16^); TargetRunA16.G:=Convert16_16(SourceG16^); TargetRunA16.B:=Convert16_16(SourceB16^); TargetRunA16.A:=Convert16_16(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(SourceR16^); TargetRun16.G:=Convert16_16(SourceG16^); TargetRun16.B:=Convert16_16(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertBGR2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // Converts BGR source schemes to RGB target schemes and takes care for byte swapping, alpha copy/skip and // gamma correction. var SourceR16,SourceG16,SourceB16,SourceA16: PWord; SourceR8,SourceG8,SourceB8,SourceA8: PByte; TargetRun16: PRGB16; TargetRunA16: PRGBA16; TargetRun8: PRGB; TargetRunA8: PRGBA; BitRun: byte; Convert8_8: function(Value: byte): byte of object; Convert16_8: function(Value: word): byte of object; Convert16_8Alpha: function(Value: word): byte of object; Convert16_16: function(Value: word): word of object; SourceIncrement,TargetIncrement: cardinal; CopyAlpha: boolean; begin BitRun:=$80; // determine alpha handling once CopyAlpha:=False; if coAlpha in FSourceOptions then begin SourceIncrement:=SizeOf(TRGBA); TargetIncrement:=SizeOf(TRGB); if coAlpha in FTargetOptions then CopyAlpha:=True; end else begin SourceIncrement:=SizeOf(TRGB); if coAlpha in FTargetOptions then TargetIncrement:=sizeof(TRGBA) else TargetIncrement:=sizeof(TRGB); end; // in planar mode source increment is always 1 if Length(Source)>1 then SourceIncrement:=1; case FSourceBPS of 8: begin if Length(Source)=1 then begin // interleaved mode SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; case FTargetBPS of 8: begin // 888 to 888 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert8_8(SourceR8^); TargetRunA8.G:=Convert8_8(SourceG8^); TargetRunA8.B:=Convert8_8(SourceB8^); // alpha values are never gamma corrected TargetRunA8.A:=SourceA8^; Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert8_8(SourceR8^); TargetRun8.G:=Convert8_8(SourceG8^); TargetRun8.B:=Convert8_8(SourceB8^); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 888 to 161616 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRunA16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRunA16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); TargetRunA16.A:=Convert16_16(MulDiv16(SourceA8^,65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^), 65535, 255)); TargetRun16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^), 65535, 255)); TargetRun16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^), 65535, 255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; 16: begin if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; case FTargetBPS of 8: begin // 161616 to 888 if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleGammaConvert else Convert16_8:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleConvert else Convert16_8:=ComponentScaleConvert; end; // since alpha channels are never gamma corrected we need a separate conversion routine if coNeedbyteSwap in FSourceOptions then Convert16_8Alpha:=ComponentSwapScaleConvert else Convert16_8Alpha:=ComponentScaleConvert; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert16_8(SourceR16^); TargetRunA8.G:=Convert16_8(SourceG16^); TargetRunA8.B:=Convert16_8(SourceB16^); TargetRunA8.A:=Convert16_8Alpha(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert16_8(SourceR16^); TargetRun8.G:=Convert16_8(SourceG16^); TargetRun8.B:=Convert16_8(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 161616 to 161616 // no gamma correction for 16 bit samples yet if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(SourceR16^); TargetRunA16.G:=Convert16_16(SourceG16^); TargetRunA16.B:=Convert16_16(SourceB16^); TargetRunA16.A:=Convert16_16(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(SourceR16^); TargetRun16.G:=Convert16_16(SourceG16^); TargetRun16.B:=Convert16_16(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertCIELAB2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // conversion of the CIE L*a*b color space to BGR using a two way approach assuming a D65 white point, // first a conversion to CIE XYZ is performed and then from there to RGB var LRun8,aRun8,bRun8: PByte; LRun16,aRun16,bRun16: PWord; L,A,B,X,Y,Z, // color values in float format T, YYn3: extended; // intermediate results Target8: PByte; Target16: PWord; Increment,AlphaSkip: integer; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin LRun8:=Source[0]; aRun8:=LRun8; Inc(aRun8); bRun8:=aRun8; Inc(bRun8); Increment:=3; end else begin LRun8:=Source[0]; aRun8:=Source[1]; bRun8:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin /// 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun8^/2.55 else L:=LRun8^; Inc(LRun8, Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun8^-128; Inc(aRun8,Increment); B:=bRun8^-128; Inc(bRun8,Increment); end else begin A:=ShortInt(aRun8^); Inc(aRun8,Increment); B:=ShortInt(bRun8^); Inc(bRun8,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // once we have CIE XYZ it is easy (yet quite expensive) // to calculate RGB values from this // blue Target8^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target8); // green Target8^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target8); // red Target8^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun8^/2.55 else L:=LRun8^; Inc(LRun8,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun8^-128; Inc(aRun8,Increment); B:=bRun8^-128; Inc(bRun8,Increment); end else begin A:=ShortInt(aRun8^); Inc(aRun8,Increment); B:=ShortInt(bRun8^); Inc(bRun8,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // blue Target16^:=MulDiv16(ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))),65535,255); Inc(Target16); // green Target16^:=MulDiv16(ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))),65535,255); Inc(Target16); // red Target16^:=MulDiv16(ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))),65535,255); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end end; end; end; 16: begin if Length(Source)=1 then begin LRun16:=Source[0]; aRun16:=LRun16; Inc(aRun16); bRun16:=aRun16; Inc(bRun16); Increment:=3; end else begin LRun16:=Source[0]; aRun16:=Source[1]; bRun16:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun16^/2.55 else L:=LRun16^; Inc(LRun16,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun16^-128; Inc(aRun16,Increment); B:=bRun16^-128; Inc(bRun16,Increment); end else begin A:=ShortInt(aRun16^); Inc(aRun16, Increment); B:=ShortInt(bRun16^); Inc(bRun16, Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // blue Target8^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target8); // green Target8^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target8); // red Target8^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun16^/2.55 else L:=LRun16^; Inc(LRun16,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun16^-128; Inc(aRun16,Increment); B:=bRun16^-128; Inc(bRun16,Increment); end else begin A:=ShortInt(aRun16^); Inc(aRun16,Increment); B:=ShortInt(bRun16^); Inc(bRun16,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // blue Target16^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target16); // green Target16^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target16); // red Target16^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertCIELAB2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // just like RowConvertCIELAB2BGR but for RGB target schemes var LRun8,aRun8,bRun8: PByte; LRun16,aRun16,bRun16: PWord; L,A,B,X,Y,Z, // color values in float format T,YYn3: extended; // intermediate results Target8: PByte; Target16: PWord; Increment,AlphaSkip: integer; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin LRun8:=Source[0]; aRun8:=LRun8; Inc(aRun8); bRun8:=aRun8; Inc(bRun8); Increment:=3; end else begin LRun8:=Source[0]; aRun8:=Source[1]; bRun8:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun8^/2.55 else L:=LRun8^; Inc(LRun8,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun8^-128; Inc(aRun8,Increment); B:=bRun8^-128; Inc(bRun8,Increment); end else begin A:=ShortInt(aRun8^); Inc(aRun8,Increment); B:=ShortInt(bRun8^); Inc(bRun8,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // once we have CIE XYZ it is easy (yet quite expensive) to calculate RGB values from this // red Target8^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target8); // green Target8^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target8); // blue Target8^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun8^/2.55 else L:=LRun8^; Inc(LRun8,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun8^-128; Inc(aRun8,Increment); B:=bRun8^-128; Inc(bRun8,Increment); end else begin A:=ShortInt(aRun8^); Inc(aRun8,Increment); B:=ShortInt(bRun8^); Inc(bRun8,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // red Target16^:=MulDiv16(Clampbyte(Round(255*(2.998*X-1.458*Y-0.541*Z))),65535,255); Inc(Target16); // green Target16^:=MulDiv16(Clampbyte(Round(255*(-0.952*X+1.893*Y+0.059*Z))),65535,255); Inc(Target16); // blue Target16^:=MulDiv16(Clampbyte(Round(255*(0.099*X-0.198*Y+1.099*Z))),65535,255); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end end; end; end; 16: begin if Length(Source)=1 then begin LRun16:=Source[0]; aRun16:=LRun16; Inc(aRun16); bRun16:=aRun16; Inc(bRun16); Increment:=3; end else begin LRun16:=Source[0]; aRun16:=Source[1]; bRun16:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun16^/2.55 else L:=LRun16^; Inc(LRun16,Increment); if coLabChromaOffset in FSourceOptions then begin A:=aRun16^-128; Inc(aRun16,Increment); B:=bRun16^-128; Inc(bRun16,Increment); end else begin A:=ShortInt(aRun16^); Inc(aRun16,Increment); B:=ShortInt(bRun16^); Inc(bRun16,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // red Target8^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target8); // green Target8^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target8); // blue Target8^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coLabbyteRange in FSourceOptions then L:=LRun16^/2.55 else L:=LRun16^; Inc(LRun16,Increment); if coLabChromaOffset in FSourceOptions then begin a:=aRun16^-128; Inc(aRun16,Increment); b:=bRun16^-128; Inc(bRun16,Increment); end else begin a:=ShortInt(aRun16^); Inc(aRun16,Increment); b:=ShortInt(bRun16^); Inc(bRun16,Increment); end; YYn3:=(L+16)/116; // this corresponds to (Y/Yn)^1/3 if L<7.9996 then begin Y:=L/903.3; X:=A/3893.5+Y; Z:=Y-B/1557.4; end else begin T:=YYn3+A/500; X:=T*T*T; Y:=YYn3*YYn3*YYn3; T:=YYn3-B/200; Z:=T*T*T; end; // red Target16^:=ClampByte(Round(255*(2.998*X-1.458*Y-0.541*Z))); Inc(Target16); // green Target16^:=ClampByte(Round(255*(-0.952*X+1.893*Y+0.059*Z))); Inc(Target16); // blue Target16^:=ClampByte(Round(255*(0.099*X-0.198*Y+1.099*Z))); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertCMYK2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // converts a stream of Count CMYK values to BGR var C8,M8,Y8,K8: PByte; C16,M16,Y16,K16: PWord; Target8: PByte; Target16: PWord; Increment,AlphaSkip: integer; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=4 then begin // plane mode C8:=Source[0]; M8:=Source[1]; Y8:=Source[2]; K8:=Source[3]; Increment:=1; end else begin // interleaved mode C8:=Source[0]; M8:=C8; Inc(M8); Y8:=M8; Inc(Y8); K8:=Y8; Inc(K8); Increment:=4; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin // blue Target8^:=ClampByte(255-(Y8^-MulDiv16(Y8^,K8^,255)+K8^)); Inc(Target8); // green Target8^:=ClampByte(255-(M8^-MulDiv16(M8^,K8^,255)+K8^)); Inc(Target8); // blue Target8^:=ClampByte(255-(C8^-MulDiv16(C8^,K8^,255)+K8^)); Inc(Target8,1+AlphaSkip); Inc(C8,Increment); Inc(M8,Increment); Inc(Y8,Increment); Inc(K8,Increment); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count > 0 do begin if Boolean(Mask and BitRun) then begin // blue Target16^:=MulDiv16(Clampbyte(255-(Y8^-MulDiv16(Y8^,K8^,255)+K8^)),65535,255); Inc(Target16); // green Target16^:=MulDiv16(Clampbyte(255-(M8^-MulDiv16(M8^,K8^,255)+K8^)),65535,255); Inc(Target16); // blue Target16^:=MulDiv16(Clampbyte(255-(C8^-MulDiv16(C8^,K8^,255)+K8^)),65535,255); Inc(Target16,1+AlphaSkip); Inc(C8,Increment); Inc(M8,Increment); Inc(Y8,Increment); Inc(K8,Increment); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=4 then begin // plane mode C16:=Source[0]; M16:=Source[1]; Y16:=Source[2]; K16:=Source[3]; Increment:=1; end else begin // interleaved mode C16:=Source[0]; M16:=C16; Inc(M16); Y16:=M16; Inc(Y16); K16:=Y16; Inc(K16); Increment:=4; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin // blue Target8^:=ClampByte(255-MulDiv16((Y16^-MulDiv16(Y16^,K16^,65535)+K16^),255,65535)); Inc(Target8); // green Target8^:=ClampByte(255-MulDiv16((M16^-MulDiv16(M16^,K16^,65535)+K16^),255,65535)); Inc(Target8); // blue Target8^:=ClampByte(255-MulDiv16((C16^-MulDiv16(C16^,K16^,65535)+K16^),255,65535)); Inc(Target8,1+AlphaSkip); Inc(C16,Increment); Inc(M16,Increment); Inc(Y16,Increment); Inc(K16,Increment); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin // blue Target16^:=65535-(Y16^-MulDiv16(Y16^,K16^,65535)+K16^); Inc(Target16); // green Target16^:=65535-(M16^-MulDiv16(M16^,K16^,65535)+K16^); Inc(Target16); // blue Target16^:=65535-(C16^-MulDiv16(C16^,K16^,65535)+K16^); Inc(Target16,1+AlphaSkip); Inc(C16,Increment); Inc(M16,Increment); Inc(Y16,Increment); Inc(K16,Increment); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertCMYK2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // converts a stream of Count CMYK values to RGB, var C8,M8,Y8,K8: PByte; C16,M16,Y16,K16: PWord; Target8: PByte; Target16: PWord; Increment,AlphaSkip: integer; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=4 then begin // plane mode C8:=Source[0]; M8:=Source[1]; Y8:=Source[2]; K8:=Source[3]; Increment:=1; end else begin // interleaved mode C8:=Source[0]; M8:=C8; Inc(M8); Y8:=M8; Inc(Y8); K8:=Y8; Inc(K8); Increment:=4; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count > 0 do begin if Boolean(Mask and BitRun) then begin // red Target8^:=ClampByte(255-(C8^-MulDiv16(C8^,K8^,255)+K8^)); Inc(Target8); // green Target8^:=ClampByte(255-(M8^-MulDiv16(M8^,K8^,255)+K8^)); Inc(Target8); // blue Target8^:=ClampByte(255-(Y8^-MulDiv16(Y8^,K8^,255)+K8^)); Inc(Target8,1+AlphaSkip); Inc(C8,Increment); Inc(M8,Increment); Inc(Y8,Increment); Inc(K8,Increment); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count > 0 do begin if Boolean(Mask and BitRun) then begin // red Target16^:=MulDiv16(ClampByte(255-(C8^-MulDiv16(C8^,K8^,255)+K8^)),65535,255); Inc(Target16); // green Target16^:=MulDiv16(ClampByte(255-(M8^-MulDiv16(M8^,K8^,255)+K8^)),65535,255); Inc(Target16); // blue Target16^:=MulDiv16(ClampByte(255-(Y8^-MulDiv16(Y8^,K8^,255)+K8^)),65535,255); Inc(Target16,1+AlphaSkip); Inc(C8,Increment); Inc(M8,Increment); Inc(Y8,Increment); Inc(K8,Increment); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=4 then begin // plane mode C16:=Source[0]; M16:=Source[1]; Y16:=Source[2]; K16:=Source[3]; Increment:=1; end else begin // interleaved mode C16:=Source[0]; M16:=C16; Inc(M16); Y16:=M16; Inc(Y16); K16:=Y16; Inc(K16); Increment:=4; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin // red Target8^:=ClampByte(255-MulDiv16((C16^-MulDiv16(C16^,K16^,65535)+K16^),255,65535)); Inc(Target8); // green Target8^:=ClampByte(255-MulDiv16((M16^-MulDiv16(M16^,K16^,65535)+K16^),255,65535)); Inc(Target8); // blue Target8^:=ClampByte(255-MulDiv16((Y16^-MulDiv16(Y16^,K16^,65535)+K16^),255,65535)); Inc(Target8,1+AlphaSkip); Inc(C16,Increment); Inc(M16,Increment); Inc(Y16,Increment); Inc(K16,Increment); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin // red Target16^:=65535-(C16^-MulDiv16(C16^,K16^,65535)+K16^); Inc(Target16); // green Target16^:=65535-(M16^-MulDiv16(M16^,K16^,65535)+K16^); Inc(Target16); // blue Target16^:=65535-(Y16^-MulDiv16(Y16^,K16^,65535)+K16^); Inc(Target16,1+AlphaSkip); Inc(C16,Increment); Inc(M16,Increment); Inc(Y16,Increment); Inc(K16,Increment); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertGray(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // conversion from source grayscale (possibly with alpha) to target grayscale // Note: Since grayscale is basically handled like indexed mode (palette), there is no need to // handle gamma correction here as this happend already during palette creation. var Target8: PByte; Target16: PWord; Source8: PByte; Source16: PWord; BitRun: byte; AlphaSkip: integer; Convert16: function(Value: word): byte of object; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FSourceOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: case FTargetBPS of 8: begin // 888 to 888 Source8:=Source[0]; Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Target8^:=Source8^; Inc(Source8,1+AlphaSkip); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Target8); end; end; 16: begin // 888 to 161616 Source8:=Source[0]; Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Target16^:=MulDiv16(Source8^,65535,255); Inc(Source8,1+AlphaSkip); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Target16); end; end; end; 16: case FTargetBPS of 8: begin // 161616 to 888 Source16:=Source[0]; Target8:=Target; if coNeedbyteSwap in FSourceOptions then Convert16:=ComponentSwapScaleConvert else Convert16:=ComponentScaleConvert; while Count>0 do begin if Boolean(Mask and BitRun) then begin Target8^:=Convert16(Source16^); Inc(Source16,1+AlphaSkip); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Target8); end; end; 16: begin // 161616 to 161616 Source16:=Source[0]; Target16:=Target; if coNeedbyteSwap in FSourceOptions then begin while Count>0 do begin if Boolean(Mask and BitRun) then begin Target16^:=System.Swap(Source16^); Inc(Source16,1+AlphaSkip); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Target16); end; end else begin while Count>0 do begin if Boolean(Mask and BitRun) then begin Target16^:=Source16^; Inc(Source16,1+AlphaSkip); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Target16); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertIndexed8(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // This is the core conversion routine for indexed pixel formats. // This routine takes care about sample scaling and interlacing. // Note: 16 bit indexed mode is a bit different (words instead bytes and byte swap) and handled separately. var SourceRun,TargetRun: PByte; Value,BitRun,TargetMask,SourceMask,SourceShift,TargetShift,MaxInSample, MaxOutSample,SourceBPS, // local copies to ease assembler access TargetBPS: byte; Done: cardinal; begin SourceRun:=Source[0]; TargetRun:=Target; if (FSourceBPS=FTargetBPS) and (Mask=$FF) then Move(SourceRun^,TargetRun^,(Count*FSourceBPS+7) div 8) else begin BitRun:=$80; // make a copy of these both values from private variables to local variables // to ease access during assembler parts in the code SourceBPS:=FSourceBPS; TargetBPS:=FTargetBPS; SourceMask:=byte(not ((1 shl (8-SourceBPS))-1)); MaxInSample:=(1 shl SourceBPS)-1; TargetMask:=(1 shl (8-TargetBPS))-1; MaxOutSample:=(1 shl TargetBPS)-1; SourceShift:=8; TargetShift:=8-TargetBPS; Done:=0; while Done0 do begin if Boolean(Mask and BitRun) then begin TargetRun^:=System.Swap(SourceRun^); Inc(SourceRun); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRun); end; end else begin if Mask=$FF then Move(SourceRun^,TargetRun^,2*Count) else while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun^:=SourceRun^; Inc(SourceRun); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRun); end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertIndexedSource16(Source: array of Pointer; Target: Pointer; Count: Cardinal; Mask: byte); // This is the core conversion routine for indexed pixel formats with 16 bits per source sample values involved. var TargetRun8: PByte; SourceRun16: PWord; Value,BitRun,TargetMask,TargetShift,MaxOutSample, TargetBPS: byte; // local copies to ease assembler access begin SourceRun16:=Source[0]; TargetRun8:=Target; BitRun:=$80; // make a copy of these both values from private variables to local variables // to ease access during assembler parts in the code TargetBPS:=FTargetBPS; TargetMask:=(1 shl (8-TargetBPS))-1; MaxOutSample:=(1 shl TargetBPS)-1; TargetShift:=8-TargetBPS; while Count>0 do begin if Boolean(Mask and BitRun) then begin if coNeedbyteSwap in FSourceOptions then Value:=MulDiv16(System.Swap(SourceRun16^),MaxOutSample,65535) else Value:=MulDiv16(SourceRun16^,MaxOutSample,65535); TargetRun8^:=(TargetRun8^ and TargetMask) or (Value shl TargetShift); Inc(SourceRun16); end; asm ROR byte PTR [BitRun],1 // adjust test bit mask MOV CL,[TargetBPS] ROR byte PTR [TargetMask],CL // roll target mask with target bit count end; if TargetShift=0 then TargetShift:=8-TargetBPS else Dec(TargetShift,TargetBPS); Dec(Count); // advance target pointer every (8 div target bit count) if (Count mod (8 div TargetBPS))=0 then Inc(TargetRun8); end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertIndexedTarget16(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // This is the core conversion routine for indexed pixel formats with 16 bits per target sample values involved. var SourceRun8: PByte; TargetRun16: PWord; Value: word; BitRun,SourceMask,SourceShift,MaxInSample,SourceBPS: byte; begin SourceRun8:=Source[0]; TargetRun16:=Target; BitRun:=$80; SourceBPS:=FSourceBPS; SourceMask:=byte(not ((1 shl (8-SourceBPS))-1)); MaxInSample:=(1 shl SourceBPS)-1; SourceShift:=8; while Count>0 do begin if Boolean(Mask and BitRun) then begin // adjust shift value by source bit depth Dec(SourceShift,SourceBPS); Value:=(SourceRun8^ and SourceMask) shr SourceShift; Value:=MulDiv16(Value,65535,MaxInSample); if coNeedbyteSwap in FSourceOptions then TargetRun16^:=System.Swap(Value) else TargetRun16^:=Value; if SourceShift=0 then begin SourceShift:=8; Inc(SourceRun8); end; asm MOV CL,[SourceBPS] ROR byte PTR [SourceMask],CL // roll source bit mask with source bit count end; end; asm ROR byte PTR [BitRun],1 // adjust test bit mask end; Dec(Count); // advance target pointer every (8 div target bit count) Inc(TargetRun16); end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertRGB2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // Converts RGB source schemes to BGR target schemes and takes care for byte swapping, alpha copy/skip and // gamma correction. var SourceR16,SourceG16,SourceB16,SourceA16: PWord; SourceR8,SourceG8,SourceB8,SourceA8: PByte; TargetRun16: PBGR16; TargetRunA16: PBGRA16; TargetRun8: PBGR; TargetRunA8: PBGRA; BitRun: byte; Convert8_8: function(Value: byte): byte of object; Convert16_8: function(Value: word): byte of object; Convert16_8Alpha: function(Value: word): byte of object; Convert16_16: function(Value: word): word of object; SourceIncrement,TargetIncrement: cardinal; CopyAlpha: boolean; begin BitRun:=$80; // determine alpha handling once CopyAlpha:=False; if coAlpha in FSourceOptions then begin // byte size of components doesn't matter as the increments are applied to // pointers whose data types determine the final increment SourceIncrement:=sizeof(TRGBA); TargetIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then CopyAlpha:=True; end else begin SourceIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then TargetIncrement:=sizeof(TRGBA) else TargetIncrement:=sizeof(TRGB); end; // in planar mode source increment is always 1 if Length(Source)>1 then SourceIncrement:=1; case FSourceBPS of 8: begin if Length(Source)=1 then begin // interleaved mode SourceR8:=Source[0]; SourceG8:=SourceR8; Inc(SourceG8); SourceB8:=SourceG8; Inc(SourceB8); SourceA8:=SourceB8; Inc(SourceA8); end else begin SourceR8:=Source[0]; SourceG8:=Source[1]; SourceB8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; case FTargetBPS of 8: begin // 888 to 888 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert8_8(SourceR8^); TargetRunA8.G:=Convert8_8(SourceG8^); TargetRunA8.B:=Convert8_8(SourceB8^); // alpha values are never gamma corrected TargetRunA8.A:=SourceA8^; Inc(SourceB8, SourceIncrement); Inc(SourceG8, SourceIncrement); Inc(SourceR8, SourceIncrement); Inc(SourceA8, SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert8_8(SourceR8^); TargetRun8.G:=Convert8_8(SourceG8^); TargetRun8.B:=Convert8_8(SourceB8^); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 888 to 161616 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRunA16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRunA16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); TargetRunA16.A:=Convert16_16(MulDiv16(SourceA8^,65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRun16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRun16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; 16: begin if Length(Source)=1 then begin SourceR16:=Source[0]; SourceG16:=SourceR16; Inc(SourceG16); SourceB16:=SourceG16; Inc(SourceB16); SourceA16:=SourceB16; Inc(SourceA16); end else begin SourceR16:=Source[0]; SourceG16:=Source[1]; SourceB16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; case FTargetBPS of 8: begin // 161616 to 888 if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleGammaConvert else Convert16_8:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleConvert else Convert16_8:=ComponentScaleConvert; end; // since alpha channels are never gamma corrected we need a separate conversion routine if coNeedbyteSwap in FSourceOptions then Convert16_8Alpha:=ComponentSwapScaleConvert else Convert16_8Alpha:=ComponentScaleConvert; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert16_8(SourceR16^); TargetRunA8.G:=Convert16_8(SourceG16^); TargetRunA8.B:=Convert16_8(SourceB16^); TargetRunA8.A:=Convert16_8Alpha(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert16_8(SourceR16^); TargetRun8.G:=Convert16_8(SourceG16^); TargetRun8.B:=Convert16_8(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(Pbyte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 161616 to 161616 // no gamma correction for 16 bit samples yet if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(SourceR16^); TargetRunA16.G:=Convert16_16(SourceG16^); TargetRunA16.B:=Convert16_16(SourceB16^); TargetRunA16.A:=Convert16_16(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(SourceR16^); TargetRun16.G:=Convert16_16(SourceG16^); TargetRun16.B:=Convert16_16(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertRGB2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // same as ConvertRGB2BGR but for RGB target schemes var SourceR16,SourceG16,SourceB16,SourceA16: PWord; SourceR8,SourceG8,SourceB8,SourceA8: PByte; TargetRun16: PRGB16; TargetRunA16: PRGBA16; TargetRun8: PRGB; TargetRunA8: PRGBA; BitRun: byte; Convert8_8: function(Value: byte): byte of object; Convert16_8: function(Value: word): byte of object; Convert16_8Alpha: function(Value: word): byte of object; Convert16_16: function(Value: word): word of object; SourceIncrement,TargetIncrement: cardinal; CopyAlpha: boolean; begin BitRun:=$80; // determine alpha handling once CopyAlpha:=False; if coAlpha in FSourceOptions then begin SourceIncrement:=sizeof(TRGBA); TargetIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then CopyAlpha:=True; end else begin SourceIncrement:=sizeof(TRGB); if coAlpha in FTargetOptions then TargetIncrement:=sizeof(TRGBA) else TargetIncrement:=sizeof(TRGB); end; // in planar mode source increment is always 1 if Length(Source)>1 then SourceIncrement:=1; case FSourceBPS of 8: begin if Length(Source)=1 then begin // interleaved mode SourceR8:=Source[0]; SourceG8:=SourceR8; Inc(SourceG8); SourceB8:=SourceG8; Inc(SourceB8); SourceA8:=SourceB8; Inc(SourceA8); end else begin SourceR8:=Source[0]; SourceG8:=Source[1]; SourceB8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; case FTargetBPS of 8: begin // 888 to 888 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert8_8(SourceR8^); TargetRunA8.G:=Convert8_8(SourceG8^); TargetRunA8.B:=Convert8_8(SourceB8^); // alpha values are never gamma corrected TargetRunA8.A:=SourceA8^; Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert8_8(SourceR8^); TargetRun8.G:=Convert8_8(SourceG8^); TargetRun8.B:=Convert8_8(SourceB8^); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 888 to 161616 if coApplyGamma in FTargetOptions then Convert8_8:=ComponentGammaConvert else Convert8_8:=ComponentNoConvert8; if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB8:=Source[0]; SourceG8:=SourceB8; Inc(SourceG8); SourceR8:=SourceG8; Inc(SourceR8); SourceA8:=SourceR8; Inc(SourceA8); end else begin SourceB8:=Source[0]; SourceG8:=Source[1]; SourceR8:=Source[2]; if coAlpha in FSourceOptions then SourceA8:=Source[3] else SourceA8:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^),65535,255)); TargetRunA16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^),65535,255)); TargetRunA16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^),65535,255)); TargetRunA16.A:=Convert16_16(MulDiv16(SourceA8^,65535,255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); Inc(SourceA8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(MulDiv16(Convert8_8(SourceR8^), 65535, 255)); TargetRun16.G:=Convert16_16(MulDiv16(Convert8_8(SourceG8^), 65535, 255)); TargetRun16.B:=Convert16_16(MulDiv16(Convert8_8(SourceB8^), 65535, 255)); Inc(SourceB8,SourceIncrement); Inc(SourceG8,SourceIncrement); Inc(SourceR8,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; 16: begin if Length(Source)=1 then begin SourceR16:=Source[0]; SourceG16:=SourceR16; Inc(SourceG16); SourceB16:=SourceG16; Inc(SourceB16); SourceA16:=SourceB16; Inc(SourceA16); end else begin SourceR16:=Source[0]; SourceG16:=Source[1]; SourceB16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; case FTargetBPS of 8: begin // 161616 to 888 if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleGammaConvert else Convert16_8:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16_8:=ComponentSwapScaleConvert else Convert16_8:=ComponentScaleConvert; end; // since alpha channels are never gamma corrected we need a separate conversion routine if coNeedbyteSwap in FSourceOptions then Convert16_8Alpha:=ComponentSwapScaleConvert else Convert16_8Alpha:=ComponentScaleConvert; if CopyAlpha then begin TargetRunA8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA8.R:=Convert16_8(SourceR16^); TargetRunA8.G:=Convert16_8(SourceG16^); TargetRunA8.B:=Convert16_8(SourceB16^); TargetRunA8.A:=Convert16_8Alpha(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA8); end; end else begin TargetRun8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun8.R:=Convert16_8(SourceR16^); TargetRun8.G:=Convert16_8(SourceG16^); TargetRun8.B:=Convert16_8(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PByte(TargetRun8),TargetIncrement); end; end; end; 16: begin // 161616 to 161616 // no gamma correction for 16 bit samples yet if coNeedbyteSwap in FSourceOptions then Convert16_16:=ComponentSwapConvert else Convert16_16:=ComponentNoConvert16; if Length(Source)=1 then begin SourceB16:=Source[0]; SourceG16:=SourceB16; Inc(SourceG16); SourceR16:=SourceG16; Inc(SourceR16); SourceA16:=SourceR16; Inc(SourceA16); end else begin SourceB16:=Source[0]; SourceG16:=Source[1]; SourceR16:=Source[2]; if coAlpha in FSourceOptions then SourceA16:=Source[3] else SourceA16:=nil; end; if CopyAlpha then begin TargetRunA16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRunA16.R:=Convert16_16(SourceR16^); TargetRunA16.G:=Convert16_16(SourceG16^); TargetRunA16.B:=Convert16_16(SourceB16^); TargetRunA16.A:=Convert16_16(SourceA16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); Inc(SourceA16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(TargetRunA16); end; end else begin TargetRun16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin TargetRun16.R:=Convert16_16(SourceR16^); TargetRun16.G:=Convert16_16(SourceG16^); TargetRun16.B:=Convert16_16(SourceB16^); Inc(SourceB16,SourceIncrement); Inc(SourceG16,SourceIncrement); Inc(SourceR16,SourceIncrement); end; asm ROR byte PTR [BitRun],1 end; Dec(Count); Inc(PWord(TargetRun16),TargetIncrement); end; end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertPhotoYCC2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // converts from PhotoYCC format to BGR(A) var Y,Cb,Cr: integer; Yf,Cbf,Crf: single; Y8Run,Cb8Run,Cr8Run: PByte; Y16Run,Cb16Run,Cr16Run: PWord; Target8: PByte; Target16: PWord; AlphaSkip: integer; BitRun: byte; Increment: integer; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin Y8Run:=Source[0]; Cb8Run:=Y8Run; Inc(Cb8Run); Cr8Run:=Cb8Run; Inc(Cr8Run); Increment:=3; end else begin Y8Run:=Source[0]; Cb8Run:=Source[1]; Cr8Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // blue Target16^:=MulDiv16(Clampbyte(Y+FCbToBlueTable[Cb]),65535,255); Inc(Target16); // green Target16^:=MulDiv16(Clampbyte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]), 65535, 255); Inc(Target16); // red Target16^:=MulDiv16(Clampbyte(Y+FCrToRedTable[Cr]),65535,255); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=1 then begin Y16Run:=Source[0]; Cb16Run:=Y16Run; Inc(Cb16Run); Cr16Run:=Cb16Run; Inc(Cr16Run); Increment:=3; end else begin Y16Run:=Source[0]; Cb16Run:=Source[1]; Cr16Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=MulDiv16(Y16Run^,255,65535); Inc(Y16Run,Increment); Cb:=MulDiv16(Cb16Run^,255,65535); Inc(Cb16Run,Increment); Cr:=MulDiv16(Cr16Run^,255,65535); Inc(Cr16Run,Increment); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; // conversion from 16 to 16 is done with full precision, so there is no // loss of information, but the code is slower because the lookup tables // cannot be used while Count>0 do begin if Boolean(Mask and BitRun) then begin Yf:=1.3584*Y16Run^; Inc(Y16Run,Increment); Cbf:=Cb16Run^-40092; // (156*65535) div 255 Inc(Cb16Run,Increment); Crf:=Cr16Run^-35209; // (137*65535) div 255 Inc(Cr16Run,Increment); // blue Target16^:=Round(Yf+2.2179*Cbf); Inc(Target16); // green Target16^:=Round(Yf-0.9271435*Crf-0.4302726*Cbf); Inc(Target16); // red Target16^:=Round(Yf+1.8215*Crf); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertPhotoYCC2RGB(Source: array of Pointer; Target: Pointer; Count: Cardinal; Mask: byte); // converts from PhotoYCC format to RGB(A) var Y,Cb,Cr,AlphaSkip,Increment: integer; Yf,Cbf,Crf: single; Y8Run, Cb8Run, Cr8Run: PByte; Y16Run, Cb16Run, Cr16Run: PWord; Target8: PByte; Target16: PWord; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin Y8Run:=Source[0]; Cb8Run:=Y8Run; Inc(Cb8Run); Cr8Run:=Cb8Run; Inc(Cr8Run); Increment:=3; end else begin Y8Run:=Source[0]; Cb8Run:=Source[1]; Cr8Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // red Target16^:=MulDiv16(ClampByte(Y+FCrToRedTable[Cr]),65535,255); Inc(Target16,1+AlphaSkip); // green Target16^:=MulDiv16(ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]),65535,255); Inc(Target16); // blue Target16^:=MulDiv16(ClampByte(Y+FCbToBlueTable[Cb]),65535,255); Inc(Target16); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=1 then begin Y16Run:=Source[0]; Cb16Run:=Y16Run; Inc(Cb16Run); Cr16Run:=Cb16Run; Inc(Cr16Run); Increment:=3; end else begin Y16Run:=Source[0]; Cb16Run:=Source[1]; Cr16Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=MulDiv16(Y16Run^,255,65535); Inc(Y16Run,Increment); Cb:=MulDiv16(Cb16Run^,255,65535); Inc(Cb16Run,Increment); Cr:=MulDiv16(Cr16Run^,255,65535); Inc(Cr16Run,Increment); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreenTable[Cr]); Inc(Target8); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; // conversion from 16 to 16 is done with full precision, so there is no // loss of information, but the code is slower because the lookup tables // cannot be used while Count>0 do begin if Boolean(Mask and BitRun) then begin Yf:=1.3584*Y16Run^; Inc(Y16Run,Increment); Cbf:=Cb16Run^-40092; // (156*65535) div 255 Inc(Cb16Run,Increment); Crf:=Cr16Run^-35209; // (137*65535) div 255 Inc(Cr16Run,Increment); // red Target16^:=Round(Yf+1.8215*Crf); Inc(Target16,1+AlphaSkip); // green Target16^:=Round(Yf-0.9271435*Crf-0.4302726*Cbf); Inc(Target16); // blue Target16^:=Round(Yf+2.2179*Cbf); Inc(Target16); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertYCbCr2BGR(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // converts from standard YCbCr to BGR(A) var Y,Cb,Cr,AlphaSkip,Increment: integer; Yf,Cbf,Crf: single; Y8Run,Cb8Run,Cr8Run: PByte; Y16Run,Cb16Run,Cr16Run: PWord; Target8: PByte; Target16: PWord; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin Y8Run:=Source[0]; Cb8Run:=Y8Run; Inc(Cb8Run); Cr8Run:=Cb8Run; Inc(Cr8Run); Increment:=3; end else begin Y8Run:=Source[0]; Cb8Run:=Source[1]; Cr8Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // blue Target16^:=MulDiv16(Clampbyte(Y+FCbToBlueTable[Cb]),65535,255); Inc(Target16); // green Target16^:=MulDiv16(Clampbyte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]),65535,255); Inc(Target16); // red Target16^:=MulDiv16(Clampbyte(Y+FCrToRedTable[Cr]),65535,255); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=1 then begin Y16Run:=Source[0]; Cb16Run:=Y16Run; Inc(Cb16Run); Cr16Run:=Cb16Run; Inc(Cr16Run); Increment:=3; end else begin Y16Run:=Source[0]; Cb16Run:=Source[1]; Cr16Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=MulDiv16(Y16Run^,255,65535); Inc(Y16Run,Increment); Cb:=MulDiv16(Cb16Run^,255,65535); Inc(Cb16Run,Increment); Cr:=MulDiv16(Cr16Run^,255,65535); Inc(Cr16Run,Increment); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; // conversion from 16 to 16 is done with full precision, so there is no // loss of information, but the code is slower because the lookup tables // cannot be used while Count>0 do begin if Boolean(Mask and BitRun) then begin Yf:=1.3584*Y16Run^; Inc(Y16Run,Increment); Cbf:=Cb16Run^-40092; // (156*65535) div 255 Inc(Cb16Run,Increment); Crf:=Cr16Run^-35209; // (137*65535) div 255 Inc(Cr16Run,Increment); // blue Target16^:=Round(Yf+2.2179*Cbf); Inc(Target16); // green Target16^:=Round(Yf-0.9271435*Crf-0.4302726*Cbf); Inc(Target16); // red Target16^:=Round(Yf+1.8215*Crf); Inc(Target16,1+AlphaSkip); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.RowConvertYCbCr2RGB(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // converts from standard YCbCr to RGB(A) var Y,Cb,Cr,AlphaSkip,Increment: integer; Yf,Cbf,Crf: single; Y8Run,Cb8Run,Cr8Run: PByte; Y16Run,Cb16Run,Cr16Run: PWord; Target8: PByte; Target16: PWord; BitRun: byte; begin BitRun:=$80; AlphaSkip:=Ord(coAlpha in FTargetOptions); // 0 if no alpha must be skipped, otherwise 1 case FSourceBPS of 8: begin if Length(Source)=1 then begin Y8Run:=Source[0]; Cb8Run:=Y8Run; Inc(Cb8Run); Cr8Run:=Cb8Run; Inc(Cr8Run); Increment:=3; end else begin Y8Run:=Source[0]; Cb8Run:=Source[1]; Cr8Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 888 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // red Target8^:=ClampByte(Y+FCrToRedTable[Cr]); Inc(Target8,1+AlphaSkip); // green Target8^:=ClampByte(Y+FCbToGreenTable[Cb]+FCrToGreenTable[Cr]); Inc(Target8); // blue Target8^:=ClampByte(Y+FCbToBlueTable[Cb]); Inc(Target8); end else Inc(Target8,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 888 to 161616 Target16:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=Y8Run^; Inc(Y8Run,Increment); Cb:=Cb8Run^; Inc(Cb8Run,Increment); Cr:=Cr8Run^; Inc(Cr8Run,Increment); // red Target16^:=MulDiv16(Clampbyte(Y+FCrToRedTable[Cr]),65535,255); Inc(Target16, 1+AlphaSkip); // green Target16^:=MulDiv16(Clampbyte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]), 65535, 255); Inc(Target16); // blue Target16^:=MulDiv16(Clampbyte(Y+FCbToBlueTable[Cb]),65535,255); Inc(Target16); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; 16: begin if Length(Source)=1 then begin Y16Run:=Source[0]; Cb16Run:=Y16Run; Inc(Cb16Run); Cr16Run:=Cb16Run; Inc(Cr16Run); Increment:=3; end else begin Y16Run:=Source[0]; Cb16Run:=Source[1]; Cr16Run:=Source[2]; Increment:=1; end; case FTargetBPS of 8: begin // 161616 to 888 Target8:=Target; while Count>0 do begin if Boolean(Mask and BitRun) then begin Y:=MulDiv16(Y16Run^, 255, 65535); Inc(Y16Run, Increment); Cb:=MulDiv16(Cb16Run^, 255, 65535); Inc(Cb16Run, Increment); Cr:=MulDiv16(Cr16Run^, 255, 65535); Inc(Cr16Run, Increment); // red Target8^:=Clampbyte(Y+FCrToRedTable[Cr]); Inc(Target8, 1+AlphaSkip); // green Target8^:=Clampbyte(Y+FCbToGreenTable[Cb]+FCrToGreentable[Cr]); Inc(Target8); // blue Target8^:=Clampbyte(Y+FCbToBlueTable[Cb]); Inc(Target8); end else Inc(Target8, 3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; 16: begin // 161616 to 161616 Target16:=Target; // conversion from 16 to 16 is done with full precision, so there is no // loss of information, but the code is slower because the lookup tables // cannot be used while Count > 0 do begin if Boolean(Mask and BitRun) then begin Yf:=1.3584*Y16Run^; Inc(Y16Run,Increment); Cbf:=Cb16Run^-40092; // (156*65535) div 255 Inc(Cb16Run,Increment); Crf:=Cr16Run^-35209; // (137*65535) div 255 Inc(Cr16Run,Increment); // red Target16^:=Round(Yf+1.8215*Crf); Inc(Target16,1+AlphaSkip); // green Target16^:=Round(Yf-0.9271435*Crf-0.4302726*Cbf); Inc(Target16); // blue Target16^:=Round(Yf+2.2179*Cbf); Inc(Target16); end else Inc(Target16,3+AlphaSkip); asm ROR byte PTR [BitRun],1 end; Dec(Count); end; end; end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.CreateYCbCrLookup; // In order to speedup YCbCr conversion lookup tables are used, this methods creates them. // R:=Y+Cr*(2-2*LumaRed); // B:=Y+Cb*(2-2*LumaBlue); // G:=Y-LumaBlue*Cb*(2-2*LumaBlue) / LumaGreen // -LumaRed*Cr*(2-2*LumaRed) / LumaGreen; // // To avoid floating point arithmetic the fractional constants that come out of the equations are represented // as fixed point values in the range 0...2^16. We also eliminate multiplications by // pre-calculating possible // values indexed by Cb and Cr (this code assumes conversion is being done for 8-bit samples). // // Note: the color manager uses dynamic arrays so the memory used here is automatically freed. // // Needed settings: //-YCbCr parameters must be set or default values are used var F1,F2,F3,F4: single; LumaRed,LumaGreen,LumaBlue: single; I,Offset1,Offset2: integer; begin LumaRed:=FYCbCrCoefficients[0]; LumaGreen:=FYCbCrCoefficients[1]; LumaBlue:=FYCbCrCoefficients[2]; F1:=2-2*LumaRed; F2:=LumaRed*F1/LumaGreen; F3:=2-2*LumaBlue; F4:=LumaBlue*F3/LumaGreen; SetLength(FCrToRedTable,256); SetLength(FCbToBlueTable,256); SetLength(FCrToGreenTable,256); SetLength(FCbToGreenTable,256); if FSourceScheme=csYCbCr then begin // I is the actual input pixel value in the range 0..255, Cb and Cr values are in the range -128..127. // (for TIFF files they are in a range defined by the ReferenceBlackWhite tag). Offset1:=-128; for I:=0 to 255 do begin FCrToRedTable[I]:=Round(F1*Offset1); FCbToBlueTable[I]:=Round(F3*Offset1); FCrToGreenTable[I]:=-Round(F2*Offset1); FCbToGreenTable[I]:=-Round(F4*Offset1); Inc(Offset1); end; end else begin // PhotoYCC // I is the actual input pixel value in the range 0..255, Cb values are in the range -156..99, // Cr values are in the range -137..118. // (for TIFF files they are in a range defined by the ReferenceBlackWhite tag). Offset1:=-156; Offset2:=-137; for I:=0 to 255 do begin FCrToRedTable[I]:=Round(F1*Offset2); FCbToBlueTable[I]:=Round(F3*Offset1); FCrToGreenTable[I]:=-Round(F2*Offset2); FCbToGreenTable[I]:=-Round(F4*Offset1); Inc(Offset1); Inc(Offset2); end; end; end; //---------------------------------------------------------------------------------------------------------------------- function TColorManager.GetPixelFormat(Index: integer): TPixelFormat; // determines the pixel format from the current sample and pixel sizes // Note: setting pfCustom as pixel format will raise an exception so check the result from this method first // before you actually assign it to a bitmap. // // Needed settings: //-source samples per pixel and bits per sample for Index = 0 //-target samples per pixel and bits per sample for Index = 1 var SamplesPerPixel,BitsPerSample: byte; begin case Index of 0: begin SamplesPerPixel:=FSourceSPP; BitsPerSample:=FSourceBPS; end; else SamplesPerPixel:=FTargetSPP; BitsPerSample:=FTargetBPS; end; case SamplesPerPixel of 1: // one sample per pixel, this is usually a palette format case BitsPerSample of 1: Result:=pf1Bit; 2..4: Result:=pf4bit; // values<4 should be upscaled 8..16: Result:=pf8bit; // values > 8 bits must be downscaled to 8 bits else Result:=pfCustom; end; 3: // Typical case is RGB or CIE L*a*b* (565 and 555 16 bit color formats would also be possible, but aren't handled // by the manager). case BitsPerSample of 1..5: Result:=pf15Bit; // values<5 should be upscaled else // values > 8 bits should be downscaled Result:=pf24bit; end; 4: // Typical cases: RGBA and CMYK (with 8 bps, other formats like PCX's // 4 planes with 1 bit must be handled elsewhere) if BitsPerSample>=8 then Result:=pf32Bit else Result:=pfCustom; else Result:=pfCustom; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.PrepareConversion; // depending on the source and target pixel and color formats a conversion function must be // determined which actually carries out the conversion // // Needed settings: //-source and target samples per pixel and bits per sample //-source and target color scheme begin FRowConversion:=nil; // Conversion between indexed and non-indexed formats is not supported as well as // between source BPS<8 and target BPS > 8. // csGA and csG (grayscale w and w/o alpha) are considered being indexed modes if (FSourceScheme in [csIndexed,csG,csGA]) xor (FTargetScheme in [csIndexed,csG]) then Error(14{gesIndexedNotSupported}); // set up special conversion options if FSourceScheme in [csGA,csRGBA,csBGRA] then Include(FSourceOptions,coAlpha) else Exclude(FSourceOptions,coAlpha); if FTargetScheme in [csGA,csRGBA,csBGRA] then Include(FTargetOptions,coAlpha) else Exclude(FTargetOptions,coAlpha); case FSourceScheme of csG: if (FSourceBPS=16) or (FTargetBPS=16) then begin if (FSourceBPS>=8) and (FTargetBPS>=8) then FRowConversion:=RowConvertGray; end else FRowConversion:=RowConvertIndexed8; csGA: if (FSourceBPS in [8,16]) and (FTargetBPS in [8,16]) then FRowConversion:=RowConvertGray; csIndexed: begin // Grayscale is handled like indexed mode. // Generally use indexed conversions (with various possible bit operations), // assign special methods for source only, target only or source and target being 16 bits per sample if (FSourceBPS=16) and (FTargetBPS=16) then FRowConversion:=RowConvertIndexedBoth16 else if FSourceBPS=16 then FRowConversion:=RowConvertIndexedSource16 else if FTargetBPS=16 then FRowConversion:=RowConvertIndexedTarget16 else FRowConversion:=RowConvertIndexed8; end; csRGB, csRGBA: case FTargetScheme of csRGB: FRowConversion:=RowConvertRGB2RGB; csRGBA: FRowConversion:=RowConvertRGB2RGB; csBGR: FRowConversion:=RowConvertRGB2BGR; csBGRA: FRowConversion:=RowConvertRGB2BGR; csCMY,csCMYK,csCIELab,csYCbCr: ; end; csBGRA, csBGR: case FTargetScheme of csRGBA: FRowConversion:=RowConvertBGR2RGB; csBGRA: FRowConversion:=RowConvertBGR2BGR; csRGB,csBGR,csCMY,csCMYK,csCIELab,csYCbCr: ; end; csCMY: case FTargetScheme of csRGB,csRGBA,csBGR,csBGRA,csCMY,csCMYK,csCIELab,csYCbCr: ; end; csCMYK: case FTargetScheme of csRGBA: FRowConversion:=RowConvertCMYK2RGB; csBGRA, csBGR: FRowConversion:=RowConvertCMYK2BGR; csRGB,{csBGR,}csCMY,csCMYK,csCIELab,csYCbCr: ; end; csCIELab: case FTargetScheme of csRGBA: FRowConversion:=RowConvertCIELab2RGB; csBGRA,csBGR: FRowConversion:=RowConvertCIELab2BGR; csRGB,csCMY,csCMYK,csCIELab,csYCbCr: ; end; csYCbCr: begin // create lookup tables to speed up conversion CreateYCbCrLookup; case FTargetScheme of csRGBA: FRowConversion:=RowConvertYCbCr2RGB; csBGRA: FRowConversion:=RowConvertYCbCr2BGR; csRGB,csBGR,csCMY,csCMYK,csCIELab,csYCbCr: ; end; end; csPhotoYCC: begin // create lookup tables to speed up conversion CreateYCbCrLookup; case FTargetScheme of csRGBA: FRowConversion:=RowConvertPhotoYCC2RGB; csBGRA: FRowConversion:=RowConvertPhotoYCC2BGR; csRGB,csBGR,csCMY,csCMYK,csCIELab,csYCbCr: ; end; end; end; FChanged:=False; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetSourceBitsPerSample(const Value: byte); begin if not (Value in [1..16]) then Error(16{gesInvalidSampleDepth}); if FSourceBPS<>Value then begin FSourceBPS:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetSourceColorScheme(const Value: TColorScheme); begin if FSourceScheme<>Value then begin FSourceScheme:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetSourceSamplesPerPixel(const Value: byte); begin if not (Value in [1..4]) then Error(17{gesInvalidPixelDepth}); if FSourceSPP<>Value then begin FSourceSPP:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetTargetBitsPerSample(const Value: byte); begin if not (Value in [1..16]) then Error(16{gesInvalidSampleDepth}); if FTargetBPS<>Value then begin FTargetBPS:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetTargetColorScheme(const Value: TColorScheme); begin if FTargetScheme<>Value then begin FTargetScheme:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetTargetSamplesPerPixel(const Value: byte); begin if not (Value in [1..4]) then Error(17{gesInvalidPixelDepth}); if FTargetSPP<>Value then begin FTargetSPP:=Value; FChanged:=True; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.ConvertRow(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte); // initializes the color conversion method if necessary and calls it to do the actual conversion // // Needed settings: //-source and target BPS and SPP //-main and display gamma, if gamma correction is wanted //-conversion options //-YCbCr parameters if any of the color schemes is csYCbCr begin // if there are pending changes then apply them if FChanged then PrepareConversion; // check if there's now a conversion method if not Assigned( FRowConversion ) then Error(15{gesConversionUnsupported}) else FRowConversion(Source,Target,Count,Mask); end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.CreateColorPalette(BMP: PBitmap; Data: array of pointer; DataFormat: TRawPaletteFormat; ColorCount: cardinal; RGB: boolean); // Creates a color palette from the provided data which can be in various raw formats: //-either interlaced or plane //-8 bits or 16 bits per component //-in RGB or BGR order //-with 3 or 4 components per entry (fourth is ignored) // ColorCount determines the number of color entries to create. If this number does not equal the // number of palette entries which would result from the given target bits per sample resolution // then the palette is adjusted accordingly to allow conversion between resolutions. // // Notes: For interlaced formats only one pointer needs to be passed in Data (only the first one is used) // while for plane data 3 pointers are necessary (for each plane one pointer). // The data order is assumed rgb or bgr in interlaced order (depending on RGB). In plane mode the three needed // pointers must also be given such that the pointer to red components is in Data[0], the green pointer in // Data[1] and the blue one in Data[2]. In this case BGR is not needed. // // Needed settings: //-main and display gamma, if gamma correction is wanted //-Options set as needed (gamma, byte swap) //-source and target bits per sample resolution var I,MaxIn,MaxOut: integer; RunR8,RunG8,RunB8: PByte; RunR16,RunG16,RunB16: PWord; Convert8: function(Value: byte): byte of object; Convert16: function(Value: word): byte of object; R,G,B: byte; begin case DataFormat of pfInterlaced8Triple, pfInterlaced8Quad: begin RunR8:=Data[0]; if coApplyGamma in FTargetOptions then Convert8:=ComponentGammaConvert else Convert8:=ComponentNoConvert8; if RGB then begin for I:=0 to pred(ColorCount) do begin B:=Convert8(RunR8^); Inc(RunR8); G:=Convert8(RunR8^); Inc(RunR8); R:=Convert8(RunR8^); Inc(RunR8); BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B); if DataFormat=pfInterlaced8Quad then Inc(RunR8); end; end else begin for I:=0 to pred(ColorCount) do begin R:=Convert8(RunR8^); Inc(RunR8); G:=Convert8(RunR8^); Inc(RunR8); B:=Convert8(RunR8^); Inc(RunR8); BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B); if DataFormat=pfInterlaced8Quad then Inc(RunR8); end; end; end; pfPlane8Triple, pfPlane8Quad: begin RunR8:=Data[0]; RunG8:=Data[1]; RunB8:=Data[2]; if coApplyGamma in FTargetOptions then Convert8:=ComponentGammaConvert else Convert8:=ComponentNoConvert8; for I:=0 to pred(ColorCount) do begin R:=Convert8(RunR8^); Inc(RunR8); G:=Convert8(RunG8^); Inc(RunG8); B:=Convert8(RunB8^); Inc(RunB8); BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B); end; end; pfInterlaced16Triple, pfInterlaced16Quad: begin RunR16:=Data[0]; if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16:=ComponentSwapScaleGammaConvert else Convert16:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16:=ComponentSwapScaleConvert else Convert16:=ComponentScaleConvert; end; if RGB then begin for I:=0 to pred(ColorCount) do begin R:=Convert16(RunR16^); Inc(RunR16); G:=Convert16(RunR16^); Inc(RunR16); B:=Convert16(RunR16^); Inc(RunR16); BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B); if DataFormat=pfInterlaced16Quad then Inc(RunR16); end; end else begin for I:=0 to pred(ColorCount) do begin B:=Convert16(RunR16^); Inc(RunR16); G:=Convert16(RunR16^); Inc(RunR16); R:=Convert16(RunR16^); Inc(RunR16); BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B); if DataFormat=pfInterlaced16Quad then Inc(RunR16); end; end; end; pfPlane16Triple, pfPlane16Quad: begin RunR16:=Data[0]; RunG16:=Data[1]; RunB16:=Data[2]; if coApplyGamma in FTargetOptions then begin if coNeedbyteSwap in FSourceOptions then Convert16:=ComponentSwapScaleGammaConvert else Convert16:=ComponentScaleGammaConvert; end else begin if coNeedbyteSwap in FSourceOptions then Convert16:=ComponentSwapScaleConvert else Convert16:=ComponentScaleConvert; end; for I:=0 to pred(ColorCount) do begin R:=Convert16(RunR16^); Inc(RunR16); G:=Convert16(RunG16^); Inc(RunG16); B:=Convert16(RunB16^); Inc(RunB16); BMP.DIBPalEntries[I]:=Windows.RGB(B,G,R); end; end; end; MaxIn:=(1 shl FSourceBPS)-1; MaxOut:=(1 shl FTargetBPS)-1; if (FTargetBPS<=8) and (MaxIn<>MaxOut) then begin // If target resolution and given color depth differ then the palette needs to be adjusted. // Consider the case for 2 bit to 4 bit conversion. Only 4 colors will be given to create // the palette but after scaling all values will be up to 15 for which no color is in the palette. // This and the reverse case need to be accounted for. MaxIn:=(1 shl FSourceBPS)-1; MaxOut:=(1 shl FTargetBPS)-1; if MaxIn8 then BPS:=8; Upper:=(1 shl BPS)-1; Factor:=255 div Upper; if MinimumIsWhite then begin if not (coApplyGamma in FTargetOptions) then begin for I:=0 to Upper do BMP.DIBPalEntries[Upper-I]:=RGB(I*Factor,I*Factor,I*Factor); end else begin for I:=0 to Upper do BMP.DIBPalEntries[Upper-I]:=RGB(FGammaTable[I*Factor],FGammaTable[I*Factor],FGammaTable[I*Factor]); end; end else begin if not (coApplyGamma in FTargetOptions) then begin for I:=0 to Upper do BMP.DIBPalEntries[I]:=RGB(I*Factor,I*Factor,I*Factor); end else begin for I:=0 to Upper do BMP.DIBPalEntries[I]:=RGB(FGammaTable[I*Factor],FGammaTable[I*Factor],FGammaTable[I*Factor]); end; end; end; //---------------------------------------------------------------------------------------------------------------------- {$IFDEF NOT_USE_KOL_ERR} procedure TColorManager.Error(Code: integer); var E: Exception; begin E:=Exception.Create(int2str(Code)); //E.ErrorCode:=Code; raise E; end; {$ELSE} procedure TColorManager.Error(Code: integer); var E: Exception; begin E:=Exception.Create(e_Custom,ErrorMsg[Code]); E.ErrorCode:=Code; raise E; end; {$ENDIF} //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetGamma(MainGamma,DisplayGamma: single); // sets the current gamma values and creates the gamma lookup table // // Needed settings: //-source bits per samples must be set //-target bits per samples must be set var I,SourceHighBound,TargetHighBound: integer; Gamma: single; begin if MainGamma<=0 then FMainGamma:=1 else FMainGamma:=MainGamma; if DisplayGamma<=0 then FDisplayGamma:=2.2 {default value for a usual CRT} else FDisplayGamma:=DisplayGamma; Gamma:=1/(FMainGamma*FDisplayGamma); // source high bound is the maximum possible source value which can appear (0..255) if FSourceBPS>=8 then SourceHighBound:=255 else SourceHighBound:=(1 shl FTargetBPS)-1; // target high bound is the target value which corresponds to a target sample value of 1 (0..255) if FTargetBPS>=8 then TargetHighBound:=255 else TargetHighBound:=(1 shl FTargetBPS)-1; for I:=0 to SourceHighBound do FGammaTable[I]:=Round(Power((I/SourceHighBound),Gamma)*TargetHighBound); end; //---------------------------------------------------------------------------------------------------------------------- procedure TColorManager.SetYCbCrParameters(Values: array of single; HSubSampling,VSubSampling: byte); // sets coefficients needed to convert from YCbCr color scheme begin // there must always be at least one value in an open array FYCbCrCoefficients[0]:=Values[0]; if High(Values)>0 then begin FYCbCrCoefficients[1]:=Values[1]; if High(Values)>1 then FYCbCrCoefficients[2]:=Values[2]; end; // subsampling can be 1, 2 or 4 and vertical subsampling must always be<=horizontal subsampling if not (HSubSampling in [1,2,4]) then Error(18{gesInvalidSubSampling}); if not (VSubSampling in [1,2,4]) then Error(18{gesInvalidSubSampling}); if VSubSampling>HSubSampling then Error(19{gesVerticalSubSamplingError}); FHSubSampling:=HSubSampling; FVSubSampling:=VSubSampling; end; //---------------------------------------------------------------------------------------------------------------------- end.