| 1 | {=== Pixel types and functions ===}
|
|---|
| 2 |
|
|---|
| 3 | {$IFDEF INCLUDE_INTERFACE}
|
|---|
| 4 | {$UNDEF INCLUDE_INTERFACE}
|
|---|
| 5 | type
|
|---|
| 6 | {* Pointer for direct pixel access. Data is stored as a sequence of ''TBGRAPixel''.
|
|---|
| 7 | See [[BGRABitmap tutorial 4]] }
|
|---|
| 8 | PBGRAPixel = ^TBGRAPixel;
|
|---|
| 9 |
|
|---|
| 10 | {$IFNDEF BGRABITMAP_BGRAPIXEL}
|
|---|
| 11 | {$IFDEF BGRABITMAP_USE_LCL}
|
|---|
| 12 | {$IFDEF LCLgtk}
|
|---|
| 13 | {$DEFINE BGRABITMAP_RGBAPIXEL}
|
|---|
| 14 | {$ENDIF}
|
|---|
| 15 | {$IFDEF LCLgtk2}
|
|---|
| 16 | {$DEFINE BGRABITMAP_RGBAPIXEL}
|
|---|
| 17 | {$ENDIF}
|
|---|
| 18 | {$IFDEF DARWIN}
|
|---|
| 19 | {$IFNDEF LCLQt}
|
|---|
| 20 | {$DEFINE BGRABITMAP_RGBAPIXEL}
|
|---|
| 21 | {$ENDIF}
|
|---|
| 22 | {$ENDIF}
|
|---|
| 23 | {$ENDIF}
|
|---|
| 24 | {$ENDIF}
|
|---|
| 25 |
|
|---|
| 26 | {* Each pixel is a sequence of 4 bytes containing blue, green, red and alpha channel.
|
|---|
| 27 | Values range from 0 to 255, color is in sRGB colorspace. The alpha value of 0
|
|---|
| 28 | is transparent and 255 is opaque. In the bitmap data, when the pixel is fully transparent,
|
|---|
| 29 | the RGB values are supposed to be set to zero. }
|
|---|
| 30 |
|
|---|
| 31 | { TBGRAPixel }
|
|---|
| 32 |
|
|---|
| 33 | TBGRAPixel = packed record
|
|---|
| 34 | private
|
|---|
| 35 | function GetClassIntensity: word;
|
|---|
| 36 | function GetClassLightness: word;
|
|---|
| 37 | procedure SetClassIntensity(AValue: word);
|
|---|
| 38 | procedure SetClassLightness(AValue: word);
|
|---|
| 39 | public
|
|---|
| 40 | {$IFDEF BGRABITMAP_RGBAPIXEL}
|
|---|
| 41 | red, green, blue, alpha: byte;
|
|---|
| 42 | {$ELSE}
|
|---|
| 43 | blue, green, red, alpha: byte;
|
|---|
| 44 | {$ENDIF}
|
|---|
| 45 | procedure FromRGB(ARed,AGreen,ABlue: Byte; AAlpha: Byte = 255);
|
|---|
| 46 | procedure FromColor(AColor: TColor; AAlpha: Byte = 255);
|
|---|
| 47 | procedure FromString(AStr: string);
|
|---|
| 48 | procedure FromFPColor(AColor: TFPColor);
|
|---|
| 49 | procedure ToRGB(out ARed,AGreen,ABlue,AAlpha: Byte); overload;
|
|---|
| 50 | procedure ToRGB(out ARed,AGreen,ABlue: Byte); overload;
|
|---|
| 51 | function ToColor: TColor;
|
|---|
| 52 | function ToString: string;
|
|---|
| 53 | function ToGrayscale(AGammaCorrection: boolean = true): TBGRAPixel;
|
|---|
| 54 | function ToFPColor: TFPColor;
|
|---|
| 55 | class Operator := (Source: TBGRAPixel): TColor;
|
|---|
| 56 | class Operator := (Source: TColor): TBGRAPixel;
|
|---|
| 57 | property Intensity: word read GetClassIntensity write SetClassIntensity;
|
|---|
| 58 | property Lightness: word read GetClassLightness write SetClassLightness;
|
|---|
| 59 | end;
|
|---|
| 60 | TBGRAPixelBuffer = packed array of TBGRAPixel;
|
|---|
| 61 |
|
|---|
| 62 | procedure AllocateBGRAPixelBuffer(var ABuffer: TBGRAPixelBuffer; ASize: integer);
|
|---|
| 63 |
|
|---|
| 64 | const
|
|---|
| 65 | {$IFDEF BGRABITMAP_RGBAPIXEL}
|
|---|
| 66 | TBGRAPixel_RGBAOrder = True;
|
|---|
| 67 | TBGRAPixel_RedByteOffset = 0;
|
|---|
| 68 | TBGRAPixel_GreenByteOffset = 1;
|
|---|
| 69 | TBGRAPixel_BlueByteOffset = 2;
|
|---|
| 70 | {$ELSE}
|
|---|
| 71 | TBGRAPixel_RGBAOrder = False;
|
|---|
| 72 | TBGRAPixel_BlueByteOffset = 0;
|
|---|
| 73 | TBGRAPixel_GreenByteOffset = 1;
|
|---|
| 74 | TBGRAPixel_RedByteOffset = 2;
|
|---|
| 75 | {$ENDIF}
|
|---|
| 76 | TBGRAPixel_AlphaByteOffset = 3;
|
|---|
| 77 | {$IFDEF ENDIAN_LITTLE}
|
|---|
| 78 | TBGRAPixel_RedShift = TBGRAPixel_RedByteOffset*8;
|
|---|
| 79 | TBGRAPixel_GreenShift = TBGRAPixel_GreenByteOffset*8;
|
|---|
| 80 | TBGRAPixel_BlueShift = TBGRAPixel_BlueByteOffset*8;
|
|---|
| 81 | TBGRAPixel_AlphaShift = TBGRAPixel_AlphaByteOffset*8;
|
|---|
| 82 | {$ELSE}
|
|---|
| 83 | TBGRAPixel_RedShift = 24 - TBGRAPixel_RedByteOffset*8;
|
|---|
| 84 | TBGRAPixel_GreenShift = 24 - TBGRAPixel_GreenByteOffset*8;
|
|---|
| 85 | TBGRAPixel_BlueShift = 24 - TBGRAPixel_BlueByteOffset*8;
|
|---|
| 86 | TBGRAPixel_AlphaShift = 24 - TBGRAPixel_AlphaByteOffset*8;
|
|---|
| 87 | {$ENDIF}
|
|---|
| 88 |
|
|---|
| 89 | {** Creates a pixel with given RGBA values }
|
|---|
| 90 | function BGRA(red, green, blue, alpha: byte): TBGRAPixel; overload; inline;
|
|---|
| 91 | {** Creates a opaque pixel with given RGB values }
|
|---|
| 92 | function BGRA(red, green, blue: byte): TBGRAPixel; overload; inline;
|
|---|
| 93 | {** Checks if two pixels are equal. If they are both transparent,
|
|---|
| 94 | RGB values are ignored }
|
|---|
| 95 | operator = (const c1, c2: TBGRAPixel): boolean; inline;
|
|---|
| 96 | {** Returns the intensity of a pixel. The intensity is the
|
|---|
| 97 | maximum value reached by any component }
|
|---|
| 98 | function GetIntensity(c: TBGRAPixel): word; inline;
|
|---|
| 99 | {** Sets the intensity of a pixel }
|
|---|
| 100 | function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
|
|---|
| 101 | {** Returns the lightness of a pixel. The lightness is the
|
|---|
| 102 | perceived brightness, 0 being black and 65535 being white }
|
|---|
| 103 | function GetLightness(c: TBGRAPixel): word; overload;
|
|---|
| 104 | {** Sets the lightness of a pixel }
|
|---|
| 105 | function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel; overload;
|
|---|
| 106 | {** Sets the lightness quickly, by fading towards black if ''lightness'' is
|
|---|
| 107 | less than 32768, and fading towards white if ''lightness'' is more
|
|---|
| 108 | than 32768 }
|
|---|
| 109 | function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel; inline;
|
|---|
| 110 | {** Sets the intensity quickly, by fading towards black if ''lightness'' is
|
|---|
| 111 | less than 32768, and multiplying all components if ''lightness'' is more
|
|---|
| 112 | than 32768. In case of saturation, it fades towards white }
|
|---|
| 113 | function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
|
|---|
| 114 | {** Combines two lightnesses together. A value of 32768 is neutral. The
|
|---|
| 115 | result may exceed 65535 }
|
|---|
| 116 | function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
|
|---|
| 117 | {** Converts a color into grayscale }
|
|---|
| 118 | function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
|
|---|
| 119 | function BGRAToGrayscaleLinear(c: TBGRAPixel): TBGRAPixel;
|
|---|
| 120 | {** Create a gray color with the given ''lightness'' }
|
|---|
| 121 | function GrayscaleToBGRA(lightness: word): TBGRAPixel;
|
|---|
| 122 | {** Merge two colors without gamma correction }
|
|---|
| 123 | function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel; overload;
|
|---|
| 124 | {** Merge two colors without gamma correction. ''weight1'' and ''weight2''
|
|---|
| 125 | indicates the weight of the color barycentre }
|
|---|
| 126 | function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel; weight2: integer): TBGRAPixel; overload;
|
|---|
| 127 | {** Merge two colors with gamma correction. ''weight1'' and ''weight2''
|
|---|
| 128 | indicates the weight of the color barycentre }
|
|---|
| 129 | function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel; weight2: byte): TBGRAPixel;
|
|---|
| 130 | {** Converts a ''TColor'' value into an opaque pixel }
|
|---|
| 131 | function ColorToBGRA(color: TColor): TBGRAPixel; overload;
|
|---|
| 132 | {** Converts a ''TColor'' value into a pixel with given ''opacity'' }
|
|---|
| 133 | function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
|
|---|
| 134 | {** Converts a pixel into a TColor value, discarding the alpha value }
|
|---|
| 135 | function BGRAToColor(c: TBGRAPixel): TColor;
|
|---|
| 136 | {** Converts a ''TFPColor'' value into a pixel. Note that even if
|
|---|
| 137 | ''TFPColor'' have 16-bit values, they are not considered as
|
|---|
| 138 | gamma expanded }
|
|---|
| 139 | function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
|
|---|
| 140 | {** Converts a pixel into a ''TFPColor'' }
|
|---|
| 141 | function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
|
|---|
| 142 | function Color16BitToBGRA(AColor: Word): TBGRAPixel;
|
|---|
| 143 | function BGRAToColor16Bit(const AColor: TBGRAPixel): Word;
|
|---|
| 144 | {** Computes the difference (with gamma correction) between two pixels,
|
|---|
| 145 | taking into account all dimensions, including transparency. The
|
|---|
| 146 | result ranges from 0 to 65535 }
|
|---|
| 147 | function BGRAWordDiff(c1, c2: TBGRAPixel): word;
|
|---|
| 148 | {** Computes the difference (with gamma correction) between two pixels,
|
|---|
| 149 | taking into account all dimensions, including transparency. The
|
|---|
| 150 | result ranges from 0 to 255 }
|
|---|
| 151 | function BGRADiff(c1, c2: TBGRAPixel): byte;
|
|---|
| 152 | function FastBGRALinearDiff(c1,c2: TBGRAPixel): byte;
|
|---|
| 153 | function FastBGRAExpandedDiff(c1,c2: TBGRAPixel): word;
|
|---|
| 154 |
|
|---|
| 155 | type
|
|---|
| 156 | {* Array of pixels }
|
|---|
| 157 | ArrayOfTBGRAPixel = array of TBGRAPixel;
|
|---|
| 158 | {** Merge given colors without gamma correction }
|
|---|
| 159 | function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel; overload;
|
|---|
| 160 |
|
|---|
| 161 | { Get height [0..1] stored in a TBGRAPixel }
|
|---|
| 162 | function MapHeight(Color: TBGRAPixel): Single;
|
|---|
| 163 |
|
|---|
| 164 | { Get TBGRAPixel to store height [0..1] }
|
|---|
| 165 | function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
|
|---|
| 166 |
|
|---|
| 167 | type
|
|---|
| 168 | {* Possible modes when drawing a pixel over another one }
|
|---|
| 169 | TDrawMode = (
|
|---|
| 170 | {** The pixel is replaced }
|
|---|
| 171 | dmSet,
|
|---|
| 172 | {** The pixel is replaced if the pixel over has an alpha value of 255 }
|
|---|
| 173 | dmSetExceptTransparent,
|
|---|
| 174 | {** The pixel is blend over the other one according to alpha values,
|
|---|
| 175 | however no gamma correction is applied. In other words, the color
|
|---|
| 176 | space is assumed to be linear }
|
|---|
| 177 | dmLinearBlend,
|
|---|
| 178 | {** The pixel is blend over the other one according to alpha values,
|
|---|
| 179 | and a gamma correction is applied. In other word, the color
|
|---|
| 180 | space is assumed to be sRGB }
|
|---|
| 181 | dmDrawWithTransparency,
|
|---|
| 182 | {** Values of all channels are combined with Xor. This is useful to
|
|---|
| 183 | compute the binary difference, however it is not something that makes
|
|---|
| 184 | much sense to display on the screen }
|
|---|
| 185 | dmXor);
|
|---|
| 186 |
|
|---|
| 187 | const
|
|---|
| 188 | {** An alias for the linear blend, because it is faster than blending
|
|---|
| 189 | with gamma correction }
|
|---|
| 190 | dmFastBlend = dmLinearBlend;
|
|---|
| 191 |
|
|---|
| 192 | type
|
|---|
| 193 | {* Advanced blending modes. See [http://www.brighthub.com/multimedia/photography/articles/18301.aspx Paint.NET blend modes]
|
|---|
| 194 | and [http://www.pegtop.net/delphi/articles/blendmodes/ Formulas]. Blending layers has two steps. The first one is
|
|---|
| 195 | to apply the blend operations listed below, and the second is the actual merging of the colors }
|
|---|
| 196 | TBlendOperation = (
|
|---|
| 197 | {** Simple blend, except that it forces a linear merge so it is equivalent to ''dmLinearBlend'' }
|
|---|
| 198 | boLinearBlend,
|
|---|
| 199 | {** Simple blend. It is equivalent to ''dmLinearBlend'' or ''dmDrawWithTransparency'' }
|
|---|
| 200 | boTransparent,
|
|---|
| 201 | {** Lighting blend modes (tends to increase the luminosity) }
|
|---|
| 202 | boLighten, boScreen, boAdditive, boLinearAdd, boColorDodge, boDivide, boNiceGlow, boSoftLight, boHardLight,
|
|---|
| 203 | {** Masking blend modes (tends to decrease the luminosity) }
|
|---|
| 204 | boGlow, boReflect, boOverlay, boDarkOverlay, boDarken, boMultiply, boColorBurn,
|
|---|
| 205 | {** Difference blend modes }
|
|---|
| 206 | boDifference, boLinearDifference, boExclusion, boLinearExclusion, boSubtract, boLinearSubtract, boSubtractInverse, boLinearSubtractInverse,
|
|---|
| 207 | {** Negation blend modes }
|
|---|
| 208 | boNegation, boLinearNegation,
|
|---|
| 209 | {** Xor blend mode. It is sightly different from ''dmXor'' because the alpha value is used like in other blends modes }
|
|---|
| 210 | boXor,
|
|---|
| 211 | {** Additional blend modes **}
|
|---|
| 212 | boSvgSoftLight);
|
|---|
| 213 |
|
|---|
| 214 | const
|
|---|
| 215 | {** Alias to glow that express that this blend mode masks the part where the top layer is black }
|
|---|
| 216 | boGlowMask = boGlow;
|
|---|
| 217 | {** Alias because linear or non linear multiply modes are identical }
|
|---|
| 218 | boLinearMultiply = boMultiply;
|
|---|
| 219 | {** Alias to express that dark overlay is simply an overlay with gamma correction }
|
|---|
| 220 | boNonLinearOverlay = boDarkOverlay;
|
|---|
| 221 |
|
|---|
| 222 | const
|
|---|
| 223 | {** String constants for blend modes }
|
|---|
| 224 | BlendOperationStr : array[TBlendOperation] of string
|
|---|
| 225 | = ('LinearBlend', 'Transparent',
|
|---|
| 226 | 'Lighten', 'Screen', 'Additive', 'LinearAdd', 'ColorDodge', 'Divide', 'NiceGlow', 'SoftLight', 'HardLight',
|
|---|
| 227 | 'Glow', 'Reflect', 'Overlay', 'DarkOverlay', 'Darken', 'Multiply', 'ColorBurn',
|
|---|
| 228 | 'Difference', 'LinearDifference', 'Exclusion', 'LinearExclusion', 'Subtract', 'LinearSubtract', 'SubtractInverse', 'LinearSubtractInverse',
|
|---|
| 229 | 'Negation', 'LinearNegation', 'Xor', 'SvgSoftLight');
|
|---|
| 230 |
|
|---|
| 231 | {** Returns the blend mode expressed by the string }
|
|---|
| 232 | function StrToBlendOperation(str: string): TBlendOperation;
|
|---|
| 233 |
|
|---|
| 234 | type
|
|---|
| 235 | {* Specifies how a palette handles the alpha channel }
|
|---|
| 236 | TAlphaChannelPaletteOption = (
|
|---|
| 237 | {** The alpha channel is ignored. The alpha channel is considered to be stored elsewhere }
|
|---|
| 238 | acIgnore,
|
|---|
| 239 | {** One entry is allocated the fully transparent color }
|
|---|
| 240 | acTransparentEntry,
|
|---|
| 241 | {** The alpha channel is fully embedded in the palette so that a color is identified by its four RGBA channels }
|
|---|
| 242 | acFullChannelInPalette);
|
|---|
| 243 |
|
|---|
| 244 | {* Dithering algorithms that specifies how to handle colors that are not found in the palette }
|
|---|
| 245 | TDitheringAlgorithm = (
|
|---|
| 246 | {** The nearest color is to be used instead }
|
|---|
| 247 | daNearestNeighbor,
|
|---|
| 248 | {** The nearest color may be used however another color may be used to compensate for the error,
|
|---|
| 249 | following Floyd-Steinberg algorithm }
|
|---|
| 250 | daFloydSteinberg);
|
|---|
| 251 |
|
|---|
| 252 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 253 | {$i basiccolorspace.inc}
|
|---|
| 254 |
|
|---|
| 255 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 256 | {$i extendedcolorspace.inc}
|
|---|
| 257 |
|
|---|
| 258 | {$ENDIF}
|
|---|
| 259 |
|
|---|
| 260 | {$IFDEF INCLUDE_IMPLEMENTATION}
|
|---|
| 261 | {$UNDEF INCLUDE_IMPLEMENTATION}
|
|---|
| 262 |
|
|---|
| 263 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 264 | {$i basiccolorspace.inc}
|
|---|
| 265 |
|
|---|
| 266 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 267 | {$i extendedcolorspace.inc}
|
|---|
| 268 |
|
|---|
| 269 | function StrToBlendOperation(str: string): TBlendOperation;
|
|---|
| 270 | var op: TBlendOperation;
|
|---|
| 271 | begin
|
|---|
| 272 | result := boTransparent;
|
|---|
| 273 | str := LowerCase(str);
|
|---|
| 274 | for op := low(TBlendOperation) to high(TBlendOperation) do
|
|---|
| 275 | if str = LowerCase(BlendOperationStr[op]) then
|
|---|
| 276 | begin
|
|---|
| 277 | result := op;
|
|---|
| 278 | exit;
|
|---|
| 279 | end;
|
|---|
| 280 | end;
|
|---|
| 281 |
|
|---|
| 282 | {************************** Color functions **************************}
|
|---|
| 283 |
|
|---|
| 284 | procedure AllocateBGRAPixelBuffer(var ABuffer: TBGRAPixelBuffer; ASize: integer);
|
|---|
| 285 | begin
|
|---|
| 286 | if ASize > length(ABuffer) then
|
|---|
| 287 | setlength(ABuffer, max(length(ABuffer)*2,ASize));
|
|---|
| 288 | end;
|
|---|
| 289 |
|
|---|
| 290 | function BGRA(red, green, blue, alpha: byte): TBGRAPixel;
|
|---|
| 291 | begin
|
|---|
| 292 | DWord(result) := (red shl TBGRAPixel_RedShift) or
|
|---|
| 293 | (green shl TBGRAPixel_GreenShift) or
|
|---|
| 294 | (blue shl TBGRAPixel_BlueShift) or
|
|---|
| 295 | (alpha shl TBGRAPixel_AlphaShift);
|
|---|
| 296 | end;
|
|---|
| 297 |
|
|---|
| 298 | function BGRA(red, green, blue: byte): TBGRAPixel; overload;
|
|---|
| 299 | begin
|
|---|
| 300 | DWord(result) := (red shl TBGRAPixel_RedShift) or
|
|---|
| 301 | (green shl TBGRAPixel_GreenShift) or
|
|---|
| 302 | (blue shl TBGRAPixel_BlueShift) or
|
|---|
| 303 | (255 shl TBGRAPixel_AlphaShift);
|
|---|
| 304 | end;
|
|---|
| 305 |
|
|---|
| 306 | operator = (const c1, c2: TBGRAPixel): boolean;
|
|---|
| 307 | begin
|
|---|
| 308 | if (c1.alpha = 0) and (c2.alpha = 0) then
|
|---|
| 309 | Result := True
|
|---|
| 310 | else
|
|---|
| 311 | Result := (c1.alpha = c2.alpha) and (c1.red = c2.red) and
|
|---|
| 312 | (c1.green = c2.green) and (c1.blue = c2.blue);
|
|---|
| 313 | end;
|
|---|
| 314 |
|
|---|
| 315 | function GetIntensity(c: TBGRAPixel): word;
|
|---|
| 316 | begin
|
|---|
| 317 | Result := c.red;
|
|---|
| 318 | if c.green > Result then
|
|---|
| 319 | Result := c.green;
|
|---|
| 320 | if c.blue > Result then
|
|---|
| 321 | Result := c.blue;
|
|---|
| 322 | result := GammaExpansionTab[Result];
|
|---|
| 323 | end;
|
|---|
| 324 |
|
|---|
| 325 | function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
|
|---|
| 326 | begin
|
|---|
| 327 | result := GammaCompression(SetIntensity(GammaExpansion(c),intensity));
|
|---|
| 328 | end;
|
|---|
| 329 |
|
|---|
| 330 | function GetLightness(c: TBGRAPixel): word;
|
|---|
| 331 | begin
|
|---|
| 332 | result := GetLightness(GammaExpansion(c));
|
|---|
| 333 | end;
|
|---|
| 334 |
|
|---|
| 335 | function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
|
|---|
| 336 | begin
|
|---|
| 337 | result := GammaCompression(SetLightness(GammaExpansion(c),lightness));
|
|---|
| 338 | end;
|
|---|
| 339 |
|
|---|
| 340 | function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel;
|
|---|
| 341 | var
|
|---|
| 342 | r,g,b: word;
|
|---|
| 343 | lightness256: byte;
|
|---|
| 344 | begin
|
|---|
| 345 | if lightness <= 32768 then
|
|---|
| 346 | begin
|
|---|
| 347 | if lightness = 32768 then
|
|---|
| 348 | result := color else
|
|---|
| 349 | begin
|
|---|
| 350 | lightness256 := GammaCompressionTab[lightness shl 1];
|
|---|
| 351 | result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
|
|---|
| 352 | color.blue * lightness256 shr 8, color.alpha);
|
|---|
| 353 | end;
|
|---|
| 354 | end else
|
|---|
| 355 | begin
|
|---|
| 356 | if lightness = 65535 then
|
|---|
| 357 | result := BGRA(255,255,255,color.alpha) else
|
|---|
| 358 | begin
|
|---|
| 359 | lightness -= 32767;
|
|---|
| 360 | r := GammaExpansionTab[color.red];
|
|---|
| 361 | g := GammaExpansionTab[color.green];
|
|---|
| 362 | b := GammaExpansionTab[color.blue];
|
|---|
| 363 | result := BGRA(GammaCompressionTab[ r + (not r)*lightness shr 15 ],
|
|---|
| 364 | GammaCompressionTab[ g + (not g)*lightness shr 15 ],
|
|---|
| 365 | GammaCompressionTab[ b + (not b)*lightness shr 15 ],
|
|---|
| 366 | color.alpha);
|
|---|
| 367 | end;
|
|---|
| 368 | end;
|
|---|
| 369 | end;
|
|---|
| 370 |
|
|---|
| 371 | function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
|
|---|
| 372 | var
|
|---|
| 373 | maxValue,invMaxValue,r,g,b: longword;
|
|---|
| 374 | lightness256: byte;
|
|---|
| 375 | begin
|
|---|
| 376 | if lightness <= 32768 then
|
|---|
| 377 | begin
|
|---|
| 378 | if lightness = 32768 then
|
|---|
| 379 | result := color else
|
|---|
| 380 | begin
|
|---|
| 381 | lightness256 := GammaCompressionTab[lightness shl 1];
|
|---|
| 382 | result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
|
|---|
| 383 | color.blue * lightness256 shr 8, color.alpha);
|
|---|
| 384 | end;
|
|---|
| 385 | end else
|
|---|
| 386 | begin
|
|---|
| 387 | r := CombineLightness(GammaExpansionTab[color.red], lightness);
|
|---|
| 388 | g := CombineLightness(GammaExpansionTab[color.green], lightness);
|
|---|
| 389 | b := CombineLightness(GammaExpansionTab[color.blue], lightness);
|
|---|
| 390 | maxValue := r;
|
|---|
| 391 | if g > maxValue then maxValue := g;
|
|---|
| 392 | if b > maxValue then maxValue := b;
|
|---|
| 393 | if maxValue <= 65535 then
|
|---|
| 394 | result := BGRA(GammaCompressionTab[r],
|
|---|
| 395 | GammaCompressionTab[g],
|
|---|
| 396 | GammaCompressionTab[b],
|
|---|
| 397 | color.alpha)
|
|---|
| 398 | else
|
|---|
| 399 | begin
|
|---|
| 400 | invMaxValue := (longword(2147483647)+longword(maxValue-1)) div maxValue;
|
|---|
| 401 | maxValue := (maxValue-65535) shr 1;
|
|---|
| 402 | r := r*invMaxValue shr 15 + maxValue;
|
|---|
| 403 | g := g*invMaxValue shr 15 + maxValue;
|
|---|
| 404 | b := b*invMaxValue shr 15 + maxValue;
|
|---|
| 405 | if r >= 65535 then result.red := 255 else
|
|---|
| 406 | result.red := GammaCompressionTab[r];
|
|---|
| 407 | if g >= 65535 then result.green := 255 else
|
|---|
| 408 | result.green := GammaCompressionTab[g];
|
|---|
| 409 | if b >= 65535 then result.blue := 255 else
|
|---|
| 410 | result.blue := GammaCompressionTab[b];
|
|---|
| 411 | result.alpha := color.alpha;
|
|---|
| 412 | end;
|
|---|
| 413 | end;
|
|---|
| 414 | end;
|
|---|
| 415 |
|
|---|
| 416 | function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
|
|---|
| 417 | {$ifdef CPUI386} {$asmmode intel} assembler;
|
|---|
| 418 | asm
|
|---|
| 419 | imul edx
|
|---|
| 420 | shl edx, 17
|
|---|
| 421 | shr eax, 15
|
|---|
| 422 | or edx, eax
|
|---|
| 423 | mov result, edx
|
|---|
| 424 | end;
|
|---|
| 425 | {$ELSE}
|
|---|
| 426 | begin
|
|---|
| 427 | if (lightness1 < 0) xor (lightness2 < 0) then
|
|---|
| 428 | result := -(int64(-lightness1)*lightness2 shr 15)
|
|---|
| 429 | else
|
|---|
| 430 | result := int64(lightness1)*lightness2 shr 15;
|
|---|
| 431 | end;
|
|---|
| 432 | {$ENDIF}
|
|---|
| 433 |
|
|---|
| 434 | // Conversion to grayscale by taking into account
|
|---|
| 435 | // different color weights
|
|---|
| 436 | function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
|
|---|
| 437 | var
|
|---|
| 438 | ec: TExpandedPixel;
|
|---|
| 439 | gray: word;
|
|---|
| 440 | cgray: byte;
|
|---|
| 441 | begin
|
|---|
| 442 | if c.alpha = 0 then
|
|---|
| 443 | begin
|
|---|
| 444 | result := BGRAPixelTransparent;
|
|---|
| 445 | exit;
|
|---|
| 446 | end;
|
|---|
| 447 | //gamma expansion
|
|---|
| 448 | ec := GammaExpansion(c);
|
|---|
| 449 | //gray composition
|
|---|
| 450 | gray := (ec.red * redWeightShl10 + ec.green * greenWeightShl10 +
|
|---|
| 451 | ec.blue * blueWeightShl10 + 512) shr 10;
|
|---|
| 452 | //gamma compression
|
|---|
| 453 | cgray := GammaCompressionTab[gray];
|
|---|
| 454 | Result.red := cgray;
|
|---|
| 455 | Result.green := cgray;
|
|---|
| 456 | Result.blue := cgray;
|
|---|
| 457 | Result.alpha := c.alpha;
|
|---|
| 458 | end;
|
|---|
| 459 |
|
|---|
| 460 | function BGRAToGrayscaleLinear(c: TBGRAPixel): TBGRAPixel;
|
|---|
| 461 | var
|
|---|
| 462 | gray: byte;
|
|---|
| 463 | begin
|
|---|
| 464 | if c.alpha = 0 then
|
|---|
| 465 | begin
|
|---|
| 466 | result := BGRAPixelTransparent;
|
|---|
| 467 | exit;
|
|---|
| 468 | end;
|
|---|
| 469 | //gray composition
|
|---|
| 470 | gray := (c.red * redWeightShl10 + c.green * greenWeightShl10 +
|
|---|
| 471 | c.blue * blueWeightShl10 + 512) shr 10;
|
|---|
| 472 | //gamma compression
|
|---|
| 473 | Result.red := gray;
|
|---|
| 474 | Result.green := gray;
|
|---|
| 475 | Result.blue := gray;
|
|---|
| 476 | Result.alpha := c.alpha;
|
|---|
| 477 | end;
|
|---|
| 478 |
|
|---|
| 479 | function GrayscaleToBGRA(lightness: word): TBGRAPixel;
|
|---|
| 480 | begin
|
|---|
| 481 | result.red := GammaCompressionTab[lightness];
|
|---|
| 482 | result.green := result.red;
|
|---|
| 483 | result.blue := result.red;
|
|---|
| 484 | result.alpha := $ff;
|
|---|
| 485 | end;
|
|---|
| 486 |
|
|---|
| 487 | { Merge linearly two colors of same importance }
|
|---|
| 488 | function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel;
|
|---|
| 489 | var c12: cardinal;
|
|---|
| 490 | begin
|
|---|
| 491 | if (c1.alpha = 0) then
|
|---|
| 492 | Result := c2
|
|---|
| 493 | else
|
|---|
| 494 | if (c2.alpha = 0) then
|
|---|
| 495 | Result := c1
|
|---|
| 496 | else
|
|---|
| 497 | begin
|
|---|
| 498 | c12 := c1.alpha + c2.alpha;
|
|---|
| 499 | Result.red := (c1.red * c1.alpha + c2.red * c2.alpha + c12 shr 1) div c12;
|
|---|
| 500 | Result.green := (c1.green * c1.alpha + c2.green * c2.alpha + c12 shr 1) div c12;
|
|---|
| 501 | Result.blue := (c1.blue * c1.alpha + c2.blue * c2.alpha + c12 shr 1) div c12;
|
|---|
| 502 | Result.alpha := (c12 + 1) shr 1;
|
|---|
| 503 | end;
|
|---|
| 504 | end;
|
|---|
| 505 |
|
|---|
| 506 | function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel;
|
|---|
| 507 | weight2: integer): TBGRAPixel;
|
|---|
| 508 | var
|
|---|
| 509 | f1,f2,f12: int64;
|
|---|
| 510 | begin
|
|---|
| 511 | if (weight1 = 0) then
|
|---|
| 512 | begin
|
|---|
| 513 | if (weight2 = 0) then
|
|---|
| 514 | result := BGRAPixelTransparent
|
|---|
| 515 | else
|
|---|
| 516 | Result := c2
|
|---|
| 517 | end
|
|---|
| 518 | else
|
|---|
| 519 | if (weight2 = 0) then
|
|---|
| 520 | Result := c1
|
|---|
| 521 | else
|
|---|
| 522 | if (weight1+weight2 = 0) then
|
|---|
| 523 | Result := BGRAPixelTransparent
|
|---|
| 524 | else
|
|---|
| 525 | begin
|
|---|
| 526 | f1 := int64(c1.alpha)*weight1;
|
|---|
| 527 | f2 := int64(c2.alpha)*weight2;
|
|---|
| 528 | f12 := f1+f2;
|
|---|
| 529 | if f12 = 0 then
|
|---|
| 530 | result := BGRAPixelTransparent
|
|---|
| 531 | else
|
|---|
| 532 | begin
|
|---|
| 533 | Result.red := (c1.red * f1 + c2.red * f2 + f12 shr 1) div f12;
|
|---|
| 534 | Result.green := (c1.green * f1 + c2.green * f2 + f12 shr 1) div f12;
|
|---|
| 535 | Result.blue := (c1.blue * f1 + c2.blue * f2 + f12 shr 1) div f12;
|
|---|
| 536 | {$hints off}
|
|---|
| 537 | Result.alpha := (f12 + ((weight1+weight2) shr 1)) div (weight1+weight2);
|
|---|
| 538 | {$hints on}
|
|---|
| 539 | end;
|
|---|
| 540 | end;
|
|---|
| 541 | end;
|
|---|
| 542 |
|
|---|
| 543 | function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel;
|
|---|
| 544 | weight2: byte): TBGRAPixel;
|
|---|
| 545 | var
|
|---|
| 546 | w1,w2,f1,f2,f12,a: UInt32or64;
|
|---|
| 547 | begin
|
|---|
| 548 | w1 := weight1;
|
|---|
| 549 | w2 := weight2;
|
|---|
| 550 | if (w1 = 0) then
|
|---|
| 551 | begin
|
|---|
| 552 | if (w2 = 0) then
|
|---|
| 553 | result := BGRAPixelTransparent
|
|---|
| 554 | else
|
|---|
| 555 | Result := c2
|
|---|
| 556 | end
|
|---|
| 557 | else
|
|---|
| 558 | if (w2 = 0) then
|
|---|
| 559 | Result := c1
|
|---|
| 560 | else
|
|---|
| 561 | begin
|
|---|
| 562 | f1 := c1.alpha*w1;
|
|---|
| 563 | f2 := c2.alpha*w2;
|
|---|
| 564 | a := (f1+f2 + ((w1+w2) shr 1)) div (w1+w2);
|
|---|
| 565 | if a = 0 then
|
|---|
| 566 | begin
|
|---|
| 567 | result := BGRAPixelTransparent;
|
|---|
| 568 | exit;
|
|---|
| 569 | end else
|
|---|
| 570 | Result.alpha := a;
|
|---|
| 571 | {$IFNDEF CPU64}
|
|---|
| 572 | if (f1 >= 32768) or (f2 >= 32768) then
|
|---|
| 573 | begin
|
|---|
| 574 | f1 := f1 shr 1;
|
|---|
| 575 | f2 := f2 shr 1;
|
|---|
| 576 | end;
|
|---|
| 577 | {$ENDIF}
|
|---|
| 578 | f12 := f1+f2;
|
|---|
| 579 | Result.red := GammaCompressionTab[(GammaExpansionTab[c1.red] * f1 + GammaExpansionTab[c2.red] * f2) div f12];
|
|---|
| 580 | Result.green := GammaCompressionTab[(GammaExpansionTab[c1.green] * f1 + GammaExpansionTab[c2.green] * f2) div f12];
|
|---|
| 581 | Result.blue := GammaCompressionTab[(GammaExpansionTab[c1.blue] * f1 + GammaExpansionTab[c2.blue] * f2) div f12];
|
|---|
| 582 | end;
|
|---|
| 583 | end;
|
|---|
| 584 |
|
|---|
| 585 | { Convert a TColor value to a TBGRAPixel value }
|
|---|
| 586 | {$PUSH}{$R-}
|
|---|
| 587 | function ColorToBGRA(color: TColor): TBGRAPixel; overload;
|
|---|
| 588 | begin
|
|---|
| 589 | if (color < 0) or (color > $ffffff) then color := ColorToRGB(color);
|
|---|
| 590 | RedGreenBlue(color, Result.red,Result.green,Result.blue);
|
|---|
| 591 | Result.alpha := 255;
|
|---|
| 592 | end;
|
|---|
| 593 |
|
|---|
| 594 | function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
|
|---|
| 595 | begin
|
|---|
| 596 | if (color < 0) or (color > $ffffff) then color := ColorToRGB(color);
|
|---|
| 597 | RedGreenBlue(color, Result.red,Result.green,Result.blue);
|
|---|
| 598 | Result.alpha := opacity;
|
|---|
| 599 | end;
|
|---|
| 600 | {$POP}
|
|---|
| 601 |
|
|---|
| 602 | function BGRAToColor(c: TBGRAPixel): TColor;
|
|---|
| 603 | begin
|
|---|
| 604 | Result := RGBToColor(c.red, c.green, c.blue);
|
|---|
| 605 | end;
|
|---|
| 606 |
|
|---|
| 607 | { Conversion from TFPColor to TBGRAPixel assuming TFPColor
|
|---|
| 608 | is already gamma compressed }
|
|---|
| 609 | function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
|
|---|
| 610 | begin
|
|---|
| 611 | with AValue do
|
|---|
| 612 | Result := BGRA(red shr 8, green shr 8, blue shr 8, alpha shr 8);
|
|---|
| 613 | end;
|
|---|
| 614 |
|
|---|
| 615 | function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
|
|---|
| 616 | begin
|
|---|
| 617 | result.red := AValue.red shl 8 + AValue.red;
|
|---|
| 618 | result.green := AValue.green shl 8 + AValue.green;
|
|---|
| 619 | result.blue := AValue.blue shl 8 + AValue.blue;
|
|---|
| 620 | result.alpha := AValue.alpha shl 8 + AValue.alpha;
|
|---|
| 621 | end;
|
|---|
| 622 |
|
|---|
| 623 | function Color16BitToBGRA(AColor: Word): TBGRAPixel;
|
|---|
| 624 | begin
|
|---|
| 625 | result := BGRA( ((AColor and $F800) shr 11)*255 div 31,
|
|---|
| 626 | ((AColor and $07e0) shr 5)*255 div 63,
|
|---|
| 627 | (AColor and $001f)*255 div 31 );
|
|---|
| 628 | end;
|
|---|
| 629 |
|
|---|
| 630 | function BGRAToColor16Bit(const AColor: TBGRAPixel): Word;
|
|---|
| 631 | begin
|
|---|
| 632 | result := (((AColor.Red * 31 + 64) div 255) shl 11) +
|
|---|
| 633 | (((AColor.green * 63 + 64) div 255) shl 5) +
|
|---|
| 634 | ((AColor.blue * 31 + 64) div 255);
|
|---|
| 635 | end;
|
|---|
| 636 |
|
|---|
| 637 | function BGRAWordDiff(c1, c2: TBGRAPixel): word;
|
|---|
| 638 | begin
|
|---|
| 639 | result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2));
|
|---|
| 640 | end;
|
|---|
| 641 |
|
|---|
| 642 | function BGRADiff(c1,c2: TBGRAPixel): byte;
|
|---|
| 643 | begin
|
|---|
| 644 | result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2)) shr 8;
|
|---|
| 645 | end;
|
|---|
| 646 |
|
|---|
| 647 | function FastBGRALinearDiff(c1, c2: TBGRAPixel): byte;
|
|---|
| 648 | begin
|
|---|
| 649 | result := max(min((abs(c1.red-c2.red)+(abs(c1.green-c2.green) shl 1)+abs(c1.blue-c2.blue)) shr 2,
|
|---|
| 650 | min(c1.alpha,c2.alpha)), abs(c1.alpha-c2.alpha));
|
|---|
| 651 | end;
|
|---|
| 652 |
|
|---|
| 653 | function FastBGRAExpandedDiff(c1, c2: TBGRAPixel): word;
|
|---|
| 654 | var wa1,wa2: word;
|
|---|
| 655 | begin
|
|---|
| 656 | wa1 := c1.alpha shl 8 + c1.alpha;
|
|---|
| 657 | wa2 := (c2.alpha shl 8) + c2.alpha;
|
|---|
| 658 | result := max(min((abs(GammaExpansionTab[c1.red]-GammaExpansionTab[c2.red])+
|
|---|
| 659 | (abs(GammaExpansionTab[c1.green]-GammaExpansionTab[c2.green]) shl 1)+
|
|---|
| 660 | abs(GammaExpansionTab[c1.blue]-GammaExpansionTab[c2.blue])) shr 2,
|
|---|
| 661 | min(wa1,wa2)),
|
|---|
| 662 | abs(wa1-wa2));
|
|---|
| 663 | end;
|
|---|
| 664 |
|
|---|
| 665 | function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel;
|
|---|
| 666 | var
|
|---|
| 667 | sumR,sumG,sumB,sumA: NativeUInt;
|
|---|
| 668 | i: integer;
|
|---|
| 669 | begin
|
|---|
| 670 | if length(colors)<=0 then
|
|---|
| 671 | begin
|
|---|
| 672 | result := BGRAPixelTransparent;
|
|---|
| 673 | exit;
|
|---|
| 674 | end;
|
|---|
| 675 | sumR := 0;
|
|---|
| 676 | sumG := 0;
|
|---|
| 677 | sumB := 0;
|
|---|
| 678 | sumA := 0;
|
|---|
| 679 | for i := 0 to high(colors) do
|
|---|
| 680 | with colors[i] do
|
|---|
| 681 | begin
|
|---|
| 682 | sumR += red*alpha;
|
|---|
| 683 | sumG += green*alpha;
|
|---|
| 684 | sumB += blue*alpha;
|
|---|
| 685 | sumA += alpha;
|
|---|
| 686 | end;
|
|---|
| 687 | if sumA > 0 then
|
|---|
| 688 | begin
|
|---|
| 689 | result.red := (sumR + sumA shr 1) div sumA;
|
|---|
| 690 | result.green := (sumG + sumA shr 1) div sumA;
|
|---|
| 691 | result.blue := (sumB + sumA shr 1) div sumA;
|
|---|
| 692 | result.alpha := sumA div longword(length(colors));
|
|---|
| 693 | end
|
|---|
| 694 | else
|
|---|
| 695 | result := BGRAPixelTransparent;
|
|---|
| 696 | end;
|
|---|
| 697 |
|
|---|
| 698 | function MapHeight(Color: TBGRAPixel): Single;
|
|---|
| 699 | var intval: integer;
|
|---|
| 700 | begin
|
|---|
| 701 | intval := color.Green shl 16 + color.red shl 8 + color.blue;
|
|---|
| 702 | result := intval*5.960464832810452e-8;
|
|---|
| 703 | end;
|
|---|
| 704 |
|
|---|
| 705 | function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
|
|---|
| 706 | var intval: integer;
|
|---|
| 707 | begin
|
|---|
| 708 | if Height >= 1 then result := BGRA(255,255,255,alpha) else
|
|---|
| 709 | if Height <= 0 then result := BGRA(0,0,0,alpha) else
|
|---|
| 710 | begin
|
|---|
| 711 | intval := round(Height*16777215);
|
|---|
| 712 | {$PUSH}{$R-}
|
|---|
| 713 | result := BGRA(intval shr 8,intval shr 16,intval,alpha);
|
|---|
| 714 | {$POP}
|
|---|
| 715 | end;
|
|---|
| 716 | end;
|
|---|
| 717 | {$ENDIF}
|
|---|
| 718 |
|
|---|
| 719 | {$IFDEF INCLUDE_INIT}
|
|---|
| 720 | {$UNDEF INCLUDE_INIT}
|
|---|
| 721 | BGRASetGamma();
|
|---|
| 722 |
|
|---|
| 723 | {$DEFINE INCLUDE_INITIALIZATION}
|
|---|
| 724 | {$i extendedcolorspace.inc}
|
|---|
| 725 | {$ENDIF}
|
|---|