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}
|
---|