📄 graphiccolor.pas
字号:
unit GraphicColor;
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
// specific language governing rights and limitations under the License.
//
// The original code is GraphicColor.pas, released November 1, 1999.
//
// The initial developer of the original code is Dipl. Ing. Mike Lischke (Plei遖, Germany, www.delphi-gems.com),
//
// Portions created by Dipl. Ing. Mike Lischke are Copyright
// (C) 1999-2003 Dipl. Ing. Mike Lischke. All Rights Reserved.
//----------------------------------------------------------------------------------------------------------------------
// This file is part of the image library GraphicEx.
//
// 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.
//
//----------------------------------------------------------------------------------------------------------------------
interface
{$I GraphicConfiguration.inc}
uses
Windows, Graphics, GraphicStrings;
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;
TColorManager = class
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);
function CreateColorPalette(Data: array of Pointer; DataFormat: TRawPaletteFormat; ColorCount: Cardinal;
RGB: Boolean): HPALETTE;
function CreateGrayscalePalette(MinimumIsWhite: Boolean): HPALETTE;
procedure Error(const Msg: String);
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 ClampByte(Value: Integer): Byte;
function MulDiv16(Number, Numerator, Denominator: Word): Word;
//----------------------------------------------------------------------------------------------------------------------
implementation
uses
Math, SysUtils;
type
EColorConversionError = class(Exception);
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 --------------------------------------------------------------------------------------
constructor TColorManager.Create;
// set some default values
begin
FSourceBPS := 8;
FTargetBPS := 8;
FSourceSPP := 3; // 24 bit format
FTargetSPP := 3; // 24 bit format
SetGamma(1, DefaultDisplayGamma);
FSourceScheme := csRGB;
FTargetScheme := csBGR;
// defaults are from CCIR Recommendation 601-1
FYCbCrCoefficients[0] := 0.299;
FYCbCrCoefficients[1] := 0.587;
FYCbCrCoefficients[2] := 0.114;
FHSubSampling := 1;
FVSubSampling := 1;
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -