1 | unit BGRAColorQuantization;
|
---|
2 |
|
---|
3 | {$mode objfpc}{$H+}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, BGRAPalette, BGRABitmapTypes;
|
---|
9 |
|
---|
10 | type
|
---|
11 | TBGRAColorBox = class;
|
---|
12 | TBGRAColorTree = class;
|
---|
13 | TBGRAApproxPalette = class;
|
---|
14 | TBiggestLeafMethod = (blMix, blApparentInterval, blWeight);
|
---|
15 |
|
---|
16 | { TDimensionMinMax }
|
---|
17 |
|
---|
18 | TDimensionMinMax = object
|
---|
19 | Minimum: UInt32;
|
---|
20 | Maximum: UInt32;
|
---|
21 | function Size: UInt32;
|
---|
22 | function Contains(AValue: UInt32): boolean;
|
---|
23 | function PointLike: boolean;
|
---|
24 | procedure SetAsPoint(AValue: UInt32);
|
---|
25 | function GetCenter: UInt32;
|
---|
26 | procedure GrowToInclude(AValue: UInt32);
|
---|
27 | end;
|
---|
28 |
|
---|
29 | TColorDimension = (cdFast,cdRed,cdGreen,cdBlue,cdAlpha,cdRGB,cdRG,cdGB,cdRB,cdRInvG,cdGInvB,cdRInvB,cdRInvGB,cdGInvRB,cdBInvRG,
|
---|
30 | cdSaturation);
|
---|
31 | TColorDimensions = set of TColorDimension;
|
---|
32 |
|
---|
33 | { TBGRAColorQuantizer }
|
---|
34 |
|
---|
35 | TBGRAColorQuantizer = class(TBGRACustomColorQuantizer)
|
---|
36 | private
|
---|
37 | FColors: ArrayOfWeightedColor;
|
---|
38 | FPalette: TBGRAApproxPalette;
|
---|
39 | FReductionColorCount: Integer;
|
---|
40 | FReductionKeepContrast: boolean;
|
---|
41 | FSeparateAlphaChannel: boolean;
|
---|
42 | procedure Init(ABox: TBGRAColorBox);
|
---|
43 | procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; ARedBounds, AGreenBounds, ABlueBounds, AAlphaBounds: TDimensionMinMax; AUniform: boolean); overload;
|
---|
44 | procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; AColorBounds, AAlphaBounds: TDimensionMinMax); overload;
|
---|
45 | protected
|
---|
46 | function GetPalette: TBGRACustomApproxPalette; override;
|
---|
47 | function GetSourceColor(AIndex: integer): TBGRAPixel; override;
|
---|
48 | function GetSourceColorCount: Integer; override;
|
---|
49 | function GetReductionColorCount: integer; override;
|
---|
50 | procedure SetReductionColorCount(AValue: Integer); override;
|
---|
51 | public
|
---|
52 | constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); override;
|
---|
53 | constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); override;
|
---|
54 | constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); override;
|
---|
55 | constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); override;
|
---|
56 | destructor Destroy; override;
|
---|
57 | procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); override;
|
---|
58 | function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; overload; override;
|
---|
59 | function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm;
|
---|
60 | ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; overload; override;
|
---|
61 | procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm;
|
---|
62 | ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); override;
|
---|
63 | end;
|
---|
64 |
|
---|
65 | { TBGRAApproxPalette }
|
---|
66 |
|
---|
67 | TBGRAApproxPalette = class(TBGRACustomApproxPalette)
|
---|
68 | private
|
---|
69 | FTree: TBGRAColorTree;
|
---|
70 | FColors: ArrayOfWeightedColor;
|
---|
71 | protected
|
---|
72 | function GetCount: integer; override;
|
---|
73 | function GetColorByIndex(AIndex: integer): TBGRAPixel; override;
|
---|
74 | function GetWeightByIndex(AIndex: Integer): UInt32; override;
|
---|
75 | procedure Init(const AColors: ArrayOfTBGRAPixel);
|
---|
76 | public
|
---|
77 | constructor Create(const AColors: ArrayOfTBGRAPixel); overload;
|
---|
78 | constructor Create(const AColors: ArrayOfWeightedColor); overload;
|
---|
79 | constructor Create(AOwnedSplitTree: TBGRAColorTree); overload;
|
---|
80 | destructor Destroy; override;
|
---|
81 | function ContainsColor(AValue: TBGRAPixel): boolean; override;
|
---|
82 | function IndexOfColor(AValue: TBGRAPixel): integer; override;
|
---|
83 | function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override;
|
---|
84 | function FindNearestColorIndex(AValue: TBGRAPixel): integer; override;
|
---|
85 | function GetAsArrayOfColor: ArrayOfTBGRAPixel; override;
|
---|
86 | function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
|
---|
87 | end;
|
---|
88 |
|
---|
89 | { TBGRAApproxPaletteViaLargerPalette }
|
---|
90 |
|
---|
91 | TBGRAApproxPaletteViaLargerPalette = class(TBGRAApproxPalette)
|
---|
92 | private
|
---|
93 | FLarger: TBGRACustomApproxPalette;
|
---|
94 | FLargerColors: array of record
|
---|
95 | approxColor: TBGRAPixel;
|
---|
96 | approxColorIndex: integer;
|
---|
97 | end;
|
---|
98 | FLargerOwned: boolean;
|
---|
99 | FTransparentColorIndex: integer;
|
---|
100 | protected
|
---|
101 | function FindNearestLargerColorIndex(AValue: TBGRAPixel): integer; virtual;
|
---|
102 | function SlowFindNearestColorIndex(AValue: TBGRAPixel): integer;
|
---|
103 | public
|
---|
104 | constructor Create(const AColors: ArrayOfTBGRAPixel; ALarger: TBGRACustomApproxPalette; ALargerOwned: boolean);
|
---|
105 | destructor Destroy; override;
|
---|
106 | function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override;
|
---|
107 | function FindNearestColorIndex(AValue: TBGRAPixel): integer; override;
|
---|
108 | function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
|
---|
109 | end;
|
---|
110 |
|
---|
111 | TIsChannelStrictlyGreaterFunc = TBGRAPixelComparer;
|
---|
112 | TIsChannelGreaterThanOrEqualToValueFunc = function (p : PBGRAPixel; v: UInt32): boolean;
|
---|
113 |
|
---|
114 | TColorBoxBounds = array[TColorDimension] of TDimensionMinMax;
|
---|
115 |
|
---|
116 | { TBGRAColorBox }
|
---|
117 |
|
---|
118 | TBGRAColorBox = class
|
---|
119 | private
|
---|
120 | FBounds: TColorBoxBounds;
|
---|
121 | FTotalWeight: UInt32;
|
---|
122 | FColors: ArrayOfWeightedColor;
|
---|
123 | FDimensions: TColorDimensions;
|
---|
124 | FPureTransparentColorCount: integer;
|
---|
125 | function GetApparentInterval(ADimension: TColorDimension): UInt32;
|
---|
126 | function GetAverageColor: TBGRAPixel;
|
---|
127 | function GetAverageColorOrMainColor: TBGRAPixel;
|
---|
128 | function GetBounds(ADimension: TColorDimension): TDimensionMinMax;
|
---|
129 | function GetColorCount(ACountPureTransparent: boolean): integer;
|
---|
130 | function GetHasPureTransparentColor: boolean;
|
---|
131 | function GetInferiorColor: TBGRAPixel;
|
---|
132 | function GetLargestApparentDimension: TColorDimension;
|
---|
133 | function GetLargestApparentInterval: UInt32;
|
---|
134 | function GetPointLike: boolean;
|
---|
135 | function GetSuperiorColor: TBGRAPixel;
|
---|
136 | procedure Init(AColors: ArrayOfWeightedColor; AOwner: boolean);
|
---|
137 | procedure SortBy(ADimension: TColorDimension);
|
---|
138 | function GetMedianIndex(ADimension : TColorDimension; AMinValue, AMaxValue: UInt32): integer;
|
---|
139 | public
|
---|
140 | constructor Create(ADimensions: TColorDimensions; AColors: ArrayOfWeightedColor; AOwner: boolean); overload;
|
---|
141 | constructor Create(ADimensions: TColorDimensions; const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette); overload;
|
---|
142 | constructor Create(ADimensions: TColorDimensions; ABounds: TColorBoxBounds); overload;
|
---|
143 | constructor Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette); overload;
|
---|
144 | constructor Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload;
|
---|
145 | constructor Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption); overload;
|
---|
146 | function BoundsContain(AColor: TBGRAPixel): boolean;
|
---|
147 | function MedianCut(ADimension: TColorDimension; out SuperiorMiddle: UInt32): TBGRAColorBox;
|
---|
148 | function Duplicate : TBGRAColorBox;
|
---|
149 | property Bounds[ADimension: TColorDimension]: TDimensionMinMax read GetBounds;
|
---|
150 | property ApparentInterval[AChannel: TColorDimension]: UInt32 read GetApparentInterval;
|
---|
151 | property LargestApparentDimension: TColorDimension read GetLargestApparentDimension;
|
---|
152 | property LargestApparentInterval: UInt32 read GetLargestApparentInterval;
|
---|
153 | property PointLike: boolean read GetPointLike;
|
---|
154 | property AverageColor: TBGRAPixel read GetAverageColor;
|
---|
155 | property SuperiorColor: TBGRAPixel read GetSuperiorColor;
|
---|
156 | property InferiorColor: TBGRAPixel read GetInferiorColor;
|
---|
157 | property AverageColorOrMainColor: TBGRAPixel read GetAverageColorOrMainColor;
|
---|
158 | function GetAsArrayOfColors(AIncludePureTransparent: boolean): ArrayOfTBGRAPixel;
|
---|
159 | property TotalWeight: UInt32 read FTotalWeight;
|
---|
160 | property ColorCount[ACountPureTransparent: boolean]: integer read GetColorCount;
|
---|
161 | property HasPureTransparentColor: boolean read GetHasPureTransparentColor;
|
---|
162 | property PureTransparentColorCount: integer read FPureTransparentColorCount;
|
---|
163 | end;
|
---|
164 |
|
---|
165 | TBGRALeafColorMode = (lcAverage, lcCenter, lcExtremum, lcMix);
|
---|
166 |
|
---|
167 | { TBGRAColorTree }
|
---|
168 |
|
---|
169 | TBGRAColorTree = class
|
---|
170 | private
|
---|
171 | FLeaf: TBGRAColorBox;
|
---|
172 | FIsLeaf: boolean;
|
---|
173 | FLargestApparentInterval: integer;
|
---|
174 | FWeight: UInt32;
|
---|
175 |
|
---|
176 | FLeafColor: TBGRAPixel;
|
---|
177 | FLeafColorIndex: integer;
|
---|
178 | FLeafColorComputed: boolean;
|
---|
179 | FMinBorder, FMaxBorder: array[TColorDimension] of boolean;
|
---|
180 | FCenterColor: TBGRAPixel;
|
---|
181 | FAverageColor: TBGRAPixel;
|
---|
182 |
|
---|
183 | FPureTransparentColorCount: integer;
|
---|
184 | FPureTransparentColorIndex: integer;
|
---|
185 | FDimension: TColorDimension;
|
---|
186 | FPixelValueComparer: TIsChannelGreaterThanOrEqualToValueFunc;
|
---|
187 | FSuperiorMiddle: UInt32;
|
---|
188 | FInferiorBranch, FSuperiorBranch: TBGRAColorTree;
|
---|
189 | function GetApproximatedColorCount: integer;
|
---|
190 | function GetHasPureTransparentColor: boolean;
|
---|
191 | function GetLeafCount: integer;
|
---|
192 | procedure Init(ALeaf: TBGRAColorBox; AOwned: boolean);
|
---|
193 | procedure InternalComputeLeavesColor(ALeafColor: TBGRALeafColorMode; var AStartIndex: integer);
|
---|
194 | procedure CheckColorComputed;
|
---|
195 | public
|
---|
196 | constructor Create(ABox: TBGRAColorBox; AOwned: boolean); overload;
|
---|
197 | constructor Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette); overload;
|
---|
198 | constructor Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload;
|
---|
199 | destructor Destroy; override;
|
---|
200 | procedure FreeLeaves;
|
---|
201 | function FindBiggestLeaf(AMethod: TBiggestLeafMethod): TBGRAColorTree;
|
---|
202 | property LargestApparentInterval: integer read FLargestApparentInterval;
|
---|
203 | property Weight: UInt32 read FWeight;
|
---|
204 | property IsLeaf: boolean read FIsLeaf;
|
---|
205 | function TrySplitLeaf: boolean;
|
---|
206 | procedure ComputeLeavesColor(ALeafColor: TBGRALeafColorMode);
|
---|
207 | function ApproximateColor(AColor: TBGRAPixel): TBGRAPixel;
|
---|
208 | function ApproximateColorIndex(AColor: TBGRAPixel): integer;
|
---|
209 | function GetAsArrayOfApproximatedColors: ArrayOfTBGRAPixel;
|
---|
210 | function GetAsArrayOfWeightedColors: ArrayOfWeightedColor;
|
---|
211 | procedure SplitIntoPalette(ACount: integer; AMethod: TBiggestLeafMethod;
|
---|
212 | ALeafColor: TBGRALeafColorMode);
|
---|
213 | function SplitIntoPaletteWithSubPalette(ACount: integer; AMethod: TBiggestLeafMethod;
|
---|
214 | ALeafColor: TBGRALeafColorMode; ASubPaletteCount: integer): ArrayOfTBGRAPixel;
|
---|
215 | property LeafCount: integer read GetLeafCount;
|
---|
216 | property ApproximatedColorCount: integer read GetApproximatedColorCount;
|
---|
217 | property HasPureTransparentColor: boolean read GetHasPureTransparentColor;
|
---|
218 | property PureTransparentColorCount: integer read FPureTransparentColorCount;
|
---|
219 | end;
|
---|
220 |
|
---|
221 | function GetPixelStrictComparer(ADimension: TColorDimension): TIsChannelStrictlyGreaterFunc;
|
---|
222 | function GetPixelValueComparer(ADimension: TColorDimension): TIsChannelGreaterThanOrEqualToValueFunc;
|
---|
223 | function BGRAColorCount(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption): integer;
|
---|
224 |
|
---|
225 | const AllColorDimensions = [cdRed,cdGreen,cdBlue,cdAlpha,cdRGB,cdRG,cdGB,cdRB,cdRInvG,cdGInvB,cdRInvB,cdRInvGB,cdGInvRB,cdBInvRG,
|
---|
226 | cdSaturation];
|
---|
227 |
|
---|
228 | implementation
|
---|
229 |
|
---|
230 | uses BGRADithering, FPimage, FPWriteBMP, BGRAWritePNG, math;
|
---|
231 |
|
---|
232 | const MedianMinPercentage = 0.2;
|
---|
233 |
|
---|
234 | const RedShift = 1;
|
---|
235 | GreenShift = 2;
|
---|
236 | AlphaShift = 1;
|
---|
237 | SaturationShift = 2;
|
---|
238 |
|
---|
239 | function GetDimensionValue(APixel: TBGRAPixel; ADimension: TColorDimension): UInt32;
|
---|
240 | var v: UInt32;
|
---|
241 | begin
|
---|
242 | case ADimension of
|
---|
243 | cdFast: result := DWord(APixel);
|
---|
244 | cdRed: result := GammaExpansionTab[APixel.red] shl RedShift;
|
---|
245 | cdGreen: result := GammaExpansionTab[APixel.green] shl GreenShift;
|
---|
246 | cdBlue: result := GammaExpansionTab[APixel.blue];
|
---|
247 | cdAlpha: result := (APixel.alpha + (APixel.alpha shl 8)) shl AlphaShift;
|
---|
248 | cdRGB: result := GammaExpansionTab[APixel.blue] + (GammaExpansionTab[APixel.red] shl RedShift) + (GammaExpansionTab[APixel.green] shl GreenShift);
|
---|
249 | cdRG: result := (GammaExpansionTab[APixel.red] shl RedShift) + (GammaExpansionTab[APixel.green] shl GreenShift);
|
---|
250 | cdGB: result := GammaExpansionTab[APixel.blue] + (GammaExpansionTab[APixel.green] shl GreenShift);
|
---|
251 | cdRB: result := (GammaExpansionTab[APixel.red] shl RedShift) + GammaExpansionTab[APixel.blue];
|
---|
252 | cdRInvG: result := (GammaExpansionTab[APixel.red] shl RedShift) + ((not GammaExpansionTab[APixel.green]) shl GreenShift);
|
---|
253 | cdGInvB: result := (GammaExpansionTab[APixel.green] shl GreenShift) + (not GammaExpansionTab[APixel.blue]);
|
---|
254 | cdRInvB: result := (GammaExpansionTab[APixel.red] shl RedShift) + (not GammaExpansionTab[APixel.blue]);
|
---|
255 | cdRInvGB: result := (GammaExpansionTab[APixel.red] shl RedShift) + ((not GammaExpansionTab[APixel.green]) shl GreenShift) + (not GammaExpansionTab[APixel.blue]);
|
---|
256 | cdGInvRB: result := (GammaExpansionTab[APixel.green] shl GreenShift) + ((not GammaExpansionTab[APixel.red]) shl RedShift) + (not GammaExpansionTab[APixel.blue]);
|
---|
257 | cdBInvRG: result := (GammaExpansionTab[APixel.blue]) + ((not GammaExpansionTab[APixel.red]) shl RedShift) + ((not GammaExpansionTab[APixel.green]) shl GreenShift);
|
---|
258 | cdSaturation: with GammaExpansion(APixel) do
|
---|
259 | begin
|
---|
260 | v := red;
|
---|
261 | if green>v then v := green;
|
---|
262 | if blue>v then v := blue;
|
---|
263 | result := v;
|
---|
264 | v := red;
|
---|
265 | if green<v then v := green;
|
---|
266 | if blue<v then v := blue;
|
---|
267 | result -= v;
|
---|
268 | result := result shl SaturationShift;
|
---|
269 | end
|
---|
270 | else raise exception.Create('Unknown dimension');
|
---|
271 | end;
|
---|
272 | end;
|
---|
273 |
|
---|
274 | function IsRGBGreater(p1, p2: PBGRAPixel): boolean;
|
---|
275 | begin
|
---|
276 | result := ((GammaExpansionTab[p1^.red] shl RedShift)+(GammaExpansionTab[p1^.green] shl GreenShift)+GammaExpansionTab[p1^.blue]) >
|
---|
277 | ((GammaExpansionTab[p2^.red] shl RedShift)+(GammaExpansionTab[p2^.green] shl GreenShift)+GammaExpansionTab[p2^.blue]);
|
---|
278 | end;
|
---|
279 |
|
---|
280 | function IsRGBGreaterThanValue(p: PBGRAPixel;
|
---|
281 | v: UInt32): boolean;
|
---|
282 | begin
|
---|
283 | with p^ do
|
---|
284 | result := ((GammaExpansionTab[red] shl RedShift)+(GammaExpansionTab[green] shl GreenShift)+GammaExpansionTab[blue]) >= v;
|
---|
285 | end;
|
---|
286 |
|
---|
287 | function IsRGGreater(p1, p2: PBGRAPixel): boolean;
|
---|
288 | begin
|
---|
289 | result := ((GammaExpansionTab[p1^.red] shl RedShift)+(GammaExpansionTab[p1^.green] shl GreenShift)) >
|
---|
290 | ((GammaExpansionTab[p2^.red] shl RedShift)+(GammaExpansionTab[p2^.green] shl GreenShift));
|
---|
291 | end;
|
---|
292 |
|
---|
293 | function IsRGGreaterThanValue(p: PBGRAPixel;
|
---|
294 | v: UInt32): boolean;
|
---|
295 | begin
|
---|
296 | with p^ do
|
---|
297 | result := ((GammaExpansionTab[red] shl RedShift)+(GammaExpansionTab[green] shl GreenShift)) >= v;
|
---|
298 | end;
|
---|
299 |
|
---|
300 | function IsGBGreater(p1, p2: PBGRAPixel): boolean;
|
---|
301 | begin
|
---|
302 | result := ((GammaExpansionTab[p1^.green] shl GreenShift)+GammaExpansionTab[p1^.blue]) >
|
---|
303 | ((GammaExpansionTab[p2^.green] shl GreenShift)+GammaExpansionTab[p2^.blue]);
|
---|
304 | end;
|
---|
305 |
|
---|
306 | function IsGBGreaterThanValue(p: PBGRAPixel;
|
---|
307 | v: UInt32): boolean;
|
---|
308 | begin
|
---|
309 | with p^ do
|
---|
310 | result := ((GammaExpansionTab[green] shl GreenShift)+GammaExpansionTab[blue]) >= v;
|
---|
311 | end;
|
---|
312 |
|
---|
313 | function IsRBGreater(p1, p2: PBGRAPixel): boolean;
|
---|
314 | begin
|
---|
315 | result := ((GammaExpansionTab[p1^.red] shl RedShift)+GammaExpansionTab[p1^.blue]) >
|
---|
316 | ((GammaExpansionTab[p2^.red] shl RedShift)+GammaExpansionTab[p2^.blue]);
|
---|
317 | end;
|
---|
318 |
|
---|
319 | function IsRBGreaterThanValue(p: PBGRAPixel;
|
---|
320 | v: UInt32): boolean;
|
---|
321 | begin
|
---|
322 | with p^ do
|
---|
323 | result := ((GammaExpansionTab[red] shl RedShift)+GammaExpansionTab[blue]) >= v;
|
---|
324 | end;
|
---|
325 |
|
---|
326 | function IsRInvGGreater(p1, p2: PBGRAPixel
|
---|
327 | ): boolean;
|
---|
328 | begin
|
---|
329 | result := (GammaExpansionTab[p1^.red]+ ((not GammaExpansionTab[p1^.green]) shl GreenShift)) >
|
---|
330 | (GammaExpansionTab[p2^.red]+((not GammaExpansionTab[p2^.green]) shl GreenShift));
|
---|
331 | end;
|
---|
332 |
|
---|
333 | function IsRInvGGreaterThanValue(p: PBGRAPixel;
|
---|
334 | v: UInt32): boolean;
|
---|
335 | begin
|
---|
336 | with p^ do
|
---|
337 | result := (GammaExpansionTab[red]+((not GammaExpansionTab[green]) shl GreenShift)) >= v;
|
---|
338 | end;
|
---|
339 |
|
---|
340 | function IsGInvBGreater(p1, p2: PBGRAPixel
|
---|
341 | ): boolean;
|
---|
342 | begin
|
---|
343 | result := (GammaExpansionTab[p1^.green] shl GreenShift + not GammaExpansionTab[p1^.blue]) >
|
---|
344 | (GammaExpansionTab[p2^.green] shl GreenShift + not GammaExpansionTab[p2^.blue]);
|
---|
345 | end;
|
---|
346 |
|
---|
347 | function IsGInvBGreaterThanValue(p: PBGRAPixel;
|
---|
348 | v: UInt32): boolean;
|
---|
349 | begin
|
---|
350 | with p^ do
|
---|
351 | result := (GammaExpansionTab[green] shl GreenShift + not GammaExpansionTab[blue]) >= v;
|
---|
352 | end;
|
---|
353 |
|
---|
354 | function IsRInvBGreater(p1, p2: PBGRAPixel
|
---|
355 | ): boolean;
|
---|
356 | begin
|
---|
357 | result := (GammaExpansionTab[p1^.red] shl RedShift + not GammaExpansionTab[p1^.blue]) >
|
---|
358 | (GammaExpansionTab[p2^.red] shl RedShift + not GammaExpansionTab[p2^.blue]);
|
---|
359 | end;
|
---|
360 |
|
---|
361 | function IsRInvBGreaterThanValue(p: PBGRAPixel;
|
---|
362 | v: UInt32): boolean;
|
---|
363 | begin
|
---|
364 | with p^ do
|
---|
365 | result := (GammaExpansionTab[red] shl RedShift + not GammaExpansionTab[blue]) >= v;
|
---|
366 | end;
|
---|
367 |
|
---|
368 | function IsRInvGBGreater(p1, p2: PBGRAPixel
|
---|
369 | ): boolean;
|
---|
370 | begin
|
---|
371 | result := (GammaExpansionTab[p1^.red] shl RedShift + ((not GammaExpansionTab[p1^.green]) shl GreenShift) + not GammaExpansionTab[p1^.blue]) >
|
---|
372 | (GammaExpansionTab[p2^.red] shl RedShift + ((not GammaExpansionTab[p2^.green]) shl GreenShift) + not GammaExpansionTab[p2^.blue]);
|
---|
373 | end;
|
---|
374 |
|
---|
375 | function IsRInvGBGreaterThanValue(p: PBGRAPixel;
|
---|
376 | v: UInt32): boolean;
|
---|
377 | begin
|
---|
378 | with p^ do
|
---|
379 | result := (GammaExpansionTab[red] shl RedShift + ((not GammaExpansionTab[green]) shl GreenShift) + not GammaExpansionTab[blue]) >= v;
|
---|
380 | end;
|
---|
381 |
|
---|
382 | function IsGInvRBGreater(p1, p2: PBGRAPixel
|
---|
383 | ): boolean;
|
---|
384 | begin
|
---|
385 | result := (GammaExpansionTab[p1^.green] shl GreenShift + ((not GammaExpansionTab[p1^.red]) shl RedShift) + not GammaExpansionTab[p1^.blue]) >
|
---|
386 | (GammaExpansionTab[p2^.green] shl GreenShift + ((not GammaExpansionTab[p2^.red]) shl RedShift) + not GammaExpansionTab[p2^.blue]);
|
---|
387 | end;
|
---|
388 |
|
---|
389 | function IsGInvRBGreaterThanValue(p: PBGRAPixel;
|
---|
390 | v: UInt32): boolean;
|
---|
391 | begin
|
---|
392 | with p^ do
|
---|
393 | result := (GammaExpansionTab[green] shl GreenShift + ((not GammaExpansionTab[red]) shl RedShift) + not GammaExpansionTab[blue]) >= v;
|
---|
394 | end;
|
---|
395 |
|
---|
396 | function IsBInvRGGreater(p1, p2: PBGRAPixel
|
---|
397 | ): boolean;
|
---|
398 | begin
|
---|
399 | result := (GammaExpansionTab[p1^.blue] + ((not GammaExpansionTab[p1^.red]) shl RedShift) + ((not GammaExpansionTab[p1^.green]) shl GreenShift)) >
|
---|
400 | (GammaExpansionTab[p2^.blue] + ((not GammaExpansionTab[p2^.red]) shl RedShift) + ((not GammaExpansionTab[p2^.green]) shl GreenShift));
|
---|
401 | end;
|
---|
402 |
|
---|
403 | function IsBInvRGGreaterThanValue(p: PBGRAPixel;
|
---|
404 | v: UInt32): boolean;
|
---|
405 | begin
|
---|
406 | with p^ do
|
---|
407 | result := (GammaExpansionTab[blue] + ((not GammaExpansionTab[red]) shl RedShift) + ((not GammaExpansionTab[green]) shl GreenShift)) >= v;
|
---|
408 | end;
|
---|
409 |
|
---|
410 | function IsSaturationGreater(p1, p2: PBGRAPixel): boolean;
|
---|
411 | begin
|
---|
412 | result := GetDimensionValue(p1^,cdSaturation) > GetDimensionValue(p2^,cdSaturation);
|
---|
413 | end;
|
---|
414 |
|
---|
415 | function IsSaturationGreaterThanValue(p: PBGRAPixel;
|
---|
416 | v: UInt32): boolean;
|
---|
417 | begin
|
---|
418 | result := GetDimensionValue(p^,cdSaturation) >= v;
|
---|
419 | end;
|
---|
420 |
|
---|
421 | function IsRedGreater(p1, p2: PBGRAPixel): boolean;
|
---|
422 | begin
|
---|
423 | result := p1^.red > p2^.red;
|
---|
424 | end;
|
---|
425 |
|
---|
426 | function IsRedGreaterThanValue(p: PBGRAPixel;
|
---|
427 | v: UInt32): boolean;
|
---|
428 | begin
|
---|
429 | result := GammaExpansionTab[p^.red] shl RedShift >= v;
|
---|
430 | end;
|
---|
431 |
|
---|
432 | function IsGreenGreater(p1, p2: PBGRAPixel
|
---|
433 | ): boolean;
|
---|
434 | begin
|
---|
435 | result := p1^.green > p2^.green;
|
---|
436 | end;
|
---|
437 |
|
---|
438 | function IsGreenGreaterThanValue(p: PBGRAPixel;
|
---|
439 | v: UInt32): boolean;
|
---|
440 | begin
|
---|
441 | result := GammaExpansionTab[p^.green] shl GreenShift >= v;
|
---|
442 | end;
|
---|
443 |
|
---|
444 | function IsBlueGreater(p1, p2: PBGRAPixel
|
---|
445 | ): boolean;
|
---|
446 | begin
|
---|
447 | result := p1^.blue > p2^.blue;
|
---|
448 | end;
|
---|
449 |
|
---|
450 | function IsBlueGreaterThanValue(p: PBGRAPixel;
|
---|
451 | v: UInt32): boolean;
|
---|
452 | begin
|
---|
453 | result := GammaExpansionTab[p^.blue] >= v;
|
---|
454 | end;
|
---|
455 |
|
---|
456 | function IsAlphaGreater(p1, p2: PBGRAPixel
|
---|
457 | ): boolean;
|
---|
458 | begin
|
---|
459 | result := p1^.alpha > p2^.alpha;
|
---|
460 | end;
|
---|
461 |
|
---|
462 | function IsAlphaGreaterThanValue(p: PBGRAPixel;
|
---|
463 | v: UInt32): boolean;
|
---|
464 | begin
|
---|
465 | result := (p^.alpha + p^.alpha shl 8) shl AlphaShift >= v;
|
---|
466 | end;
|
---|
467 |
|
---|
468 | function IsDWordGreater(p1, p2: PBGRAPixel
|
---|
469 | ): boolean;
|
---|
470 | begin
|
---|
471 | result := DWord(p1^) > DWord(p2^);
|
---|
472 | end;
|
---|
473 |
|
---|
474 | function IsDWordGreaterThanValue(p: PBGRAPixel;
|
---|
475 | v: UInt32): boolean;
|
---|
476 | begin
|
---|
477 | result := DWord(p^) >= v;
|
---|
478 | end;
|
---|
479 |
|
---|
480 | function GetPixelStrictComparer(ADimension: TColorDimension
|
---|
481 | ): TIsChannelStrictlyGreaterFunc;
|
---|
482 | begin
|
---|
483 | case ADimension of
|
---|
484 | cdFast: result := @IsDWordGreater;
|
---|
485 | cdRed: result := @IsRedGreater;
|
---|
486 | cdGreen: result := @IsGreenGreater;
|
---|
487 | cdBlue: result := @IsBlueGreater;
|
---|
488 | cdAlpha: result := @IsAlphaGreater;
|
---|
489 | cdRGB: result := @IsRGBGreater;
|
---|
490 | cdRG: result := @IsRGGreater;
|
---|
491 | cdGB: result := @IsGBGreater;
|
---|
492 | cdRB: result := @IsRBGreater;
|
---|
493 | cdRInvG: result := @IsRInvGGreater;
|
---|
494 | cdGInvB: result := @IsGInvBGreater;
|
---|
495 | cdRInvB: result := @IsRInvBGreater;
|
---|
496 | cdRInvGB: result := @IsRInvGBGreater;
|
---|
497 | cdGInvRB: result := @IsGInvRBGreater;
|
---|
498 | cdBInvRG: result := @IsBInvRGGreater;
|
---|
499 | cdSaturation: result := @IsSaturationGreater;
|
---|
500 | else raise Exception.Create('Unknown dimension');
|
---|
501 | end;
|
---|
502 | end;
|
---|
503 |
|
---|
504 | function GetPixelValueComparer(ADimension: TColorDimension
|
---|
505 | ): TIsChannelGreaterThanOrEqualToValueFunc;
|
---|
506 | begin
|
---|
507 | case ADimension of
|
---|
508 | cdFast: result := @IsDWordGreaterThanValue;
|
---|
509 | cdRed: result := @IsRedGreaterThanValue;
|
---|
510 | cdGreen: result := @IsGreenGreaterThanValue;
|
---|
511 | cdBlue: result := @IsBlueGreaterThanValue;
|
---|
512 | cdAlpha: result := @IsAlphaGreaterThanValue;
|
---|
513 | cdRGB: result := @IsRGBGreaterThanValue;
|
---|
514 | cdRG: result := @IsRGGreaterThanValue;
|
---|
515 | cdGB: result := @IsGBGreaterThanValue;
|
---|
516 | cdRB: result := @IsRBGreaterThanValue;
|
---|
517 | cdRInvG: result := @IsRInvGGreaterThanValue;
|
---|
518 | cdGInvB: result := @IsGInvBGreaterThanValue;
|
---|
519 | cdRInvB: result := @IsRInvBGreaterThanValue;
|
---|
520 | cdRInvGB: result := @IsRInvGBGreaterThanValue;
|
---|
521 | cdGInvRB: result := @IsGInvRBGreaterThanValue;
|
---|
522 | cdBInvRG: result := @IsBInvRGGreaterThanValue;
|
---|
523 | cdSaturation: result := @IsSaturationGreaterThanValue;
|
---|
524 | else raise Exception.Create('Unknown dimension');
|
---|
525 | end;
|
---|
526 | end;
|
---|
527 |
|
---|
528 | function BGRAColorCount(ABitmap: TBGRACustomBitmap;
|
---|
529 | AAlpha: TAlphaChannelPaletteOption): integer;
|
---|
530 | var
|
---|
531 | box: TBGRAColorBox;
|
---|
532 | begin
|
---|
533 | box := TBGRAColorBox.Create(AllColorDimensions,ABitmap,AAlpha);
|
---|
534 | result := box.ColorCount[True];
|
---|
535 | box.Free;
|
---|
536 | end;
|
---|
537 |
|
---|
538 | const
|
---|
539 | ApproxPaletteDimensions = [cdAlpha,cdRInvG,cdGInvB,cdRInvB,cdRInvGB,cdGInvRB,cdBInvRG,cdRGB];
|
---|
540 |
|
---|
541 | { TBGRAApproxPaletteViaLargerPalette }
|
---|
542 |
|
---|
543 | function TBGRAApproxPaletteViaLargerPalette.FindNearestLargerColorIndex(
|
---|
544 | AValue: TBGRAPixel): integer;
|
---|
545 | begin
|
---|
546 | result := FLarger.FindNearestColorIndex(AValue);
|
---|
547 | end;
|
---|
548 |
|
---|
549 | function TBGRAApproxPaletteViaLargerPalette.SlowFindNearestColorIndex(
|
---|
550 | AValue: TBGRAPixel): integer;
|
---|
551 | var diff,curDiff: NativeInt;
|
---|
552 | i: NativeInt;
|
---|
553 | begin
|
---|
554 | if AValue.alpha = 0 then
|
---|
555 | begin
|
---|
556 | result := FTransparentColorIndex;
|
---|
557 | exit;
|
---|
558 | end;
|
---|
559 | diff := BGRAWordDiff(AValue, FColors[0].Color);
|
---|
560 | result := 0;
|
---|
561 | for i := 0 to high(FColors) do
|
---|
562 | begin
|
---|
563 | curDiff := BGRAWordDiff(AValue, FColors[i].Color);
|
---|
564 | if curDiff < diff then
|
---|
565 | begin
|
---|
566 | result := i;
|
---|
567 | diff := curDiff;
|
---|
568 | end;
|
---|
569 | end;
|
---|
570 | end;
|
---|
571 |
|
---|
572 | constructor TBGRAApproxPaletteViaLargerPalette.Create(
|
---|
573 | const AColors: ArrayOfTBGRAPixel; ALarger: TBGRACustomApproxPalette; ALargerOwned: boolean);
|
---|
574 | var i: integer;
|
---|
575 | largeWeighted: ArrayOfWeightedColor;
|
---|
576 | begin
|
---|
577 | inherited Create(AColors);
|
---|
578 | FTransparentColorIndex:= -1;
|
---|
579 | for i := 0 to high(FColors) do
|
---|
580 | begin
|
---|
581 | FColors[i].Weight := 0;
|
---|
582 | if FColors[i].Color.alpha = 0 then FTransparentColorIndex:= i;
|
---|
583 | end;
|
---|
584 | FLarger := ALarger;
|
---|
585 | FLargerOwned := ALargerOwned;
|
---|
586 | largeWeighted := FLarger.GetAsArrayOfWeightedColor;
|
---|
587 | setlength(FLargerColors, length(largeWeighted));
|
---|
588 | for i := 0 to high(FLargerColors) do
|
---|
589 | with FLargerColors[i] do
|
---|
590 | begin
|
---|
591 | approxColorIndex := SlowFindNearestColorIndex(largeWeighted[i].Color);
|
---|
592 | if approxColorIndex = -1 then
|
---|
593 | approxColor := BGRAPixelTransparent
|
---|
594 | else
|
---|
595 | begin
|
---|
596 | approxColor := FColors[approxColorIndex].Color;
|
---|
597 | inc(FColors[approxColorIndex].Weight, largeWeighted[i].Weight);
|
---|
598 | end;
|
---|
599 | end;
|
---|
600 | end;
|
---|
601 |
|
---|
602 | destructor TBGRAApproxPaletteViaLargerPalette.Destroy;
|
---|
603 | begin
|
---|
604 | if FLargerOwned then FreeAndNil(FLarger);
|
---|
605 | inherited Destroy;
|
---|
606 | end;
|
---|
607 |
|
---|
608 | function TBGRAApproxPaletteViaLargerPalette.FindNearestColor(AValue: TBGRAPixel
|
---|
609 | ): TBGRAPixel;
|
---|
610 | var index: integer;
|
---|
611 | begin
|
---|
612 | index := FindNearestLargerColorIndex(AValue);
|
---|
613 | if index = -1 then
|
---|
614 | result := BGRAPixelTransparent
|
---|
615 | else
|
---|
616 | Result:= FLargerColors[index].approxColor;
|
---|
617 | end;
|
---|
618 |
|
---|
619 | function TBGRAApproxPaletteViaLargerPalette.FindNearestColorIndex(
|
---|
620 | AValue: TBGRAPixel): integer;
|
---|
621 | var index: integer;
|
---|
622 | begin
|
---|
623 | index := FindNearestLargerColorIndex(AValue);
|
---|
624 | if index = -1 then
|
---|
625 | result := -1
|
---|
626 | else
|
---|
627 | Result:= FLargerColors[index].approxColorIndex;
|
---|
628 | end;
|
---|
629 |
|
---|
630 | function TBGRAApproxPaletteViaLargerPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
|
---|
631 | var
|
---|
632 | i: Integer;
|
---|
633 | begin
|
---|
634 | setlength(result, length(FColors));
|
---|
635 | for i := 0 to high(FColors) do
|
---|
636 | result[i] := FColors[i];
|
---|
637 | end;
|
---|
638 |
|
---|
639 | { TBGRAApproxPalette }
|
---|
640 |
|
---|
641 | function TBGRAApproxPalette.GetCount: integer;
|
---|
642 | begin
|
---|
643 | result := length(FColors);
|
---|
644 | end;
|
---|
645 |
|
---|
646 | function TBGRAApproxPalette.GetColorByIndex(AIndex: integer): TBGRAPixel;
|
---|
647 | begin
|
---|
648 | if (AIndex < 0) or (AIndex >= length(FColors)) then
|
---|
649 | raise ERangeError.Create('Index out of bounds');
|
---|
650 | result := FColors[AIndex].Color;
|
---|
651 | end;
|
---|
652 |
|
---|
653 | function TBGRAApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32;
|
---|
654 | begin
|
---|
655 | if (AIndex < 0) or (AIndex >= length(FColors)) then
|
---|
656 | raise ERangeError.Create('Index out of bounds');
|
---|
657 | result := FColors[AIndex].Weight;
|
---|
658 | end;
|
---|
659 |
|
---|
660 | procedure TBGRAApproxPalette.Init(const AColors: ArrayOfTBGRAPixel);
|
---|
661 | var
|
---|
662 | weightedColors: ArrayOfWeightedColor;
|
---|
663 | i: NativeInt;
|
---|
664 | begin
|
---|
665 | setlength(weightedColors, length(AColors));
|
---|
666 | for i := 0 to high(weightedColors) do
|
---|
667 | with weightedColors[i] do
|
---|
668 | begin
|
---|
669 | Color := AColors[i];
|
---|
670 | Weight := 1;
|
---|
671 | end;
|
---|
672 | FTree := TBGRAColorTree.Create(TBGRAColorBox.Create(ApproxPaletteDimensions,weightedColors,True),True);
|
---|
673 | FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage);
|
---|
674 |
|
---|
675 | FColors := FTree.GetAsArrayOfWeightedColors;
|
---|
676 | end;
|
---|
677 |
|
---|
678 | constructor TBGRAApproxPalette.Create(const AColors: ArrayOfTBGRAPixel);
|
---|
679 | begin
|
---|
680 | Init(AColors);
|
---|
681 | end;
|
---|
682 |
|
---|
683 | constructor TBGRAApproxPalette.Create(const AColors: ArrayOfWeightedColor);
|
---|
684 | begin
|
---|
685 | FTree := TBGRAColorTree.Create(TBGRAColorBox.Create(ApproxPaletteDimensions,AColors,True),True);
|
---|
686 | FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage);
|
---|
687 |
|
---|
688 | FColors := FTree.GetAsArrayOfWeightedColors;
|
---|
689 | end;
|
---|
690 |
|
---|
691 | constructor TBGRAApproxPalette.Create(AOwnedSplitTree: TBGRAColorTree);
|
---|
692 | begin
|
---|
693 | FTree := AOwnedSplitTree;
|
---|
694 | FColors := FTree.GetAsArrayOfWeightedColors;
|
---|
695 | end;
|
---|
696 |
|
---|
697 | destructor TBGRAApproxPalette.Destroy;
|
---|
698 | begin
|
---|
699 | FreeAndNil(FTree);
|
---|
700 | inherited Destroy;
|
---|
701 | end;
|
---|
702 |
|
---|
703 | function TBGRAApproxPalette.ContainsColor(AValue: TBGRAPixel): boolean;
|
---|
704 | begin
|
---|
705 | result := (IndexOfColor(AValue)<>-1);
|
---|
706 | end;
|
---|
707 |
|
---|
708 | function TBGRAApproxPalette.IndexOfColor(AValue: TBGRAPixel): integer;
|
---|
709 | begin
|
---|
710 | result := FTree.ApproximateColorIndex(AValue);
|
---|
711 | if (result <> -1) and not (DWord(FColors[result].Color) = DWord(AValue)) then result := -1;
|
---|
712 | end;
|
---|
713 |
|
---|
714 | function TBGRAApproxPalette.FindNearestColor(AValue: TBGRAPixel): TBGRAPixel;
|
---|
715 | begin
|
---|
716 | result := FTree.ApproximateColor(AValue);
|
---|
717 | end;
|
---|
718 |
|
---|
719 | function TBGRAApproxPalette.FindNearestColorIndex(AValue: TBGRAPixel): integer;
|
---|
720 | begin
|
---|
721 | result := FTree.ApproximateColorIndex(AValue);
|
---|
722 | end;
|
---|
723 |
|
---|
724 | function TBGRAApproxPalette.GetAsArrayOfColor: ArrayOfTBGRAPixel;
|
---|
725 | var
|
---|
726 | i: NativeInt;
|
---|
727 | begin
|
---|
728 | setlength(result, length(FColors));
|
---|
729 | for i := 0 to high(result) do
|
---|
730 | result[i] := FColors[i].Color;
|
---|
731 | end;
|
---|
732 |
|
---|
733 | function TBGRAApproxPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
|
---|
734 | var
|
---|
735 | i: NativeInt;
|
---|
736 | begin
|
---|
737 | if Assigned(FTree) then
|
---|
738 | result := FTree.GetAsArrayOfWeightedColors
|
---|
739 | else
|
---|
740 | begin
|
---|
741 | setlength(result, length(FColors));
|
---|
742 | for i := 0 to high(result) do
|
---|
743 | result[i] := FColors[i];
|
---|
744 | end;
|
---|
745 | end;
|
---|
746 |
|
---|
747 | { TBGRAColorQuantizer }
|
---|
748 |
|
---|
749 | procedure TBGRAColorQuantizer.Init(ABox: TBGRAColorBox);
|
---|
750 | begin
|
---|
751 | FColors := ABox.FColors;
|
---|
752 | if ABox.HasPureTransparentColor then
|
---|
753 | begin
|
---|
754 | setlength(FColors,length(FColors)+1);
|
---|
755 | with FColors[high(FColors)] do
|
---|
756 | begin
|
---|
757 | Color := BGRAPixelTransparent;
|
---|
758 | Weight:= ABox.PureTransparentColorCount;
|
---|
759 | end;
|
---|
760 | end;
|
---|
761 | ABox.FColors := nil;
|
---|
762 | ABox.Free;
|
---|
763 | FReductionColorCount := 256;
|
---|
764 | FReductionKeepContrast := true;
|
---|
765 | end;
|
---|
766 |
|
---|
767 | procedure TBGRAColorQuantizer.SetReductionColorCount(AValue: Integer);
|
---|
768 | begin
|
---|
769 | if AValue < 1 then AValue := 1;
|
---|
770 | if FReductionColorCount=AValue then Exit;
|
---|
771 | FReductionColorCount:=AValue;
|
---|
772 | FreeAndNil(FPalette);
|
---|
773 | end;
|
---|
774 |
|
---|
775 | procedure TBGRAColorQuantizer.NormalizeArrayOfColors(
|
---|
776 | AColors: ArrayOfTBGRAPixel; ARedBounds, AGreenBounds, ABlueBounds,
|
---|
777 | AAlphaBounds: TDimensionMinMax; AUniform: boolean);
|
---|
778 | var
|
---|
779 | curRedBounds, curGreenBounds, curBlueBounds, curAlphaBounds: TDimensionMinMax;
|
---|
780 | RedSub,RedMul,RedDiv,RedAdd: NativeUInt;
|
---|
781 | GreenSub,GreenMul,GreenDiv,GreenAdd: NativeUInt;
|
---|
782 | BlueSub,BlueMul,BlueDiv,BlueAdd: NativeUInt;
|
---|
783 | AlphaSub,AlphaMul,AlphaDiv,AlphaAdd: NativeUInt;
|
---|
784 | i: NativeInt;
|
---|
785 | colorBounds: TDimensionMinMax;
|
---|
786 | begin
|
---|
787 | if length(AColors)=0 then exit;
|
---|
788 | if AUniform then
|
---|
789 | begin
|
---|
790 | colorBounds := ABlueBounds;
|
---|
791 | colorBounds.GrowToInclude(AGreenBounds.Minimum shr GreenShift);
|
---|
792 | colorBounds.GrowToInclude(AGreenBounds.Maximum shr GreenShift);
|
---|
793 | colorBounds.GrowToInclude(ARedBounds.Minimum shr RedShift);
|
---|
794 | colorBounds.GrowToInclude(ARedBounds.Maximum shr RedShift);
|
---|
795 | NormalizeArrayOfColors(AColors, colorBounds, AAlphaBounds);
|
---|
796 | exit;
|
---|
797 | end;
|
---|
798 | curRedBounds.SetAsPoint(GetDimensionValue(AColors[0],cdRed));
|
---|
799 | curGreenBounds.SetAsPoint(GetDimensionValue(AColors[0],cdGreen));
|
---|
800 | curBlueBounds.SetAsPoint(GetDimensionValue(AColors[0],cdBlue));
|
---|
801 | curAlphaBounds.SetAsPoint(GetDimensionValue(AColors[0],cdAlpha));
|
---|
802 | for i := 1 to high(AColors) do
|
---|
803 | with AColors[i] do
|
---|
804 | begin
|
---|
805 | curRedBounds.GrowToInclude(GetDimensionValue(AColors[i],cdRed));
|
---|
806 | curGreenBounds.GrowToInclude(GetDimensionValue(AColors[i],cdGreen));
|
---|
807 | curBlueBounds.GrowToInclude(GetDimensionValue(AColors[i],cdBlue));
|
---|
808 | curAlphaBounds.GrowToInclude(GetDimensionValue(AColors[i],cdAlpha));
|
---|
809 | end;
|
---|
810 | RedSub := curRedBounds.Minimum shr RedShift;
|
---|
811 | RedMul := ARedBounds.Size shr RedShift;
|
---|
812 | RedDiv := curRedBounds.Size shr RedShift;
|
---|
813 | RedAdd := ARedBounds.Minimum shr RedShift;
|
---|
814 | if RedDiv = 0 then RedDiv := 1;
|
---|
815 | GreenSub := curGreenBounds.Minimum shr GreenShift;
|
---|
816 | GreenMul := AGreenBounds.Size shr GreenShift;
|
---|
817 | GreenDiv := curGreenBounds.Size shr GreenShift;
|
---|
818 | GreenAdd := AGreenBounds.Minimum shr GreenShift;
|
---|
819 | if GreenDiv = 0 then GreenDiv := 1;
|
---|
820 | BlueSub := curBlueBounds.Minimum;
|
---|
821 | BlueMul := ABlueBounds.Size;
|
---|
822 | BlueDiv := curBlueBounds.Size;
|
---|
823 | BlueAdd := ABlueBounds.Minimum;
|
---|
824 | if BlueDiv = 0 then BlueDiv := 1;
|
---|
825 | AlphaSub := curAlphaBounds.Minimum shr (AlphaShift+8);
|
---|
826 | AlphaMul := AAlphaBounds.Size shr (AlphaShift+8);
|
---|
827 | AlphaDiv := curAlphaBounds.Size shr (AlphaShift+8);
|
---|
828 | AlphaAdd := AAlphaBounds.Minimum shr (AlphaShift+8);
|
---|
829 | if AlphaDiv = 0 then AlphaDiv := 1;
|
---|
830 | for i := 0 to high(AColors) do
|
---|
831 | with AColors[i] do
|
---|
832 | begin
|
---|
833 | red := GammaCompressionTab[((GammaExpansionTab[red]-RedSub)*RedMul+(RedDiv shr 1)) div RedDiv + RedAdd];
|
---|
834 | green := GammaCompressionTab[((GammaExpansionTab[green]-GreenSub)*GreenMul+(GreenDiv shr 1)) div GreenDiv + GreenAdd];
|
---|
835 | blue := GammaCompressionTab[((GammaExpansionTab[blue]-BlueSub)*BlueMul+(BlueDiv shr 1)) div BlueDiv + BlueAdd];
|
---|
836 | alpha := ((alpha-AlphaSub)*AlphaMul+(AlphaDiv shr 1)) div AlphaDiv + AlphaAdd;
|
---|
837 | end;
|
---|
838 | end;
|
---|
839 |
|
---|
840 | procedure TBGRAColorQuantizer.NormalizeArrayOfColors(
|
---|
841 | AColors: ArrayOfTBGRAPixel; AColorBounds, AAlphaBounds: TDimensionMinMax);
|
---|
842 | var
|
---|
843 | curColorBounds, curAlphaBounds: TDimensionMinMax;
|
---|
844 | ColorSub,ColorMul,ColorDiv,ColorAdd: NativeUInt;
|
---|
845 | AlphaSub,AlphaMul,AlphaDiv,AlphaAdd: NativeUInt;
|
---|
846 | i: NativeInt;
|
---|
847 | begin
|
---|
848 | if length(AColors)=0 then exit;
|
---|
849 | curColorBounds.SetAsPoint(GammaExpansionTab[AColors[0].red]);
|
---|
850 | curColorBounds.GrowToInclude(GammaExpansionTab[AColors[0].green]);
|
---|
851 | curColorBounds.GrowToInclude(GammaExpansionTab[AColors[0].blue]);
|
---|
852 | curAlphaBounds.SetAsPoint(AColors[0].alpha);
|
---|
853 | for i := 1 to high(AColors) do
|
---|
854 | with AColors[i] do
|
---|
855 | begin
|
---|
856 | curColorBounds.GrowToInclude(GammaExpansionTab[red]);
|
---|
857 | curColorBounds.GrowToInclude(GammaExpansionTab[green]);
|
---|
858 | curColorBounds.GrowToInclude(GammaExpansionTab[blue]);
|
---|
859 | curAlphaBounds.GrowToInclude(alpha);
|
---|
860 | end;
|
---|
861 | ColorSub := curColorBounds.Minimum;
|
---|
862 | ColorMul := AColorBounds.Size;
|
---|
863 | ColorDiv := curColorBounds.Size;
|
---|
864 | ColorAdd := AColorBounds.Minimum;
|
---|
865 | if ColorDiv = 0 then ColorDiv := 1;
|
---|
866 | AlphaSub := curAlphaBounds.Minimum;
|
---|
867 | AlphaMul := AAlphaBounds.Size shr 8;
|
---|
868 | AlphaDiv := curAlphaBounds.Size;
|
---|
869 | AlphaAdd := AAlphaBounds.Minimum shr 8;
|
---|
870 | if AlphaDiv = 0 then AlphaDiv := 1;
|
---|
871 | for i := 0 to high(AColors) do
|
---|
872 | with AColors[i] do
|
---|
873 | begin
|
---|
874 | red := GammaCompressionTab[((GammaExpansionTab[red]-ColorSub)*ColorMul+(ColorDiv shr 1)) div ColorDiv + ColorAdd];
|
---|
875 | green := GammaCompressionTab[((GammaExpansionTab[green]-ColorSub)*ColorMul+(ColorDiv shr 1)) div ColorDiv + ColorAdd];
|
---|
876 | blue := GammaCompressionTab[((GammaExpansionTab[blue]-ColorSub)*ColorMul+(ColorDiv shr 1)) div ColorDiv + ColorAdd];
|
---|
877 | alpha := ((alpha-AlphaSub)*AlphaMul+(AlphaDiv shr 1)) div AlphaDiv + AlphaAdd;
|
---|
878 | end;
|
---|
879 | end;
|
---|
880 |
|
---|
881 | function TBGRAColorQuantizer.GetSourceColorCount: Integer;
|
---|
882 | begin
|
---|
883 | result := length(FColors);
|
---|
884 | end;
|
---|
885 |
|
---|
886 | function TBGRAColorQuantizer.GetReductionColorCount: integer;
|
---|
887 | begin
|
---|
888 | result := FReductionColorCount;
|
---|
889 | end;
|
---|
890 |
|
---|
891 | function TBGRAColorQuantizer.GetPalette: TBGRACustomApproxPalette;
|
---|
892 | var
|
---|
893 | tree: TBGRAColorTree;
|
---|
894 |
|
---|
895 | procedure MakeTreeErrorDiffusionFriendly;
|
---|
896 | var moreColors: ArrayOfWeightedColor;
|
---|
897 | box: TBGRAColorBox;
|
---|
898 | begin
|
---|
899 | moreColors := tree.GetAsArrayOfWeightedColors;
|
---|
900 | tree.free;
|
---|
901 | box := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],moreColors,True);
|
---|
902 | tree := TBGRAColorTree.Create(box,True);
|
---|
903 | tree.SplitIntoPalette(box.ColorCount[true], blApparentInterval, lcAverage);
|
---|
904 | end;
|
---|
905 |
|
---|
906 | var
|
---|
907 | originalBox: TBGRAColorBox;
|
---|
908 | colors: ArrayOfTBGRAPixel;
|
---|
909 | bounds: array[TColorDimension] of TDimensionMinMax;
|
---|
910 | nbLarge,nbOriginal: integer;
|
---|
911 |
|
---|
912 | begin
|
---|
913 | if not Assigned(FPalette) then
|
---|
914 | if FReductionColorCount >= length(FColors) then
|
---|
915 | begin
|
---|
916 | originalBox := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],FColors, False);
|
---|
917 | tree := TBGRAColorTree.Create(originalBox,True);
|
---|
918 | tree.SplitIntoPalette(originalBox.ColorCount[true], blApparentInterval, lcAverage);
|
---|
919 | FPalette := TBGRAApproxPalette.Create(tree);
|
---|
920 | end else
|
---|
921 | begin
|
---|
922 | originalBox := TBGRAColorBox.Create(AllColorDimensions, FColors, False);
|
---|
923 | bounds[cdRed] := originalBox.Bounds[cdRed];
|
---|
924 | bounds[cdGreen] := originalBox.Bounds[cdGreen];
|
---|
925 | bounds[cdBlue] := originalBox.Bounds[cdBlue];
|
---|
926 | bounds[cdAlpha] := originalBox.Bounds[cdAlpha];
|
---|
927 | if originalBox.HasPureTransparentColor then bounds[cdAlpha].Minimum := 0;
|
---|
928 | if FReductionColorCount = 1 then
|
---|
929 | begin
|
---|
930 | setlength(colors,1);
|
---|
931 | colors[0] := originalBox.AverageColor;
|
---|
932 | originalBox.Free;
|
---|
933 | FPalette := TBGRAApproxPalette.Create(colors);
|
---|
934 | end else
|
---|
935 | begin
|
---|
936 | tree := TBGRAColorTree.Create(originalBox,True);
|
---|
937 | if FReductionColorCount <= 64 then
|
---|
938 | begin
|
---|
939 | nbLarge := 128;
|
---|
940 | nbOriginal := originalBox.ColorCount[True];
|
---|
941 | if nbOriginal < 128 then nbLarge:= nbOriginal;
|
---|
942 | colors := tree.SplitIntoPaletteWithSubPalette(nbLarge, blMix,lcMix, FReductionColorCount);
|
---|
943 | MakeTreeErrorDiffusionFriendly;
|
---|
944 | if FReductionColorCount <= 4 then
|
---|
945 | NormalizeArrayOfColors(colors, bounds[cdRed],bounds[cdGreen],bounds[cdBlue],bounds[cdAlpha],true);
|
---|
946 | FPalette := TBGRAApproxPaletteViaLargerPalette.Create(colors, TBGRAApproxPalette.Create(tree), True);
|
---|
947 | end else
|
---|
948 | begin
|
---|
949 | tree.SplitIntoPalette(FReductionColorCount, blMix,lcMix);
|
---|
950 | MakeTreeErrorDiffusionFriendly;
|
---|
951 | FPalette := TBGRAApproxPalette.Create(tree);
|
---|
952 | end;
|
---|
953 | end;
|
---|
954 | end;
|
---|
955 | result := FPalette;
|
---|
956 | end;
|
---|
957 |
|
---|
958 | function TBGRAColorQuantizer.GetSourceColor(AIndex: integer): TBGRAPixel;
|
---|
959 | begin
|
---|
960 | if (AIndex < 0) or (AIndex >= length(FColors)) then
|
---|
961 | raise ERangeError.Create('Index out of bounds');
|
---|
962 | result := FColors[AIndex].Color;
|
---|
963 | end;
|
---|
964 |
|
---|
965 | constructor TBGRAColorQuantizer.Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean);
|
---|
966 | begin
|
---|
967 | FSeparateAlphaChannel:= ASeparateAlphaChannel;
|
---|
968 | Init(TBGRAColorBox.Create(AllColorDimensions, APalette));
|
---|
969 | end;
|
---|
970 |
|
---|
971 | constructor TBGRAColorQuantizer.Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption);
|
---|
972 | begin
|
---|
973 | FSeparateAlphaChannel:= (AAlpha = acIgnore);
|
---|
974 | Init(TBGRAColorBox.Create(AllColorDimensions, ABitmap, AAlpha));
|
---|
975 | end;
|
---|
976 |
|
---|
977 | constructor TBGRAColorQuantizer.Create(APalette: TBGRACustomPalette;
|
---|
978 | ASeparateAlphaChannel: boolean; AReductionColorCount: integer);
|
---|
979 | begin
|
---|
980 | FSeparateAlphaChannel:= ASeparateAlphaChannel;
|
---|
981 | Init(TBGRAColorBox.Create(AllColorDimensions, APalette));
|
---|
982 | ReductionColorCount := AReductionColorCount;
|
---|
983 | end;
|
---|
984 |
|
---|
985 | constructor TBGRAColorQuantizer.Create(ABitmap: TBGRACustomBitmap;
|
---|
986 | AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer);
|
---|
987 | begin
|
---|
988 | FSeparateAlphaChannel:= (AAlpha = acIgnore);
|
---|
989 | Init(TBGRAColorBox.Create(AllColorDimensions, ABitmap, AAlpha));
|
---|
990 | ReductionColorCount := AReductionColorCount;
|
---|
991 | end;
|
---|
992 |
|
---|
993 | destructor TBGRAColorQuantizer.Destroy;
|
---|
994 | begin
|
---|
995 | FreeAndNil(FPalette);
|
---|
996 | inherited Destroy;
|
---|
997 | end;
|
---|
998 |
|
---|
999 | procedure TBGRAColorQuantizer.ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
|
---|
1000 | ABounds: TRect);
|
---|
1001 | var task: TDitheringTask;
|
---|
1002 | begin
|
---|
1003 | task := CreateDitheringTask(AAlgorithm, ABitmap, ReducedPalette, FSeparateAlphaChannel, ABounds);
|
---|
1004 | task.Destination := ABitmap;
|
---|
1005 | task.Execute;
|
---|
1006 | task.Free;
|
---|
1007 | end;
|
---|
1008 |
|
---|
1009 | function TBGRAColorQuantizer.GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm;
|
---|
1010 | ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
|
---|
1011 | var task: TDitheringTask;
|
---|
1012 | begin
|
---|
1013 | task := CreateDitheringTask(AAlgorithm, ABitmap, ReducedPalette, FSeparateAlphaChannel, ABounds);
|
---|
1014 | result := task.Execute;
|
---|
1015 | task.Free;
|
---|
1016 | end;
|
---|
1017 |
|
---|
1018 | function TBGRAColorQuantizer.GetDitheredBitmapIndexedData(
|
---|
1019 | ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
|
---|
1020 | out AScanlineSize: PtrInt): Pointer;
|
---|
1021 | var
|
---|
1022 | indexer: TDitheringToIndexedImage;
|
---|
1023 | begin
|
---|
1024 | indexer := TDitheringToIndexedImage.Create(ReducedPalette, FSeparateAlphaChannel, ABitDepth, AByteOrder);
|
---|
1025 | indexer.DefaultTransparentColorIndex := ReducedPalette.IndexOfColor(BGRAPixelTransparent);
|
---|
1026 | AScanlineSize:= indexer.ComputeMinimumScanlineSize(ABitmap.Width);
|
---|
1027 | result := indexer.DitherImage(AAlgorithm, ABitmap, AScanlineSize);
|
---|
1028 | indexer.Free;
|
---|
1029 | end;
|
---|
1030 |
|
---|
1031 | procedure TBGRAColorQuantizer.SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
|
---|
1032 | AStream: TStream; AFormat: TBGRAImageFormat);
|
---|
1033 | var
|
---|
1034 | dithered: TBGRACustomBitmap;
|
---|
1035 | hasTransp: boolean;
|
---|
1036 | writer: TFPCustomImageWriter;
|
---|
1037 | depth: integer;
|
---|
1038 | begin
|
---|
1039 | dithered := GetDitheredBitmap(AAlgorithm, ABitmap);
|
---|
1040 | try
|
---|
1041 | ReducedPalette.AssignTo(dithered);
|
---|
1042 | hasTransp := dithered.HasTransparentPixels;
|
---|
1043 | writer := CreateBGRAImageWriter(AFormat, hasTransp);
|
---|
1044 | try
|
---|
1045 | if writer is TBGRAWriterPNG then TBGRAWriterPNG(writer).Indexed := true else
|
---|
1046 | if writer is TFPWriterBMP then
|
---|
1047 | begin
|
---|
1048 | if not hasTransp then
|
---|
1049 | begin
|
---|
1050 | depth := BGRARequiredBitDepth(ReducedPalette);
|
---|
1051 | if depth < 8 then
|
---|
1052 | begin
|
---|
1053 | if depth > 4 then
|
---|
1054 | depth := 8
|
---|
1055 | else if depth > 1 then
|
---|
1056 | depth := 4;
|
---|
1057 | end;
|
---|
1058 | TFPWriterBMP(writer).BitsPerPixel := depth;
|
---|
1059 | end;
|
---|
1060 | end;
|
---|
1061 | dithered.SaveToStream(AStream, writer);
|
---|
1062 | finally
|
---|
1063 | writer.Free;
|
---|
1064 | end;
|
---|
1065 | finally
|
---|
1066 | dithered.Free;
|
---|
1067 | end;
|
---|
1068 | end;
|
---|
1069 |
|
---|
1070 | { TBGRAColorTree }
|
---|
1071 |
|
---|
1072 | function TBGRAColorTree.TrySplitLeaf: boolean;
|
---|
1073 | var
|
---|
1074 | dim: TColorDimension;
|
---|
1075 | box2: TBGRAColorBox;
|
---|
1076 | mid: UInt32;
|
---|
1077 | begin
|
---|
1078 | result := false;
|
---|
1079 | if IsLeaf and Assigned(FLeaf) and not FLeaf.PointLike then
|
---|
1080 | begin
|
---|
1081 | dim := FLeaf.LargestApparentDimension;
|
---|
1082 | box2 := FLeaf.MedianCut(dim,mid);
|
---|
1083 | if box2 <> nil then
|
---|
1084 | begin
|
---|
1085 | FInferiorBranch := TBGRAColorTree.Create(FLeaf,True);
|
---|
1086 | FSuperiorBranch := TBGRAColorTree.Create(box2,True);
|
---|
1087 |
|
---|
1088 | FInferiorBranch.FMinBorder := FMinBorder;
|
---|
1089 | FInferiorBranch.FMaxBorder := FMaxBorder;
|
---|
1090 | FSuperiorBranch.FMinBorder := FMinBorder;
|
---|
1091 | FSuperiorBranch.FMaxBorder := FMaxBorder;
|
---|
1092 | FInferiorBranch.FMaxBorder[dim] := false;
|
---|
1093 | FSuperiorBranch.FMinBorder[dim] := false;
|
---|
1094 |
|
---|
1095 | FLeaf := nil;
|
---|
1096 | FIsLeaf:= false;
|
---|
1097 | FDimension := dim;
|
---|
1098 | FPixelValueComparer := GetPixelValueComparer(FDimension);
|
---|
1099 | FSuperiorMiddle := mid;
|
---|
1100 | result := true;
|
---|
1101 | end;
|
---|
1102 | end;
|
---|
1103 | end;
|
---|
1104 |
|
---|
1105 | procedure TBGRAColorTree.ComputeLeavesColor(ALeafColor: TBGRALeafColorMode);
|
---|
1106 | var index: integer;
|
---|
1107 | begin
|
---|
1108 | index := 0;
|
---|
1109 | if HasPureTransparentColor then
|
---|
1110 | begin
|
---|
1111 | FPureTransparentColorIndex:= index;
|
---|
1112 | inc(index);
|
---|
1113 | end;
|
---|
1114 | InternalComputeLeavesColor(ALeafColor,{%H-}index);
|
---|
1115 | end;
|
---|
1116 |
|
---|
1117 | procedure TBGRAColorTree.InternalComputeLeavesColor(
|
---|
1118 | ALeafColor: TBGRALeafColorMode; var AStartIndex: integer);
|
---|
1119 | var nbMin,nbMax: NativeInt;
|
---|
1120 | c: TColorDimension;
|
---|
1121 | extremumColor: TBGRAPixel;
|
---|
1122 | extremumColorRelevant: Boolean;
|
---|
1123 | begin
|
---|
1124 | if IsLeaf then
|
---|
1125 | begin
|
---|
1126 | FLeafColorIndex := AStartIndex;
|
---|
1127 | inc(AStartIndex);
|
---|
1128 | if Assigned(FLeaf) then
|
---|
1129 | begin
|
---|
1130 | if not FLeafColorComputed then
|
---|
1131 | begin
|
---|
1132 | FLeafColorComputed := true;
|
---|
1133 | FCenterColor.alpha:= min(FLeaf.FBounds[cdAlpha].GetCenter shr AlphaShift, 255);
|
---|
1134 | FCenterColor.red:= GammaCompressionTab[min(FLeaf.FBounds[cdRed].GetCenter shr RedShift, 65535)];
|
---|
1135 | FCenterColor.green:= GammaCompressionTab[min(FLeaf.FBounds[cdGreen].GetCenter shr GreenShift, 65535)];
|
---|
1136 | FCenterColor.blue:= GammaCompressionTab[min(FLeaf.FBounds[cdBlue].GetCenter, 65535)];
|
---|
1137 | FAverageColor := FLeaf.AverageColorOrMainColor;
|
---|
1138 | extremumColor := FAverageColor;
|
---|
1139 |
|
---|
1140 | if ALeafColor in [lcMix,lcExtremum] then
|
---|
1141 | begin
|
---|
1142 | nbMax := 0;
|
---|
1143 | nbMin := 0;
|
---|
1144 | for c := succ(low(TColorDimension)) to high(TColorDimension) do
|
---|
1145 | begin
|
---|
1146 | if FMinBorder[c] then inc(nbMin);
|
---|
1147 | if FMaxBorder[c] then inc(nbMax);
|
---|
1148 | end;
|
---|
1149 |
|
---|
1150 | if nbMin > nbMax then
|
---|
1151 | extremumColor := FLeaf.InferiorColor
|
---|
1152 | else if nbMax > nbMin then
|
---|
1153 | extremumColor := FLeaf.SuperiorColor;
|
---|
1154 | end;
|
---|
1155 |
|
---|
1156 | case ALeafColor of
|
---|
1157 | lcAverage,lcMix: FLeafColor := FAverageColor;
|
---|
1158 | lcExtremum: FLeafColor := extremumColor;
|
---|
1159 | else FLeafColor := FCenterColor;
|
---|
1160 | end;
|
---|
1161 |
|
---|
1162 | if ALeafColor = lcMix then
|
---|
1163 | begin
|
---|
1164 | extremumColorRelevant := false;
|
---|
1165 | for c := succ(low(TColorDimension)) to high(TColorDimension) do
|
---|
1166 | if UInt32(abs(GetDimensionValue(extremumColor,c) - GetDimensionValue(FLeafColor,c))) >
|
---|
1167 | FLeaf.FBounds[c].Size div 7 then
|
---|
1168 | begin
|
---|
1169 | extremumColorRelevant := true;
|
---|
1170 | break;
|
---|
1171 | end;
|
---|
1172 | if extremumColorRelevant then FLeafColor := extremumColor;
|
---|
1173 | end;
|
---|
1174 | end;
|
---|
1175 | end else
|
---|
1176 | begin
|
---|
1177 | FLeafColor := BGRAPixelTransparent;
|
---|
1178 | FCenterColor := BGRAPixelTransparent;
|
---|
1179 | end;
|
---|
1180 | end else
|
---|
1181 | begin
|
---|
1182 | if Assigned(FInferiorBranch) then FInferiorBranch.InternalComputeLeavesColor(ALeafColor, AStartIndex);
|
---|
1183 | if Assigned(FSuperiorBranch) then FSuperiorBranch.InternalComputeLeavesColor(ALeafColor, AStartIndex);
|
---|
1184 | end;
|
---|
1185 | end;
|
---|
1186 |
|
---|
1187 | procedure TBGRAColorTree.CheckColorComputed;
|
---|
1188 | begin
|
---|
1189 | if not FLeafColorComputed then
|
---|
1190 | raise exception.Create('Color not computed. Call ComputeLeavesColor first.');
|
---|
1191 | end;
|
---|
1192 |
|
---|
1193 | function TBGRAColorTree.ApproximateColor(AColor: TBGRAPixel): TBGRAPixel;
|
---|
1194 | var branch: TBGRAColorTree;
|
---|
1195 | begin
|
---|
1196 | if AColor.alpha = 0 then
|
---|
1197 | begin
|
---|
1198 | result := BGRAPixelTransparent;
|
---|
1199 | exit;
|
---|
1200 | end;
|
---|
1201 | if IsLeaf then
|
---|
1202 | begin
|
---|
1203 | CheckColorComputed;
|
---|
1204 | result := FLeafColor;
|
---|
1205 | end else
|
---|
1206 | begin
|
---|
1207 | if FPixelValueComparer(@AColor,FSuperiorMiddle) then
|
---|
1208 | branch := FSuperiorBranch else branch := FInferiorBranch;
|
---|
1209 | if Assigned(branch) then
|
---|
1210 | result := branch.ApproximateColor(AColor)
|
---|
1211 | else
|
---|
1212 | result := BGRAPixelTransparent;
|
---|
1213 | end;
|
---|
1214 | end;
|
---|
1215 |
|
---|
1216 | function TBGRAColorTree.ApproximateColorIndex(AColor: TBGRAPixel): integer;
|
---|
1217 | var branch: TBGRAColorTree;
|
---|
1218 | begin
|
---|
1219 | if AColor.alpha = 0 then
|
---|
1220 | begin
|
---|
1221 | result := FPureTransparentColorIndex;
|
---|
1222 | exit;
|
---|
1223 | end;
|
---|
1224 | if IsLeaf then
|
---|
1225 | begin
|
---|
1226 | CheckColorComputed;
|
---|
1227 | result := FLeafColorIndex;
|
---|
1228 | end else
|
---|
1229 | begin
|
---|
1230 | if FPixelValueComparer(@AColor,FSuperiorMiddle) then
|
---|
1231 | branch := FSuperiorBranch else branch := FInferiorBranch;
|
---|
1232 | if Assigned(branch) then
|
---|
1233 | result := branch.ApproximateColorIndex(AColor)
|
---|
1234 | else
|
---|
1235 | result := FPureTransparentColorIndex;
|
---|
1236 | end;
|
---|
1237 | end;
|
---|
1238 |
|
---|
1239 | function TBGRAColorTree.GetAsArrayOfApproximatedColors: ArrayOfTBGRAPixel;
|
---|
1240 | var a,b: ArrayOfTBGRAPixel;
|
---|
1241 | idx,i: integer;
|
---|
1242 | begin
|
---|
1243 | if IsLeaf then
|
---|
1244 | begin
|
---|
1245 | CheckColorComputed;
|
---|
1246 | setlength(result,1+byte(HasPureTransparentColor));
|
---|
1247 | idx := 0;
|
---|
1248 | if HasPureTransparentColor then
|
---|
1249 | begin
|
---|
1250 | result[idx] := BGRAPixelTransparent;
|
---|
1251 | inc(idx);
|
---|
1252 | end;
|
---|
1253 | result[idx] := FLeafColor;
|
---|
1254 | end else
|
---|
1255 | begin
|
---|
1256 | a := FInferiorBranch.GetAsArrayOfApproximatedColors;
|
---|
1257 | b := FSuperiorBranch.GetAsArrayOfApproximatedColors;
|
---|
1258 | setlength(result, length(a)+length(b)+byte(HasPureTransparentColor));
|
---|
1259 | idx := 0;
|
---|
1260 | if HasPureTransparentColor then
|
---|
1261 | begin
|
---|
1262 | result[idx] := BGRAPixelTransparent;
|
---|
1263 | inc(idx);
|
---|
1264 | end;
|
---|
1265 | for i := 0 to high(a) do
|
---|
1266 | begin
|
---|
1267 | result[idx] := a[i];
|
---|
1268 | inc(idx);
|
---|
1269 | end;
|
---|
1270 | for i := 0 to high(b) do
|
---|
1271 | begin
|
---|
1272 | result[idx] := b[i];
|
---|
1273 | inc(idx);
|
---|
1274 | end;
|
---|
1275 | end;
|
---|
1276 | end;
|
---|
1277 |
|
---|
1278 | function TBGRAColorTree.GetAsArrayOfWeightedColors: ArrayOfWeightedColor;
|
---|
1279 | var a,b: ArrayOfWeightedColor;
|
---|
1280 | idx,i: integer;
|
---|
1281 | begin
|
---|
1282 | if IsLeaf then
|
---|
1283 | begin
|
---|
1284 | CheckColorComputed;
|
---|
1285 | setlength(result,1+byte(HasPureTransparentColor));
|
---|
1286 | idx := 0;
|
---|
1287 | if HasPureTransparentColor then
|
---|
1288 | begin
|
---|
1289 | result[idx].Color := BGRAPixelTransparent;
|
---|
1290 | result[idx].Weight := PureTransparentColorCount;
|
---|
1291 | inc(idx);
|
---|
1292 | end;
|
---|
1293 | result[idx].Color := FLeafColor;
|
---|
1294 | result[idx].Weight := Weight;
|
---|
1295 | end else
|
---|
1296 | begin
|
---|
1297 | a := FInferiorBranch.GetAsArrayOfWeightedColors;
|
---|
1298 | b := FSuperiorBranch.GetAsArrayOfWeightedColors;
|
---|
1299 | setlength(result, length(a)+length(b)+byte(HasPureTransparentColor));
|
---|
1300 | idx := 0;
|
---|
1301 | if HasPureTransparentColor then
|
---|
1302 | begin
|
---|
1303 | result[idx].Color := BGRAPixelTransparent;
|
---|
1304 | result[idx].Weight := PureTransparentColorCount;
|
---|
1305 | inc(idx);
|
---|
1306 | end;
|
---|
1307 | for i := 0 to high(a) do
|
---|
1308 | begin
|
---|
1309 | result[idx] := a[i];
|
---|
1310 | inc(idx);
|
---|
1311 | end;
|
---|
1312 | for i := 0 to high(b) do
|
---|
1313 | begin
|
---|
1314 | result[idx] := b[i];
|
---|
1315 | inc(idx);
|
---|
1316 | end;
|
---|
1317 | end;
|
---|
1318 | end;
|
---|
1319 |
|
---|
1320 | procedure TBGRAColorTree.SplitIntoPalette(ACount: integer;
|
---|
1321 | AMethod: TBiggestLeafMethod; ALeafColor: TBGRALeafColorMode);
|
---|
1322 | var nbColors: integer;
|
---|
1323 | leaf: TBGRAColorTree;
|
---|
1324 | begin
|
---|
1325 | nbColors := ApproximatedColorCount;
|
---|
1326 | while nbColors < ACount do
|
---|
1327 | begin
|
---|
1328 | leaf := FindBiggestLeaf(AMethod);
|
---|
1329 | if not leaf.TrySplitLeaf then break;
|
---|
1330 | inc(nbColors);
|
---|
1331 | end;
|
---|
1332 | ComputeLeavesColor(ALeafColor);
|
---|
1333 | FreeLeaves;
|
---|
1334 | end;
|
---|
1335 |
|
---|
1336 | function TBGRAColorTree.SplitIntoPaletteWithSubPalette(ACount: integer;
|
---|
1337 | AMethod: TBiggestLeafMethod; ALeafColor: TBGRALeafColorMode;
|
---|
1338 | ASubPaletteCount: integer): ArrayOfTBGRAPixel;
|
---|
1339 | var nbColors: integer;
|
---|
1340 | leaf: TBGRAColorTree;
|
---|
1341 | begin
|
---|
1342 | result := nil;
|
---|
1343 | nbColors := ApproximatedColorCount;
|
---|
1344 | if ASubPaletteCount > ACount then ASubPaletteCount:= ACount;
|
---|
1345 | if nbColors = ASubPaletteCount then
|
---|
1346 | begin
|
---|
1347 | ComputeLeavesColor(ALeafColor);
|
---|
1348 | result := GetAsArrayOfApproximatedColors;
|
---|
1349 | end;
|
---|
1350 | while nbColors < ACount do
|
---|
1351 | begin
|
---|
1352 | leaf := FindBiggestLeaf(AMethod);
|
---|
1353 | if not leaf.TrySplitLeaf then break;
|
---|
1354 | inc(nbColors);
|
---|
1355 | if nbColors = ASubPaletteCount then
|
---|
1356 | begin
|
---|
1357 | ComputeLeavesColor(ALeafColor);
|
---|
1358 | result := GetAsArrayOfApproximatedColors;
|
---|
1359 | end;
|
---|
1360 | end;
|
---|
1361 | ComputeLeavesColor(ALeafColor);
|
---|
1362 | FreeLeaves;
|
---|
1363 | end;
|
---|
1364 |
|
---|
1365 | function TBGRAColorTree.GetLeafCount: integer;
|
---|
1366 | begin
|
---|
1367 | if IsLeaf then
|
---|
1368 | result := 1
|
---|
1369 | else
|
---|
1370 | begin
|
---|
1371 | result := 0;
|
---|
1372 | if Assigned(FInferiorBranch) then result += FInferiorBranch.LeafCount;
|
---|
1373 | if Assigned(FSuperiorBranch) then result += FSuperiorBranch.LeafCount;
|
---|
1374 | end;
|
---|
1375 | end;
|
---|
1376 |
|
---|
1377 | function TBGRAColorTree.GetApproximatedColorCount: integer;
|
---|
1378 | begin
|
---|
1379 | if IsLeaf then
|
---|
1380 | result := 1
|
---|
1381 | else
|
---|
1382 | begin
|
---|
1383 | result := 0;
|
---|
1384 | if Assigned(FInferiorBranch) then result += FInferiorBranch.ApproximatedColorCount;
|
---|
1385 | if Assigned(FSuperiorBranch) then result += FSuperiorBranch.ApproximatedColorCount;
|
---|
1386 | end;
|
---|
1387 | if HasPureTransparentColor then inc(result);
|
---|
1388 | end;
|
---|
1389 |
|
---|
1390 | function TBGRAColorTree.GetHasPureTransparentColor: boolean;
|
---|
1391 | begin
|
---|
1392 | result := FPureTransparentColorCount > 0;
|
---|
1393 | end;
|
---|
1394 |
|
---|
1395 | procedure TBGRAColorTree.Init(ALeaf: TBGRAColorBox; AOwned: boolean);
|
---|
1396 | var
|
---|
1397 | c: TColorDimension;
|
---|
1398 | begin
|
---|
1399 | if not AOwned then
|
---|
1400 | FLeaf := ALeaf.Duplicate
|
---|
1401 | else
|
---|
1402 | FLeaf := ALeaf;
|
---|
1403 | FLargestApparentInterval:= FLeaf.LargestApparentInterval;
|
---|
1404 | FWeight := FLeaf.TotalWeight;
|
---|
1405 | FIsLeaf:= true;
|
---|
1406 | for c := low(TColorDimension) to high(TColorDimension) do
|
---|
1407 | begin
|
---|
1408 | FMinBorder[c] := true;
|
---|
1409 | FMaxBorder[c] := true;
|
---|
1410 | end;
|
---|
1411 | FPureTransparentColorCount:= FLeaf.PureTransparentColorCount;
|
---|
1412 | FPureTransparentColorIndex:= -1;
|
---|
1413 | end;
|
---|
1414 |
|
---|
1415 | constructor TBGRAColorTree.Create(ABox: TBGRAColorBox; AOwned: boolean);
|
---|
1416 | begin
|
---|
1417 | Init(ABox,AOwned);
|
---|
1418 | end;
|
---|
1419 |
|
---|
1420 | constructor TBGRAColorTree.Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette);
|
---|
1421 | begin
|
---|
1422 | Init(TBGRAColorBox.Create(ADimensions, APalette),True);
|
---|
1423 | end;
|
---|
1424 |
|
---|
1425 | constructor TBGRAColorTree.Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption);
|
---|
1426 | begin
|
---|
1427 | Init(TBGRAColorBox.Create(ADimensions, ABitmap, AAlpha),True);
|
---|
1428 | end;
|
---|
1429 |
|
---|
1430 | destructor TBGRAColorTree.Destroy;
|
---|
1431 | begin
|
---|
1432 | FreeAndNil(FInferiorBranch);
|
---|
1433 | FreeAndNil(FSuperiorBranch);
|
---|
1434 | FreeAndNil(FLeaf);
|
---|
1435 | inherited Destroy;
|
---|
1436 | end;
|
---|
1437 |
|
---|
1438 | procedure TBGRAColorTree.FreeLeaves;
|
---|
1439 | begin
|
---|
1440 | if IsLeaf then
|
---|
1441 | FreeAndNil(FLeaf)
|
---|
1442 | else
|
---|
1443 | begin
|
---|
1444 | if Assigned(FInferiorBranch) then FInferiorBranch.FreeLeaves;
|
---|
1445 | if Assigned(FSuperiorBranch) then FSuperiorBranch.FreeLeaves;
|
---|
1446 | end;
|
---|
1447 | end;
|
---|
1448 |
|
---|
1449 | function TBGRAColorTree.FindBiggestLeaf(AMethod: TBiggestLeafMethod
|
---|
1450 | ): TBGRAColorTree;
|
---|
1451 | var infLeaf,supLeaf: TBGRAColorTree;
|
---|
1452 | begin
|
---|
1453 | if IsLeaf then
|
---|
1454 | result := self
|
---|
1455 | else
|
---|
1456 | begin
|
---|
1457 | infLeaf := FInferiorBranch.FindBiggestLeaf(AMethod);
|
---|
1458 | supLeaf := FSuperiorBranch.FindBiggestLeaf(AMethod);
|
---|
1459 | case AMethod of
|
---|
1460 | blApparentInterval:
|
---|
1461 | if infLeaf.LargestApparentInterval >= supLeaf.LargestApparentInterval then
|
---|
1462 | result := infLeaf
|
---|
1463 | else
|
---|
1464 | result := supLeaf;
|
---|
1465 | blWeight:
|
---|
1466 | if (infLeaf.LargestApparentInterval > 0) and (infLeaf.Weight >= supLeaf.Weight) then
|
---|
1467 | result := infLeaf
|
---|
1468 | else
|
---|
1469 | result := supLeaf;
|
---|
1470 | else{blMix:}
|
---|
1471 | if (sqrt(infLeaf.Weight/FWeight)*(infLeaf.LargestApparentInterval/LargestApparentInterval) >=
|
---|
1472 | sqrt(supLeaf.Weight/FWeight)*(supLeaf.LargestApparentInterval/LargestApparentInterval) ) then
|
---|
1473 | result := infLeaf
|
---|
1474 | else
|
---|
1475 | result := supLeaf;
|
---|
1476 | end;
|
---|
1477 | end;
|
---|
1478 | end;
|
---|
1479 |
|
---|
1480 | { TDimensionMinMax }
|
---|
1481 |
|
---|
1482 | function TDimensionMinMax.Size: UInt32;
|
---|
1483 | begin
|
---|
1484 | if Maximum>Minimum then
|
---|
1485 | result := Maximum-Minimum
|
---|
1486 | else
|
---|
1487 | result := 0;
|
---|
1488 | end;
|
---|
1489 |
|
---|
1490 | function TDimensionMinMax.Contains(AValue: UInt32): boolean;
|
---|
1491 | begin
|
---|
1492 | result := (AValue >= Minimum) and (AValue <= Maximum);
|
---|
1493 | end;
|
---|
1494 |
|
---|
1495 | function TDimensionMinMax.PointLike: boolean;
|
---|
1496 | begin
|
---|
1497 | result := (Minimum = Maximum);
|
---|
1498 | end;
|
---|
1499 |
|
---|
1500 | procedure TDimensionMinMax.SetAsPoint(AValue: UInt32);
|
---|
1501 | begin
|
---|
1502 | Minimum := AValue;
|
---|
1503 | Maximum := AValue;
|
---|
1504 | end;
|
---|
1505 |
|
---|
1506 | function TDimensionMinMax.GetCenter: UInt32;
|
---|
1507 | begin
|
---|
1508 | result := (Minimum+Maximum) shr 1;
|
---|
1509 | end;
|
---|
1510 |
|
---|
1511 | procedure TDimensionMinMax.GrowToInclude(AValue: UInt32);
|
---|
1512 | begin
|
---|
1513 | if AValue < Minimum then Minimum := AValue
|
---|
1514 | else if AValue > Maximum then Maximum := AValue;
|
---|
1515 | end;
|
---|
1516 |
|
---|
1517 | { TBGRAColorBox }
|
---|
1518 |
|
---|
1519 | function TBGRAColorBox.GetApparentInterval(ADimension: TColorDimension): UInt32;
|
---|
1520 | var factor: single;
|
---|
1521 | begin
|
---|
1522 | if not (ADimension in FDimensions) then result := 0
|
---|
1523 | else
|
---|
1524 | begin
|
---|
1525 | factor := 1;
|
---|
1526 | case ADimension of
|
---|
1527 | cdRGB: factor := 0.7;
|
---|
1528 | end;
|
---|
1529 | result := round(FBounds[ADimension].Size*factor);
|
---|
1530 | end;
|
---|
1531 | end;
|
---|
1532 |
|
---|
1533 | function TBGRAColorBox.GetAverageColor: TBGRAPixel;
|
---|
1534 | var
|
---|
1535 | n: integer;
|
---|
1536 | r, g, b, a: double;
|
---|
1537 | cura: double;
|
---|
1538 | w: UInt32;
|
---|
1539 | begin
|
---|
1540 | a := 0;
|
---|
1541 | r := 0;
|
---|
1542 | g := 0;
|
---|
1543 | b := 0;
|
---|
1544 | w := 0;
|
---|
1545 | for n := 0 to high(FColors) do
|
---|
1546 | with FColors[n].Color do
|
---|
1547 | begin
|
---|
1548 | cura := (alpha / 255)*FColors[n].Weight;
|
---|
1549 | a += cura;
|
---|
1550 | r += GammaExpansionTab[red] * cura;
|
---|
1551 | g += GammaExpansionTab[green] * cura;
|
---|
1552 | b += GammaExpansionTab[blue] * cura;
|
---|
1553 | w += FColors[n].Weight;
|
---|
1554 | end;
|
---|
1555 | if w = 0 then
|
---|
1556 | Result := BGRAPixelTransparent
|
---|
1557 | else
|
---|
1558 | begin
|
---|
1559 | result.alpha := round(a*255/w);
|
---|
1560 | if result.alpha = 0 then result := BGRAPixelTransparent
|
---|
1561 | else
|
---|
1562 | begin
|
---|
1563 | result.red := GammaCompressionTab[round(r / a)];
|
---|
1564 | result.green := GammaCompressionTab[round(g / a)];
|
---|
1565 | result.blue := GammaCompressionTab[round(b / a)];
|
---|
1566 | end;
|
---|
1567 | end;
|
---|
1568 | end;
|
---|
1569 |
|
---|
1570 | function TBGRAColorBox.GetAverageColorOrMainColor: TBGRAPixel;
|
---|
1571 | var i: integer;
|
---|
1572 | maxWeight: UInt32;
|
---|
1573 | begin
|
---|
1574 | result := BGRAPixelTransparent;
|
---|
1575 | maxWeight:= 0;
|
---|
1576 | for i := 0 to high(FColors) do
|
---|
1577 | with FColors[i] do
|
---|
1578 | begin
|
---|
1579 | if Weight > maxWeight then
|
---|
1580 | begin
|
---|
1581 | maxWeight:= Weight;
|
---|
1582 | result := Color;
|
---|
1583 | end;
|
---|
1584 | end;
|
---|
1585 | if maxWeight <= 3*FTotalWeight shr 2 then
|
---|
1586 | result := GetAverageColor;
|
---|
1587 | end;
|
---|
1588 |
|
---|
1589 | function TBGRAColorBox.GetBounds(ADimension: TColorDimension): TDimensionMinMax;
|
---|
1590 | begin
|
---|
1591 | result := FBounds[ADimension];
|
---|
1592 | end;
|
---|
1593 |
|
---|
1594 | function TBGRAColorBox.GetColorCount(ACountPureTransparent: boolean): integer;
|
---|
1595 | begin
|
---|
1596 | result := length(FColors);
|
---|
1597 | if ACountPureTransparent and HasPureTransparentColor then inc(result);
|
---|
1598 | end;
|
---|
1599 |
|
---|
1600 | function TBGRAColorBox.GetHasPureTransparentColor: boolean;
|
---|
1601 | begin
|
---|
1602 | result := FPureTransparentColorCount > 0;
|
---|
1603 | end;
|
---|
1604 |
|
---|
1605 | function TBGRAColorBox.GetInferiorColor: TBGRAPixel;
|
---|
1606 | var
|
---|
1607 | n: integer;
|
---|
1608 | r, g, b, a: double;
|
---|
1609 | w: UInt32;
|
---|
1610 | cura: double;
|
---|
1611 | wantedWeight: UInt32;
|
---|
1612 | begin
|
---|
1613 | a := 0;
|
---|
1614 | r := 0;
|
---|
1615 | g := 0;
|
---|
1616 | b := 0;
|
---|
1617 | w := 0;
|
---|
1618 | wantedWeight:= FTotalWeight div 10;
|
---|
1619 | for n := 0 to high(FColors) do
|
---|
1620 | with FColors[n].Color do
|
---|
1621 | begin
|
---|
1622 | cura := (alpha / 255)*FColors[n].Weight;
|
---|
1623 | a += cura;
|
---|
1624 | r += red * cura;
|
---|
1625 | g += green * cura;
|
---|
1626 | b += blue * cura;
|
---|
1627 | w += FColors[n].Weight;
|
---|
1628 | if w >= wantedWeight then break;
|
---|
1629 | end;
|
---|
1630 | if w = 0 then
|
---|
1631 | Result := BGRAPixelTransparent
|
---|
1632 | else
|
---|
1633 | begin
|
---|
1634 | result.alpha := round(a*255/w);
|
---|
1635 | if result.alpha = 0 then result := BGRAPixelTransparent
|
---|
1636 | else
|
---|
1637 | begin
|
---|
1638 | result.red := round(r / a);
|
---|
1639 | result.green := round(g / a);
|
---|
1640 | result.blue := round(b / a);
|
---|
1641 | end;
|
---|
1642 | end;
|
---|
1643 | end;
|
---|
1644 |
|
---|
1645 | function TBGRAColorBox.GetLargestApparentDimension: TColorDimension;
|
---|
1646 | var c: TColorDimension;
|
---|
1647 | curApparentInterval, maxApparentInterval: UInt32;
|
---|
1648 | begin
|
---|
1649 | c := succ(low(TColorDimension));
|
---|
1650 | result := c;
|
---|
1651 | maxApparentInterval:= ApparentInterval[c];
|
---|
1652 | while c < high(TColorDimension) do
|
---|
1653 | begin
|
---|
1654 | inc(c);
|
---|
1655 | curApparentInterval:= ApparentInterval[c];
|
---|
1656 | if curApparentInterval > maxApparentInterval then
|
---|
1657 | begin
|
---|
1658 | maxApparentInterval:= curApparentInterval;
|
---|
1659 | result := c;
|
---|
1660 | end;
|
---|
1661 | end;
|
---|
1662 | end;
|
---|
1663 |
|
---|
1664 | function TBGRAColorBox.GetLargestApparentInterval: UInt32;
|
---|
1665 | var
|
---|
1666 | curApparentInterval: UInt32;
|
---|
1667 | c: TColorDimension;
|
---|
1668 | begin
|
---|
1669 | result:= ApparentInterval[succ(low(TColorDimension))];
|
---|
1670 | for c := succ(succ(low(TColorDimension))) to high(TColorDimension) do
|
---|
1671 | begin
|
---|
1672 | curApparentInterval:= ApparentInterval[c];
|
---|
1673 | if curApparentInterval > result then
|
---|
1674 | result := curApparentInterval;
|
---|
1675 | end;
|
---|
1676 | end;
|
---|
1677 |
|
---|
1678 | function TBGRAColorBox.GetPointLike: boolean;
|
---|
1679 | var c: TColorDimension;
|
---|
1680 | begin
|
---|
1681 | for c := succ(low(TColorDimension)) to high(TColorDimension) do
|
---|
1682 | if not FBounds[c].PointLike then
|
---|
1683 | begin
|
---|
1684 | result := false;
|
---|
1685 | exit;
|
---|
1686 | end;
|
---|
1687 | result := true;
|
---|
1688 | end;
|
---|
1689 |
|
---|
1690 | function TBGRAColorBox.GetSuperiorColor: TBGRAPixel;
|
---|
1691 | var
|
---|
1692 | n: integer;
|
---|
1693 | r, g, b, a: double;
|
---|
1694 | w: UInt32;
|
---|
1695 | cura: double;
|
---|
1696 | wantedWeight: UInt32;
|
---|
1697 | begin
|
---|
1698 | a := 0;
|
---|
1699 | r := 0;
|
---|
1700 | g := 0;
|
---|
1701 | b := 0;
|
---|
1702 | w := 0;
|
---|
1703 | wantedWeight:= FTotalWeight div 10;
|
---|
1704 | for n := high(FColors) downto 0 do
|
---|
1705 | with FColors[n].Color do
|
---|
1706 | begin
|
---|
1707 | cura := (alpha / 255)*FColors[n].Weight;
|
---|
1708 | a += cura;
|
---|
1709 | r += red * cura;
|
---|
1710 | g += green * cura;
|
---|
1711 | b += blue * cura;
|
---|
1712 | w += FColors[n].Weight;
|
---|
1713 | if w >= wantedWeight then break;
|
---|
1714 | end;
|
---|
1715 | if w = 0 then
|
---|
1716 | Result := BGRAPixelTransparent
|
---|
1717 | else
|
---|
1718 | begin
|
---|
1719 | result.alpha := round(a*255/w);
|
---|
1720 | if result.alpha = 0 then result := BGRAPixelTransparent
|
---|
1721 | else
|
---|
1722 | begin
|
---|
1723 | result.red := round(r / a);
|
---|
1724 | result.green := round(g / a);
|
---|
1725 | result.blue := round(b / a);
|
---|
1726 | end;
|
---|
1727 | end;
|
---|
1728 | end;
|
---|
1729 |
|
---|
1730 | procedure TBGRAColorBox.Init(AColors: ArrayOfWeightedColor; AOwner: boolean);
|
---|
1731 | var
|
---|
1732 | i,idx: NativeInt;
|
---|
1733 | FirstColor: boolean;
|
---|
1734 | c: TColorDimension;
|
---|
1735 | begin
|
---|
1736 | FPureTransparentColorCount:= 0;
|
---|
1737 | FTotalWeight:= 0;
|
---|
1738 | for c := low(TColorDimension) to high(TColorDimension) do
|
---|
1739 | FBounds[c].SetAsPoint(0);
|
---|
1740 | FirstColor := True;
|
---|
1741 | if AOwner then
|
---|
1742 | FColors := AColors
|
---|
1743 | else
|
---|
1744 | SetLength(FColors, length(AColors));
|
---|
1745 | idx := 0;
|
---|
1746 | for i := 0 to high(AColors) do
|
---|
1747 | with AColors[i] do
|
---|
1748 | begin
|
---|
1749 | if Color.alpha > 0 then
|
---|
1750 | begin
|
---|
1751 | if FirstColor then
|
---|
1752 | begin
|
---|
1753 | for c := low(TColorDimension) to high(TColorDimension) do
|
---|
1754 | FBounds[c].SetAsPoint(GetDimensionValue(Color,c));
|
---|
1755 | FirstColor := false;
|
---|
1756 | end else
|
---|
1757 | begin
|
---|
1758 | for c := low(TColorDimension) to high(TColorDimension) do
|
---|
1759 | FBounds[c].GrowToInclude(GetDimensionValue(Color,c));
|
---|
1760 | end;
|
---|
1761 | inc(FTotalWeight, Weight);
|
---|
1762 | if not AOwner or (idx <> i) then
|
---|
1763 | FColors[idx] := AColors[i];
|
---|
1764 | inc(idx);
|
---|
1765 | end else
|
---|
1766 | inc(FPureTransparentColorCount, Weight);
|
---|
1767 | end;
|
---|
1768 | setlength(FColors,idx);
|
---|
1769 | end;
|
---|
1770 |
|
---|
1771 | procedure TBGRAColorBox.SortBy(ADimension: TColorDimension);
|
---|
1772 | var comparer: TIsChannelStrictlyGreaterFunc;
|
---|
1773 | begin
|
---|
1774 | comparer := GetPixelStrictComparer(ADimension);
|
---|
1775 | if comparer = nil then exit;
|
---|
1776 | ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),comparer)
|
---|
1777 | end;
|
---|
1778 |
|
---|
1779 | function TBGRAColorBox.GetMedianIndex(ADimension: TColorDimension;
|
---|
1780 | AMinValue, AMaxValue: UInt32
|
---|
1781 | ): integer;
|
---|
1782 | var i: integer;
|
---|
1783 | sum,goal: UInt32;
|
---|
1784 | valueComparer: TIsChannelGreaterThanOrEqualToValueFunc;
|
---|
1785 | strictComparer: TIsChannelStrictlyGreaterFunc;
|
---|
1786 | ofs: integer;
|
---|
1787 | begin
|
---|
1788 | if length(FColors) = 1 then
|
---|
1789 | begin
|
---|
1790 | result := 0;
|
---|
1791 | exit;
|
---|
1792 | end else
|
---|
1793 | if length(FColors) = 0 then
|
---|
1794 | begin
|
---|
1795 | result := -1;
|
---|
1796 | exit;
|
---|
1797 | end;
|
---|
1798 | valueComparer:= GetPixelValueComparer(ADimension);
|
---|
1799 | sum := 0;
|
---|
1800 | goal := (FTotalWeight+1) shr 1;
|
---|
1801 | result := high(FColors) shr 1;
|
---|
1802 | for i := 0 to high(FColors) do
|
---|
1803 | begin
|
---|
1804 | inc(sum, FColors[i].Weight);
|
---|
1805 | if (sum>=goal) and (valueComparer(@FColors[i].Color, AMinValue)) then
|
---|
1806 | begin
|
---|
1807 | result := i;
|
---|
1808 | while (result > 0) and (valueComparer(@FColors[result].Color, AMaxValue+1)) do dec(result);
|
---|
1809 | break;
|
---|
1810 | end;
|
---|
1811 | end;
|
---|
1812 | if result = 0 then inc(result);
|
---|
1813 | //check that there it is not splitting consecutive colors with the same value
|
---|
1814 | strictComparer := GetPixelStrictComparer(ADimension);
|
---|
1815 | ofs := 0;
|
---|
1816 | while true do
|
---|
1817 | begin
|
---|
1818 | if (result-ofs < 1) and (result+ofs > high(FColors)) then break;
|
---|
1819 | if (result-ofs >= 1) and strictComparer(@FColors[result-ofs].Color,@FColors[result-ofs-1].Color) then
|
---|
1820 | begin
|
---|
1821 | result := result-ofs;
|
---|
1822 | exit;
|
---|
1823 | end;
|
---|
1824 | if (result+ofs <= high(FColors)) and strictComparer(@FColors[result+ofs].Color,@FColors[result+ofs-1].Color) then
|
---|
1825 | begin
|
---|
1826 | result := result+ofs;
|
---|
1827 | exit;
|
---|
1828 | end;
|
---|
1829 | inc(ofs);
|
---|
1830 | end;
|
---|
1831 | end;
|
---|
1832 |
|
---|
1833 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; AColors: ArrayOfWeightedColor; AOwner: boolean);
|
---|
1834 | begin
|
---|
1835 | FDimensions:= ADimensions;
|
---|
1836 | Init(AColors,AOwner);
|
---|
1837 | end;
|
---|
1838 |
|
---|
1839 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions;
|
---|
1840 | const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette);
|
---|
1841 | var weightedColors: ArrayOfWeightedColor;
|
---|
1842 | i: Integer;
|
---|
1843 | begin
|
---|
1844 | if AAlpha = acFullChannelInPalette then
|
---|
1845 | begin
|
---|
1846 | FDimensions:= ADimensions;
|
---|
1847 | setlength(weightedColors, length(AColors));
|
---|
1848 | for i := 0 to high(weightedColors) do
|
---|
1849 | with weightedColors[i] do
|
---|
1850 | begin
|
---|
1851 | color := AColors[i];
|
---|
1852 | Weight:= 1;
|
---|
1853 | end;
|
---|
1854 | Init(weightedColors,True);
|
---|
1855 | end else
|
---|
1856 | Create(ADimensions, @AColors[0], length(AColors), AAlpha);
|
---|
1857 | end;
|
---|
1858 |
|
---|
1859 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; ABounds: TColorBoxBounds);
|
---|
1860 | begin
|
---|
1861 | FDimensions:= ADimensions;
|
---|
1862 | FBounds := ABounds;
|
---|
1863 | FTotalWeight:= 0;
|
---|
1864 | FPureTransparentColorCount:= 0;
|
---|
1865 | end;
|
---|
1866 |
|
---|
1867 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette);
|
---|
1868 | begin
|
---|
1869 | FDimensions:= ADimensions;
|
---|
1870 | Init(APalette.GetAsArrayOfWeightedColor,False);
|
---|
1871 | end;
|
---|
1872 |
|
---|
1873 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions;
|
---|
1874 | ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption);
|
---|
1875 | begin
|
---|
1876 | Create(ADimensions, ABitmap.Data, ABitmap.NbPixels, AAlpha);
|
---|
1877 | end;
|
---|
1878 |
|
---|
1879 | constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption);
|
---|
1880 | var i,j,prev,idx: integer;
|
---|
1881 | p: PBGRAPixel;
|
---|
1882 | skip: boolean;
|
---|
1883 | alphaMask: DWord;
|
---|
1884 | transpIndex: integer;
|
---|
1885 | begin
|
---|
1886 | if AAlpha <> acFullChannelInPalette then
|
---|
1887 | alphaMask := LEtoN($FF000000)
|
---|
1888 | else
|
---|
1889 | alphaMask := 0;
|
---|
1890 | FDimensions:= ADimensions;
|
---|
1891 | transpIndex := -1;
|
---|
1892 | SetLength(FColors,ANbPixels);
|
---|
1893 | if length(FColors)>0 then
|
---|
1894 | begin
|
---|
1895 | p := AColors;
|
---|
1896 | idx := 0;
|
---|
1897 | for i := 0 to ANbPixels-1 do
|
---|
1898 | begin
|
---|
1899 | if (p^.alpha = 0) or ((AAlpha = acTransparentEntry) and (p^.alpha < 128)) then
|
---|
1900 | begin
|
---|
1901 | skip := true;
|
---|
1902 | if not (AAlpha = acIgnore) then
|
---|
1903 | begin
|
---|
1904 | if (transpIndex=-1) then
|
---|
1905 | begin
|
---|
1906 | transpIndex := idx;
|
---|
1907 | with FColors[idx] do
|
---|
1908 | begin
|
---|
1909 | Color := BGRAPixelTransparent;
|
---|
1910 | Weight:= 1;
|
---|
1911 | end;
|
---|
1912 | inc(idx);
|
---|
1913 | end else
|
---|
1914 | inc(FColors[transpIndex].Weight);
|
---|
1915 | end;
|
---|
1916 | if (p^.alpha = 0) then
|
---|
1917 | begin
|
---|
1918 | inc(p);
|
---|
1919 | continue;
|
---|
1920 | end;
|
---|
1921 | end;
|
---|
1922 | skip := false;
|
---|
1923 | for j := idx-1 downto idx-10 do
|
---|
1924 | if j < 0 then
|
---|
1925 | break
|
---|
1926 | else
|
---|
1927 | with FColors[j] do
|
---|
1928 | if DWord(Color)=DWord(p^) or alphaMask then
|
---|
1929 | begin
|
---|
1930 | skip := true;
|
---|
1931 | inc(Weight);
|
---|
1932 | break;
|
---|
1933 | end;
|
---|
1934 | if skip then
|
---|
1935 | begin
|
---|
1936 | inc(p);
|
---|
1937 | continue;
|
---|
1938 | end;
|
---|
1939 | with FColors[idx] do
|
---|
1940 | begin
|
---|
1941 | Color := p^;
|
---|
1942 | if AAlpha <> acFullChannelInPalette then Color.alpha := 255;
|
---|
1943 | Weight := 1;
|
---|
1944 | inc(p);
|
---|
1945 | inc(idx);
|
---|
1946 | end;
|
---|
1947 | end;
|
---|
1948 | setLength(FColors, idx);
|
---|
1949 |
|
---|
1950 | ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),@IsDWordGreater);
|
---|
1951 | prev := 0;
|
---|
1952 | for i := 1 to high(FColors) do
|
---|
1953 | begin
|
---|
1954 | if DWord(FColors[i].Color)=DWord(FColors[prev].Color) then
|
---|
1955 | inc(FColors[prev].Weight, FColors[i].Weight)
|
---|
1956 | else
|
---|
1957 | begin
|
---|
1958 | inc(prev);
|
---|
1959 | if i <> prev then
|
---|
1960 | FColors[prev] := FColors[i];
|
---|
1961 | end;
|
---|
1962 | end;
|
---|
1963 | setlength(FColors, prev+1);
|
---|
1964 | end;
|
---|
1965 | Init(FColors,True);
|
---|
1966 | end;
|
---|
1967 |
|
---|
1968 | function TBGRAColorBox.BoundsContain(AColor: TBGRAPixel): boolean;
|
---|
1969 | var c: TColorDimension;
|
---|
1970 | begin
|
---|
1971 | for c := succ(low(TColorDimension)) to high(TColorDimension) do
|
---|
1972 | if not FBounds[c].Contains(GetDimensionValue(AColor,c)) then
|
---|
1973 | begin
|
---|
1974 | result := false;
|
---|
1975 | exit;
|
---|
1976 | end;
|
---|
1977 | result := true;
|
---|
1978 | end;
|
---|
1979 |
|
---|
1980 | function TBGRAColorBox.MedianCut(ADimension: TColorDimension; out SuperiorMiddle: UInt32
|
---|
1981 | ): TBGRAColorBox;
|
---|
1982 | var idxSplit: NativeInt;
|
---|
1983 | secondArray: ArrayOfWeightedColor;
|
---|
1984 | i: NativeInt;
|
---|
1985 | begin
|
---|
1986 | result := nil;
|
---|
1987 | SuperiorMiddle := 0;
|
---|
1988 | if FBounds[ADimension].PointLike then exit;
|
---|
1989 | if length(FColors) <= 1 then exit;
|
---|
1990 | SortBy(ADimension);
|
---|
1991 | idxSplit := GetMedianIndex(ADimension,
|
---|
1992 | round(FBounds[ADimension].Minimum*(1-MedianMinPercentage)+FBounds[ADimension].Maximum*MedianMinPercentage),
|
---|
1993 | round(FBounds[ADimension].Minimum*MedianMinPercentage+FBounds[ADimension].Maximum*(1-MedianMinPercentage)));
|
---|
1994 | if idxSplit = -1 then exit;
|
---|
1995 | setlength(secondArray, length(FColors)-idxSplit);
|
---|
1996 | for i := idxSplit to high(FColors) do
|
---|
1997 | secondArray[i-idxSplit] := FColors[i];
|
---|
1998 | result := TBGRAColorBox.Create(FDimensions, secondArray,True);
|
---|
1999 | setlength(FColors, idxSplit);
|
---|
2000 | Init(FColors,True);
|
---|
2001 | SuperiorMiddle := (FBounds[ADimension].Maximum + result.FBounds[ADimension].Minimum + 1) shr 1;
|
---|
2002 | end;
|
---|
2003 |
|
---|
2004 | function TBGRAColorBox.Duplicate: TBGRAColorBox;
|
---|
2005 | var
|
---|
2006 | i: NativeInt;
|
---|
2007 | begin
|
---|
2008 | result := TBGRAColorBox.Create(FDimensions, FBounds);
|
---|
2009 | result.FTotalWeight := FTotalWeight;
|
---|
2010 | setlength(result.FColors, length(FColors));
|
---|
2011 | for i := 0 to high(FColors) do
|
---|
2012 | result.FColors[i] := FColors[i];
|
---|
2013 | end;
|
---|
2014 |
|
---|
2015 | function TBGRAColorBox.GetAsArrayOfColors(AIncludePureTransparent: boolean): ArrayOfTBGRAPixel;
|
---|
2016 | var i,idx: integer;
|
---|
2017 | begin
|
---|
2018 | if AIncludePureTransparent and HasPureTransparentColor then
|
---|
2019 | begin
|
---|
2020 | setlength(result, length(FColors)+1);
|
---|
2021 | result[0] := BGRAPixelTransparent;
|
---|
2022 | idx := 1;
|
---|
2023 | end else
|
---|
2024 | begin
|
---|
2025 | setlength(result, length(FColors));
|
---|
2026 | idx := 0;
|
---|
2027 | end;
|
---|
2028 | for i:= 0 to high(FColors) do
|
---|
2029 | begin
|
---|
2030 | result[idx] := FColors[i].Color;
|
---|
2031 | inc(idx);
|
---|
2032 | end;
|
---|
2033 | end;
|
---|
2034 |
|
---|
2035 | end.
|
---|
2036 |
|
---|