kolmck/Addons/KOLGraphicColor.pas

4150 lines
165 KiB
ObjectPascal

{$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 Done<Count do
begin
if Boolean(Mask and BitRun) then
begin
// adjust shift value by source bit depth
Dec(SourceShift,SourceBPS);
Value:=(SourceRun^ and SourceMask) shr SourceShift;
Value:=MulDiv16(Value,MaxOutSample,MaxInSample);
TargetRun^:=(TargetRun^ and TargetMask) or (Value shl TargetShift);
if SourceShift=0 then
begin
SourceShift:=8;
Inc(SourceRun);
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
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);
Inc(Done);
// advance target pointer every (8 div target bit count)
if (Done mod (8 div TargetBPS))=0 then Inc(TargetRun);
end;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TColorManager.RowConvertIndexedBoth16(Source: array of pointer; Target: pointer; Count: cardinal; Mask: byte);
// This is the core conversion routine for indexed pixel formats with 16 bits per sample values involved
// (special version for source and target resolution being both 16 BPS).
var TargetRun,SourceRun: PWord;
BitRun: byte;
begin
SourceRun:=Source[0];
TargetRun:=Target;
BitRun:=$80;
if coNeedbyteSwap in FSourceOptions then
begin
while Count>0 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 MaxIn<MaxOut then
begin
// palette is too small, enhance it
for I:=MaxOut downto 0 do
begin
R:=GetRValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
G:=GetGValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
B:=GetBValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B);
end;
end
else
begin
// palette contains too many entries, shorten it
for I:=0 to MaxOut do
begin
R:=GetRValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
G:=GetGValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
B:=GetBValue(BMP.DIBPalEntries[MulDiv16(I,MaxIn,MaxOut)]);
BMP.DIBPalEntries[I]:=Windows.RGB(R,G,B);
end;
end;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TColorManager.CreateGrayscalePalette(BMP: PBitmap; MinimumIsWhite: boolean);
// Creates a grayscale palette depending on the target bit depth and returns the handle of it,
// optionally applies gamma to each entry. MinimumIsWhite is True if the palette starts
// with white in index 0 decreasing to black while the index grows otherwise (and this is the usual case)
// a lower palette index has a lower brightness.
//
// Needed settings:
//-target BPS, 16 bits are handled as 8 bits
//-target SPP should be set to 1 (indicating an indexed image), but is not evaluated here
//-main and display gamma, if gamma correction is wanted
// Note: Target bps must not be changed between setting gamma and creating the palette.
var I: Integer;
BPS,Upper,Factor: byte;
begin
// the product of BPS and SPP considers planar organizatons correctly
// (e.g. PCX has a format 4 planes with 1 bit resulting to 16 color image)
BPS:=FTargetBPS*FTargetSPP;
if BPS>8 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.