source: trunk/Packages/bgrabitmap/extendedcolorspace.inc

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 26.4 KB
Line 
1{$IFDEF INCLUDE_INTERFACE}
2{$UNDEF INCLUDE_INTERFACE}
3
4type
5 TSpectralLocusPoint = record
6 W,X,Y,Z: Single;
7 end;
8
9const //horseshoe shape of visible colors
10 SpectralLocus: array[0..94] of TSpectralLocusPoint =
11 ((W:360; X:0.0001299; Y:0.000003917; Z:0.0006061),
12 (W:365; X:0.0002321; Y:0.000006965; Z:0.001086),
13 (W:370; X:0.0004149; Y:0.00001239; Z:0.001946),
14 (W:375; X:0.0007416; Y:0.00002202; Z:0.003486),
15 (W:380; X:0.001368; Y:0.000039; Z:0.006450001),
16 (W:385; X:0.002236; Y:0.000064; Z:0.01054999),
17 (W:390; X:0.004243; Y:0.00012; Z:0.02005001),
18 (W:395; X:0.00765; Y:0.000217; Z:0.03621),
19 (W:400; X:0.01431; Y:0.000396; Z:0.06785001),
20 (W:405; X:0.02319; Y:0.00064; Z:0.1102),
21 (W:410; X:0.04351; Y:0.00121; Z:0.2074),
22 (W:415; X:0.07763; Y:0.00218; Z:0.3713),
23 (W:420; X:0.13438; Y:0.004; Z:0.6456),
24 (W:425; X:0.21477; Y:0.0073; Z:1.0390501),
25 (W:430; X:0.2839; Y:0.0116; Z:1.3856),
26 (W:435; X:0.3285; Y:0.01684; Z:1.62296),
27 (W:440; X:0.34828; Y:0.023; Z:1.74706),
28 (W:445; X:0.34806; Y:0.0298; Z:1.7826),
29 (W:450; X:0.3362; Y:0.038; Z:1.77211),
30 (W:455; X:0.3187; Y:0.048; Z:1.7441),
31 (W:460; X:0.2908; Y:0.06; Z:1.6692),
32 (W:465; X:0.2511; Y:0.0739; Z:1.5281),
33 (W:470; X:0.19536; Y:0.09098; Z:1.28764),
34 (W:475; X:0.1421; Y:0.1126; Z:1.0419),
35 (W:480; X:0.09564; Y:0.13902; Z:0.8129501),
36 (W:485; X:0.05795001; Y:0.1693; Z:0.6162),
37 (W:490; X:0.03201; Y:0.20802; Z:0.46518),
38 (W:495; X:0.0147; Y:0.2586; Z:0.3533),
39 (W:500; X:0.0049; Y:0.323; Z:0.272),
40 (W:505; X:0.0024; Y:0.4073; Z:0.2123),
41 (W:510; X:0.0093; Y:0.503; Z:0.1582),
42 (W:515; X:0.0291; Y:0.6082; Z:0.1117),
43 (W:520; X:0.06327; Y:0.71; Z:0.07824999),
44 (W:525; X:0.1096; Y:0.7932; Z:0.05725001),
45 (W:530; X:0.1655; Y:0.862; Z:0.04216),
46 (W:535; X:0.2257499; Y:0.9148501; Z:0.02984),
47 (W:540; X:0.2904; Y:0.954; Z:0.0203),
48 (W:545; X:0.3597; Y:0.9803; Z:0.0134),
49 (W:550; X:0.4334499; Y:0.9949501; Z:0.008749999),
50 (W:555; X:0.5120501; Y:1; Z:0.005749999),
51 (W:560; X:0.5945; Y:0.995; Z:0.0039),
52 (W:565; X:0.6784; Y:0.9786; Z:0.002749999),
53 (W:570; X:0.7621; Y:0.952; Z:0.0021),
54 (W:575; X:0.8425; Y:0.9154; Z:0.0018),
55 (W:580; X:0.9163; Y:0.87; Z:0.001650001),
56 (W:585; X:0.9786; Y:0.8163; Z:0.0014),
57 (W:590; X:1.0263; Y:0.757; Z:0.0011),
58 (W:595; X:1.0567; Y:0.6949; Z:0.001),
59 (W:600; X:1.0622; Y:0.631; Z:0.0008),
60 (W:605; X:1.0456; Y:0.5668; Z:0.0006),
61 (W:610; X:1.0026; Y:0.503; Z:0.00034),
62 (W:615; X:0.9384; Y:0.4412; Z:0.00024),
63 (W:620; X:0.8544499; Y:0.381; Z:0.00019),
64 (W:625; X:0.7514; Y:0.321; Z:0.0001),
65 (W:630; X:0.6424; Y:0.265; Z:0.00005),
66 (W:635; X:0.5419; Y:0.217; Z:0.00003),
67 (W:640; X:0.4479; Y:0.175; Z:0.00002),
68 (W:645; X:0.3608; Y:0.1382; Z:0.00001),
69 (W:650; X:0.2835; Y:0.107; Z:0),
70 (W:655; X:0.2187; Y:0.0816; Z:0),
71 (W:660; X:0.1649; Y:0.061; Z:0),
72 (W:665; X:0.1212; Y:0.04458; Z:0),
73 (W:670; X:0.0874; Y:0.032; Z:0),
74 (W:675; X:0.0636; Y:0.0232; Z:0),
75 (W:680; X:0.04677; Y:0.017; Z:0),
76 (W:685; X:0.0329; Y:0.01192; Z:0),
77 (W:690; X:0.0227; Y:0.00821; Z:0),
78 (W:695; X:0.01584; Y:0.005723; Z:0),
79 (W:700; X:0.01135916; Y:0.004102; Z:0),
80 (W:705; X:0.008110916; Y:0.002929; Z:0),
81 (W:710; X:0.005790346; Y:0.002091; Z:0),
82 (W:715; X:0.004106457; Y:0.001484; Z:0),
83 (W:720; X:0.002899327; Y:0.001047; Z:0),
84 (W:725; X:0.00204919; Y:0.00074; Z:0),
85 (W:730; X:0.001439971; Y:0.00052; Z:0),
86 (W:735; X:0.0009999493; Y:0.0003611; Z:0),
87 (W:740; X:0.0006900786; Y:0.0002492; Z:0),
88 (W:745; X:0.0004760213; Y:0.0001719; Z:0),
89 (W:750; X:0.0003323011; Y:0.00012; Z:0),
90 (W:755; X:0.0002348261; Y:0.0000848; Z:0),
91 (W:760; X:0.0001661505; Y:0.00006; Z:0),
92 (W:765; X:0.000117413; Y:0.0000424; Z:0),
93 (W:770; X:8.307527E-05; Y:0.00003; Z:0),
94 (W:775; X:5.870652E-05; Y:0.0000212; Z:0),
95 (W:780; X:4.150994E-05; Y:0.00001499; Z:0),
96 (W:785; X:2.935326E-05; Y:0.0000106; Z:0),
97 (W:790; X:2.067383E-05; Y:7.4657E-06; Z:0),
98 (W:795; X:1.455977E-05; Y:5.2578E-06; Z:0),
99 (W:800; X:0.000010254; Y:3.7029E-06; Z:0),
100 (W:805; X:7.221456E-06; Y:2.6078E-06; Z:0),
101 (W:810; X:5.085868E-06; Y:1.8366E-06; Z:0),
102 (W:815; X:3.581652E-06; Y:1.2934E-06; Z:0),
103 (W:820; X:2.522525E-06; Y:9.1093E-07; Z:0),
104 (W:825; X:1.776509E-06; Y:6.4153E-07; Z:0),
105 (W:830; X:1.251141E-06; Y:4.5181E-07; Z:0));
106
107type
108 PXYZReferenceWhite = ^TXYZReferenceWhite;
109 TXYZReferenceWhite = packed record
110 X, Y, Z: single;
111 ObserverAngle: integer;
112 Illuminant: string
113 end;
114
115 TCustomColorspace = class;
116 TColorspaceAny = class of TCustomColorspace;
117
118 TColorspaceConvertArrayProc = procedure(ASource: pointer; ADest: Pointer; ACount: integer;
119 ASourceStride:integer; ADestStride:integer; AReferenceWhite: PXYZReferenceWhite);
120
121 { TCustomColorspace }
122
123 TCustomColorspace = class
124 class function GetChannelName(AIndex: integer): string; virtual; abstract;
125 class function GetChannelCount: integer; virtual; abstract;
126 class function IndexOfChannel(AName: string): integer;
127 class function GetMaxValue(AIndex: integer): single; virtual; abstract;
128 class function GetMinValue(AIndex: integer): single; virtual; abstract;
129 class function GetName: string; virtual; abstract;
130 class function GetSize: integer; virtual; abstract;
131 class function GetChannel(AColor: Pointer; AIndex: integer): single; virtual; abstract;
132 class procedure SetChannel(AColor: Pointer; AIndex: integer; AValue: single); virtual; abstract;
133 class procedure Convert(const ASource; out ADest; ADestColorspace: TColorspaceAny;
134 ACount: integer = 1; AReferenceWhite: PXYZReferenceWhite = nil);
135 class function HasReferenceWhite: boolean; virtual; abstract;
136 end;
137
138 ColorspaceCollection = class
139 protected
140 class var FColorspaces : array of TColorspaceAny;
141 class var FColorspaceCount: integer;
142 class var FColorspaceConversions: array of array of TColorspaceConvertArrayProc;
143 public
144 class function GetCount: integer;
145 class function GetItem(AIndex: integer): TColorspaceAny;
146 class function IndexOf(AColorspace: TColorspaceAny): integer;
147 class procedure Add(AColorspace: TColorspaceAny);
148 class procedure AddConversion(ASource: TColorspaceAny; ADest: TColorspaceAny; AConversion: TColorspaceConvertArrayProc);
149 class function GetConversion(ASource: TColorspaceAny; ADest: TColorspaceAny): TColorspaceConvertArrayProc;
150 end;
151
152{$DEFINE INCLUDE_INTERFACE}
153{$I generatedcolorspace.inc}
154
155function StdRGBAToBGRAPixel(const AStdRGBA: TStdRGBA): TBGRAPixel;
156function BGRAPixelToStdRGBA(const ABGRAPixel: TBGRAPixel): TStdRGBA;
157function LinearRGBAToExpandedPixel(const ALinearRGBA: TLinearRGBA): TExpandedPixel;
158function ExpandedPixelToLinearRGBA(const AExpandedPixel: TExpandedPixel): TLinearRGBA;
159function LinearRGBAToXYZA(const ALinearRGBA: TLinearRGBA): TXYZA;
160function XYZAToLinearRGBA(const AXYZA: TXYZA): TLinearRGBA;
161function XYZAToLabA(const AXYZA: TXYZA): TLabA; overload;
162function XYZAToLabA(const AXYZA: TXYZA; const AReferenceWhite: TXYZReferenceWhite): TLabA; overload;
163function LabAToXYZA(const ALabA: TLabA): TXYZA; overload;
164function LabAToXYZA(const ALabA: TLabA; const AReferenceWhite: TXYZReferenceWhite): TXYZA; overload;
165function StdRGBAToLinearRGBA(const AStdRGBA: TStdRGBA): TLinearRGBA;
166function LinearRGBAToStdRGBA(const ALinearRGBA: TLinearRGBA): TStdRGBA;
167function StdRGBAToStdHSLA(const AStdRGBA: TStdRGBA): TStdHSLA;
168function StdHSLAToStdRGBA(const AStdHSLA: TStdHSLA): TStdRGBA;
169function StdRGBAToStdHSVA(const AStdRGBA: TStdRGBA): TStdHSVA;
170function StdHSVAToStdRGBA(const AStdHSVA: TStdHSVA): TStdRGBA;
171function StdHSLAToStdHSVA(const AStdHSLA: TStdHSLA): TStdHSVA;
172function StdHSVAToStdHSLA(const AStdHSVA: TStdHSVA): TStdHSLA;
173function StdRGBAToStdCMYK(const AStdRGBA: TStdRGBA): TStdCMYK;
174function StdCMYKToStdRGBA(const AStdCMYK: TStdCMYK; AAlpha: Single = 1): TStdRGBA;
175function LabAToLChA(const ALabA: TLabA): TLChA;
176function LChAToLabA(const ALChA: TLChA): TLabA;
177function AdobeRGBAToXYZA(const ASource: TAdobeRGBA): TXYZA;
178function XYZAToAdobeRGBA(const AXYZA: TXYZA): TAdobeRGBA;
179
180procedure SetReferenceWhite(AObserverAngle: integer; AIlluminant: string); overload;
181procedure SetReferenceWhite(AReferenceWhite: TXYZReferenceWhite); overload;
182function GetReferenceWhite: TXYZReferenceWhite;
183
184procedure AddReferenceWhite(AReferenceWhite: TXYZReferenceWhite); overload;
185procedure AddReferenceWhite(AObserverAngle: integer; AIlluminant: string; AX, AY, AZ: single); overload;
186function GetReferenceWhiteCount: integer;
187function GetReferenceWhiteByIndex(AIndex: integer): TXYZReferenceWhite;
188
189{$ENDIF}
190
191{$IFDEF INCLUDE_IMPLEMENTATION}
192{$UNDEF INCLUDE_IMPLEMENTATION}
193
194{ TCustomColorspace }
195
196class function TCustomColorspace.IndexOfChannel(AName: string): integer;
197var
198 i: Integer;
199begin
200 for i := 0 to GetChannelCount-1 do
201 if GetChannelName(i) = AName then exit(i);
202 exit(-1);
203end;
204
205class procedure TCustomColorspace.Convert(const ASource;
206 out ADest; ADestColorspace: TColorspaceAny;
207 ACount: integer; AReferenceWhite: PXYZReferenceWhite);
208var
209 convProc: TColorspaceConvertArrayProc;
210begin
211 if self = TCustomColorspace then
212 raise exception.Create('Cannot convert from abstract colorspace')
213 else
214 if self = ADestColorspace then
215 move(ASource, {%H-}ADest, self.GetSize * ACount)
216 else
217 begin
218 convProc := ColorspaceCollection.GetConversion(self, ADestColorspace);
219 if convProc = nil then
220 raise exception.Create('Conversion procedure not found');
221 convProc(@ASource, @ADest, ACount, self.GetSize, ADestColorspace.GetSize, AReferenceWhite);
222 end;
223end;
224
225{ ColorspaceCollection }
226
227class function ColorspaceCollection.GetCount: integer;
228begin
229 result := FColorspaceCount;
230end;
231
232class function ColorspaceCollection.GetItem(AIndex: integer): TColorspaceAny;
233begin
234 if (AIndex < 0) or (AIndex >= FColorspaceCount) then
235 raise ERangeError.Create('Index out of bounds');
236 result := FColorspaces[AIndex];
237end;
238
239class function ColorspaceCollection.IndexOf(AColorspace: TColorspaceAny): integer;
240var
241 i: Integer;
242begin
243 for i := 0 to FColorspaceCount-1 do
244 if FColorspaces[i] = AColorspace then exit(i);
245 result := -1;
246end;
247
248class procedure ColorspaceCollection.Add(AColorspace: TColorspaceAny);
249var
250 i: Integer;
251begin
252 for i := 0 to high(FColorspaces) do
253 if FColorspaces[i] = AColorspace then exit;
254
255 if FColorspaceCount >= length(FColorspaces) then
256 setlength(FColorspaces, FColorspaceCount*2+8);
257 FColorspaces[FColorspaceCount] := AColorspace;
258 inc(FColorspaceCount);
259end;
260
261class procedure ColorspaceCollection.AddConversion(ASource: TColorspaceAny;
262 ADest: TColorspaceAny; AConversion: TColorspaceConvertArrayProc);
263var
264 idxSource, idxDest: Integer;
265begin
266 idxSource := IndexOf(ASource);
267 if idxSource = -1 then raise exception.Create('Colorspace not registered');
268 idxDest := IndexOf(ADest);
269 if idxDest = -1 then raise exception.Create('Colorspace not registered');
270 if idxSource >= length(FColorspaceConversions) then
271 setlength(FColorspaceConversions, FColorspaceCount+4);
272 if idxDest >= length(FColorspaceConversions[idxSource]) then
273 setlength(FColorspaceConversions[idxSource], FColorspaceCount+4);
274 FColorspaceConversions[idxSource][idxDest] := AConversion;
275end;
276
277class function ColorspaceCollection.GetConversion(ASource: TColorspaceAny;
278 ADest: TColorspaceAny): TColorspaceConvertArrayProc;
279var
280 idxSource, idxDest: Integer;
281begin
282 idxSource := IndexOf(ASource);
283 if idxSource = -1 then raise exception.Create('Colorspace not registered');
284 idxDest := IndexOf(ADest);
285 if idxDest = -1 then raise exception.Create('Colorspace not registered');
286
287 if (idxSource < length(FColorspaceConversions)) and
288 (idxDest < length(FColorspaceConversions[idxSource])) then
289 result := FColorspaceConversions[idxSource][idxDest]
290 else
291 result := nil;
292end;
293
294var
295 CurrentReferenceWhite: TXYZReferenceWhite;
296 ReferenceWhiteArray: array of TXYZReferenceWhite;
297
298function Clamp(const V, Min, Max: single): single;
299begin
300 Result := V;
301 if Result < Min then
302 Result := Min;
303 if Result > Max then
304 Result := Max;
305end;
306
307function PositiveModSingle(x, cycle: single): single;
308begin
309 if (x < 0) or (x >= cycle) then
310 Result := x - cycle * floor(x / cycle)
311 else
312 result := x;
313end;
314
315procedure PrepareReferenceWhiteArray;
316begin
317 //Source:http://www.easyrgb.com/index.php?X=MATH&H=15#text15
318 //domestic, tungsten-filament lighting
319 AddReferenceWhite(2, 'A', 1.09850, 1.00, 0.35585);
320 AddReferenceWhite(10, 'A', 1.11144, 1.00, 0.35200);
321 //deprecated daylight
322 AddReferenceWhite(2, 'C', 0.98074, 1.00, 1.18232);
323 AddReferenceWhite(10, 'C', 0.97285, 1.00, 1.16145);
324 //daylight
325 AddReferenceWhite(2, 'D50', 0.96422, 1.00, 0.82521);
326 AddReferenceWhite(10, 'D50', 0.96720, 1.00, 0.81427);
327 AddReferenceWhite(2, 'D55', 0.95682, 1.00, 0.92149);
328 AddReferenceWhite(10, 'D55', 0.95799, 1.00, 0.90926);
329 AddReferenceWhite(2, 'D65', 0.95047, 1.00, 1.08883);
330 AddReferenceWhite(10, 'D65', 0.94811, 1.00, 1.07304);
331 AddReferenceWhite(2, 'D75', 0.94972, 1.00, 1.22638);
332 AddReferenceWhite(10, 'D75', 0.94416, 1.00, 1.20641);
333 //fluorescent light
334 AddReferenceWhite(2, 'F2', 0.99187, 1.00, 0.67395);
335 AddReferenceWhite(10, 'F2', 1.03280, 1.00, 0.69026);
336 AddReferenceWhite(2, 'F7', 0.95044, 1.00, 1.08755);
337 AddReferenceWhite(10, 'F7', 0.95792, 1.00, 1.07687);
338 AddReferenceWhite(2, 'F11', 1.00966, 1.00, 0.64370);
339 AddReferenceWhite(10, 'F11', 1.03866, 1.00, 0.65627);
340end;
341
342procedure SetReferenceWhite(AObserverAngle: integer; AIlluminant: string);
343var
344 rp: TXYZReferenceWhite;
345 i: integer;
346begin
347 for i := 0 to Length(ReferenceWhiteArray) - 1 do
348 begin
349 rp := ReferenceWhiteArray[i];
350 if (rp.ObserverAngle = AObserverAngle) and (rp.Illuminant = AIlluminant) then
351 begin
352 CurrentReferenceWhite := rp;
353 Break;
354 end;
355 end;
356end;
357
358procedure SetReferenceWhite(AReferenceWhite: TXYZReferenceWhite);
359begin
360 CurrentReferenceWhite := AReferenceWhite;
361end;
362
363function GetReferenceWhite: TXYZReferenceWhite;
364begin
365 Result := CurrentReferenceWhite;
366end;
367
368procedure AddReferenceWhite(AReferenceWhite: TXYZReferenceWhite);
369begin
370 SetLength(ReferenceWhiteArray, Length(ReferenceWhiteArray) + 1);
371 ReferenceWhiteArray[Length(ReferenceWhiteArray) - 1] := AReferenceWhite;
372end;
373
374procedure AddReferenceWhite(AObserverAngle: integer; AIlluminant: string; AX, AY, AZ: single);
375var
376 rp: TXYZReferenceWhite;
377begin
378 rp.Illuminant := AIlluminant;
379 rp.ObserverAngle := AObserverAngle;
380 rp.X := AX;
381 rp.Y := AY;
382 rp.Z := AZ;
383 AddReferenceWhite(rp);
384end;
385
386function GetReferenceWhiteCount: integer;
387begin
388 result := length(ReferenceWhiteArray);
389end;
390
391function GetReferenceWhiteByIndex(AIndex: integer): TXYZReferenceWhite;
392begin
393 if (AIndex < 0) or (AIndex >= length(ReferenceWhiteArray)) then
394 raise ERangeError.Create('Index out of bounds');
395 result := ReferenceWhiteArray[AIndex];
396end;
397
398{$DEFINE INCLUDE_IMPLEMENTATION}
399{$I generatedcolorspace.inc}
400
401function StdRGBAToBGRAPixel(const AStdRGBA: TStdRGBA): TBGRAPixel;
402begin
403 with AStdRGBA do
404 begin
405 result.red := round(Clamp(red * 255, 0, 255));
406 result.green := round(Clamp(green * 255, 0, 255));
407 result.blue := round(Clamp(blue * 255, 0, 255));
408 result.alpha := round(Clamp(alpha * 255, 0, 255));
409 end;
410end;
411
412function BGRAPixelToStdRGBA(const ABGRAPixel: TBGRAPixel): TStdRGBA;
413const oneOver255 = 1/255;
414begin
415 with ABGRAPixel do
416 begin
417 result.red := red * oneOver255;
418 result.green := green * oneOver255;
419 result.blue := blue * oneOver255;
420 result.alpha := alpha * oneOver255;
421 end;
422end;
423
424function LinearRGBAToExpandedPixel(const ALinearRGBA: TLinearRGBA): TExpandedPixel;
425begin
426 with ALinearRGBA do
427 begin
428 result.red := round(Clamp(red * 65535, 0, 65535));
429 result.green := round(Clamp(green * 65535, 0, 65535));
430 result.blue := round(Clamp(blue * 65535, 0, 65535));
431 result.alpha := round(Clamp(alpha * 65535, 0, 65535));
432 end;
433end;
434
435function ExpandedPixelToLinearRGBA(const AExpandedPixel: TExpandedPixel): TLinearRGBA;
436begin
437 with AExpandedPixel do
438 begin
439 result.red := red / 65535;
440 result.green := green / 65535;
441 result.blue := blue / 65535;
442 result.alpha := alpha / 65535;
443 end;
444end;
445
446function LinearRGBAToXYZA(const ALinearRGBA: TLinearRGBA): TXYZA;
447begin
448 with ALinearRGBA do
449 begin
450 // Observer= 2°, Illuminant= D65
451 result.X := red * 0.4124 + green * 0.3576 + blue * 0.1805;
452 result.Y := red * 0.2126 + green * 0.7152 + blue * 0.0722;
453 result.Z := red * 0.0193 + green * 0.1192 + blue * 0.9505;
454 end;
455 Result.alpha := ALinearRGBA.alpha;
456end;
457
458function XYZAToLinearRGBA(const AXYZA: TXYZA): TLinearRGBA;
459begin
460 with AXYZA do
461 begin
462 result.red := Clamp(X * 3.2406 + Y * (-1.5372) + Z * (-0.49), 0, 1);
463 result.green := Clamp(X * (-0.969) + Y * 1.8758 + Z * 0.0415, 0, 1);
464 result.blue := Clamp(X * 0.0557 + Y * (-0.2040) + Z * 1.0570, 0, 1);
465 end;
466 Result.alpha := AXYZA.alpha;
467end;
468
469function XYZAToLabA(const AXYZA: TXYZA): TLabA;
470begin
471 Result := XYZAToLabA(AXYZA, CurrentReferenceWhite);
472end;
473
474function XYZAToLabA(const AXYZA: TXYZA; const AReferenceWhite: TXYZReferenceWhite): TLabA;
475var
476 xp, yp, zp: double;
477begin
478 xp := AXYZA.X / AReferenceWhite.X;
479 yp := AXYZA.Y / AReferenceWhite.Y;
480 zp := AXYZA.Z / AReferenceWhite.Z;
481 if xp > 0.008856 then
482 xp := Power(xp, 1 / 3)
483 else
484 xp := (7.787 * xp) + 0.138;
485 if yp > 0.008856 then
486 yp := Power(yp, 1 / 3)
487 else
488 yp := (7.787 * yp) + 0.138;
489 if zp > 0.008856 then
490 zp := Power(zp, 1 / 3)
491 else
492 zp := (7.787 * zp) + 0.138;
493
494 result.L := Clamp((116 * yp) - 16, 0, 100);
495 result.a := 500 * (xp - yp);
496 result.b := 200 * (yp - zp);
497 Result.Alpha := AXYZA.alpha;
498end;
499
500function LabAToXYZA(const ALabA: TLabA): TXYZA;
501begin
502 Result := LabAToXYZA(ALabA, CurrentReferenceWhite);
503end;
504
505function LabAToXYZA(const ALabA: TLabA; const AReferenceWhite: TXYZReferenceWhite): TXYZA;
506var
507 xp, yp, zp: double;
508begin
509 yp := (ALabA.L + 16) / 116;
510 xp := ALabA.a / 500 + yp;
511 zp := yp - ALabA.b / 200;
512 if yp > 0.2069 then
513 yp := IntPower(yp, 3)
514 else
515 yp := (yp - 0.138) / 7.787;
516 if xp > 0.2069 then
517 xp := IntPower(xp, 3)
518 else
519 xp := (xp - 0.138) / 7.787;
520 if zp > 0.2069 then
521 zp := IntPower(zp, 3)
522 else
523 zp := (zp - 0.138) / 7.787;
524 Result.X := AReferenceWhite.X * xp;
525 Result.Y := AReferenceWhite.Y * yp;
526 Result.Z := AReferenceWhite.Z * zp;
527 Result.alpha := ALabA.Alpha;
528end;
529
530function StdRGBAToStdHSVA(const AStdRGBA: TStdRGBA): TStdHSVA;
531var
532 Delta, mini: single;
533begin
534 with AStdRGBA do
535 begin
536 result.value := max(max(red, green), blue);
537 mini := min(min(red, green), blue);
538 Delta := result.value - mini;
539
540 if result.value = 0.0 then
541 result.saturation := 0
542 else
543 result.saturation := Delta / result.value;
544
545 if result.saturation = 0.0 then
546 result.hue := 0
547 else
548 begin
549 if red = result.value then
550 result.hue := 60.0 * (green - blue) / Delta
551 else
552 if green = result.value then
553 result.hue := 120.0 + 60.0 * (blue - red) / Delta
554 else
555 {if blue = result.value then}
556 result.hue := 240.0 + 60.0 * (red - green) / Delta;
557
558 if result.hue < 0.0 then
559 result.hue += 360.0;
560 end;
561 result.alpha := alpha;
562 end;
563end;
564
565function StdHSVAToStdRGBA(const AStdHSVA: TStdHSVA): TStdRGBA;
566var
567 C, X, M, rp, gp, bp, sp, vp: single;
568 h360: single;
569begin
570 vp := AStdHSVA.value;
571 sp := AStdHSVA.saturation;
572 C := Vp * sp;
573 h360 := PositiveModSingle(AStdHSVA.hue, 360);
574 X := C * (1 - abs(PositiveModSingle(h360 / 60, 2) - 1));
575 m := vp - c;
576 rp := 0;
577 gp := 0;
578 bp := 0;
579 case floor(h360) of
580 -1..59:
581 begin
582 rp := C;
583 gp := X;
584 bp := 0;
585 end;
586 60..119:
587 begin
588 rp := X;
589 gp := C;
590 bp := 0;
591 end;
592 120..179:
593 begin
594 rp := 0;
595 gp := C;
596 bp := X;
597 end;
598 180..239:
599 begin
600 rp := 0;
601 gp := X;
602 bp := C;
603 end;
604 240..299:
605 begin
606 rp := X;
607 gp := 0;
608 bp := C;
609 end;
610 300..359:
611 begin
612 rp := C;
613 gp := 0;
614 bp := X;
615 end;
616 end;
617 result.red := rp + m;
618 result.green := gp + m;
619 result.blue := bp + m;
620 result.alpha := AStdHSVA.alpha;
621end;
622
623function StdHSLAToStdHSVA(const AStdHSLA: TStdHSLA): TStdHSVA;
624var
625 s, l, v: single;
626begin
627 Result.hue := AStdHSLA.hue;
628 s := AStdHSLA.saturation;
629 l := AStdHSLA.lightness;
630 v := (2 * l + s * (1 - abs(2 * l - 1))) / 2;
631 if v <> 0 then
632 Result.saturation := 2 * (v - l) / v
633 else
634 Result.saturation := 0;
635 Result.value := v;
636end;
637
638function StdHSVAToStdHSLA(const AStdHSVA: TStdHSVA): TStdHSLA;
639var
640 s, v, l: single;
641begin
642 Result.hue := AStdHSVA.hue;
643 s := AStdHSVA.saturation;
644 v := AStdHSVA.value;
645 l := 0.5 * v * (2 - s);
646 if l <> 0 then
647 Result.saturation := v * s / (1 - abs(2 * l - 1))
648 else
649 Result.saturation := 0;
650 Result.lightness := l;
651end;
652
653function StdRGBAToStdCMYK(const AStdRGBA: TStdRGBA): TStdCMYK;
654begin
655 with AStdRGBA do
656 begin
657 result.K := 1 - max(max(red, green), blue);
658 if result.K >= 1 then
659 begin
660 result.C := 0;
661 result.M := 0;
662 result.Y := 0;
663 end
664 else
665 begin
666 result.C := 1 - red / (1 - result.K);
667 result.M := 1 - green / (1 - result.K);
668 result.Y := 1 - blue / (1 - result.K);
669 end;
670 end;
671end;
672
673function StdCMYKToStdRGBA(const AStdCMYK: TStdCMYK; AAlpha: Single = 1): TStdRGBA;
674begin
675 with AStdCMYK do
676 begin
677 result.red := (1 - C) * (1 - K);
678 result.green := (1 - M) * (1 - K);
679 result.blue := (1 - Y) * (1 - K);
680 result.alpha := AAlpha;
681 end;
682end;
683
684function LabAToLChA(const ALabA: TLabA): TLChA;
685var
686 a, b, HRad: single;
687begin
688 a := ALabA.a;
689 b := ALabA.b;
690 HRad := ArcTan2(b, a);
691 if HRad >= 0 then
692 result.H := (HRad / PI) * 180
693 else
694 result.H := 360 - (ABS(HRad) / PI) * 180;
695 result.L := ALabA.L;
696 result.C := SQRT(a*a + b*b);
697 result.alpha := ALabA.Alpha;
698end;
699
700function LChAToLabA(const ALChA: TLChA): TLabA;
701begin
702 result.L := ALChA.L;
703 result.a := cos(DegToRad(ALChA.h)) * ALChA.C;
704 result.b := sin(DegToRad(ALChA.h)) * ALChA.C;
705 result.Alpha:= ALChA.alpha;
706end;
707
708function AdobeRGBAToXYZA(const ASource: TAdobeRGBA): TXYZA;
709var R,G,B: single;
710begin
711 R := GammaExpansionTab[ASource.red]/65535;
712 G := GammaExpansionTab[ASource.green]/65535;
713 B := GammaExpansionTab[ASource.blue]/65535;
714 result.X := R*0.57667 + G*0.18556 + B*0.18823;
715 result.Y := R*0.29735 + G*0.62736 + B*0.07529;
716 result.Z := R*0.02703 + G*0.07069 + B*0.99133;
717 result.alpha := ASource.alpha/255;
718end;
719
720function XYZAToAdobeRGBA(const AXYZA: TXYZA): TAdobeRGBA;
721var R,G,B: single;
722begin
723 with AXYZA do
724 begin
725 R := Clamp(2.04159*X - 0.56501*Y - 0.34473*Z,0,1);
726 G := Clamp(-0.96924*X + 1.87597*Y + 0.04156*Z,0,1);
727 B := Clamp(0.01344*X - 0.11836*Y + 1.01518*Z,0,1);
728 end;
729 result.red := GammaCompressionTab[round(R*65535)];
730 result.green := GammaCompressionTab[round(G*65535)];
731 result.blue := GammaCompressionTab[round(B*65535)];
732 result.alpha := round(Clamp(AXYZA.alpha,0,1)*255);
733end;
734
735function StdRGBAToLinearRGBA(const AStdRGBA: TStdRGBA): TLinearRGBA;
736
737 function GammaExpansionF(ACompressed: single): single;
738 const oneOver65535 : single = 1/65535;
739 var
740 intPart: Integer;
741 fracPart: Single;
742 begin
743 if ACompressed <= 0 then
744 result := 0
745 else if ACompressed >= 1 then
746 result := 1
747 else
748 begin
749 ACompressed *= 255;
750 intPart := trunc(ACompressed);
751 fracPart := ACompressed - intPart;
752 if fracPart = 0 then
753 result := GammaExpansionTab[intPart]*oneOver65535
754 else
755 result := (GammaExpansionTab[intPart]*(1-fracPart)+GammaExpansionTab[intPart+1]*fracPart)*oneOver65535;
756 end;
757 end;
758
759begin
760 result.red := GammaExpansionF(AStdRGBA.red);
761 result.green := GammaExpansionF(AStdRGBA.green);
762 result.blue := GammaExpansionF(AStdRGBA.blue);
763 result.alpha := AStdRGBA.alpha;
764end;
765
766function LinearRGBAToStdRGBA(const ALinearRGBA: TLinearRGBA): TStdRGBA;
767
768 function GammaCompressionF(AExpanded: single): single;
769 const oneOver255 : single = 1/255;
770 oneOver256: single = 1/256;
771 var
772 intPart: Integer;
773 fracPart: Single;
774 begin
775 if AExpanded <= 0 then
776 result := 0
777 else if AExpanded >= 1 then
778 result := 1
779 else
780 begin
781 AExpanded *= 65535;
782 intPart := trunc(AExpanded);
783 fracPart := AExpanded - intPart;
784 if fracPart = 0 then
785 result := (GammaCompressionTab[intPart] + GammaCompressionTabFrac[intPart]*oneOver256)*oneOver255
786 else
787 result := ( (GammaCompressionTab[intPart]+GammaCompressionTabFrac[intPart]*oneOver256) *(1-fracPart)+
788 (GammaCompressionTab[intPart+1]+GammaCompressionTabFrac[intPart+1]*oneOver256) * fracPart )*oneOver255;
789 end;
790 end;
791
792begin
793 result.red := GammaCompressionF(ALinearRGBA.red);
794 result.green := GammaCompressionF(ALinearRGBA.green);
795 result.blue := GammaCompressionF(ALinearRGBA.blue);
796 result.alpha := ALinearRGBA.alpha;
797end;
798
799function StdRGBAToStdHSLA(const AStdRGBA: TStdRGBA): TStdHSLA;
800var
801 d, cmax, cmin: double;
802begin
803 with AStdRGBA do
804 begin
805 cmax := Max(red, Max(green, blue));
806 cmin := Min(red, Min(green, blue));
807 result.lightness := (cmax + cmin) / 2;
808
809 if cmax = cmin then
810 begin
811 result.hue := 0;
812 result.saturation := 0;
813 end
814 else
815 begin
816 d := cmax - cmin;
817 if result.lightness < 0.5 then
818 result.saturation := d / (cmax + cmin)
819 else
820 result.saturation := d / (2 - cmax - cmin);
821
822 if red = cmax then
823 result.hue := (green - blue) / d
824 else
825 if green = cmax then
826 result.hue := 2 + (blue - red) / d
827 else
828 result.hue := 4 + (red - green) / d;
829 if result.hue < 0 then result.hue += 6;
830 result.hue *= 60;
831 end;
832 result.alpha := alpha;
833 end;
834end;
835
836function StdHSLAToStdRGBA(const AStdHSLA: TStdHSLA): TStdRGBA;
837var
838 C, X, M, rp, gp, bp, sp, lp, h360: single;
839begin
840 lp := AStdHSLA.lightness;
841 sp := AStdHSLA.saturation;
842 C := (1 - abs(2 * Lp - 1)) * Sp;
843 h360 := PositiveModSingle(AStdHSLA.hue, 360);
844 X := C * (1 - abs(PositiveModSingle(h360 / 60, 2) - 1));
845 m := Lp - C / 2;
846 rp := 0;
847 gp := 0;
848 bp := 0;
849 case floor(h360) of
850 -1..59:
851 begin
852 rp := C;
853 gp := X;
854 bp := 0;
855 end;
856 60..119:
857 begin
858 rp := X;
859 gp := C;
860 bp := 0;
861 end;
862 120..179:
863 begin
864 rp := 0;
865 gp := C;
866 bp := X;
867 end;
868 180..239:
869 begin
870 rp := 0;
871 gp := X;
872 bp := C;
873 end;
874 240..299:
875 begin
876 rp := X;
877 gp := 0;
878 bp := C;
879 end;
880 300..359:
881 begin
882 rp := C;
883 gp := 0;
884 bp := X;
885 end;
886 end;
887 result.red := rp + m;
888 result.green := gp + m;
889 result.blue := bp + m;
890 result.alpha := AStdHSLA.alpha;
891end;
892
893{$ENDIF}
894
895{$IFDEF INCLUDE_INITIALIZATION}
896{$UNDEF INCLUDE_INITIALIZATION}
897
898 PrepareReferenceWhiteArray;
899 SetReferenceWhite(2, 'D65');
900
901 {$DEFINE INCLUDE_INITIALIZATION}
902 {$I generatedcolorspace.inc}
903
904{$ENDIF}
Note: See TracBrowser for help on using the repository browser.