source: trunk/Packages/bgrabitmap/bgrapalette.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 38.6 KB
Line 
1unit BGRAPalette;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, AvgLvlTree, BGRABitmapTypes, FPimage;
9
10const
11 MaxLastAddedColors = 10;
12
13type
14 TBGRAPaletteFormat = integer;
15
16const
17 palUnknown : TBGRAPaletteFormat = 0;
18 palPaintDotNet : TBGRAPaletteFormat = 1;
19 palGimp : TBGRAPaletteFormat = 2;
20 palAdobeSwatchExchange : TBGRAPaletteFormat = 3;
21 palKOffice : TBGRAPaletteFormat = 4;
22 palJascPSP : TBGRAPaletteFormat = 5;
23 palCustom : TBGRAPaletteFormat = 100;
24
25type
26 TBGRAIndexedPaletteEntry = packed record
27 Color: TBGRAPixel;
28 Index: UInt32;
29 end;
30 PBGRAIndexedPaletteEntry = ^TBGRAIndexedPaletteEntry;
31 TBGRAWeightedPaletteEntry = packed record
32 Color: TBGRAPixel;
33 Weight: UInt32;
34 end;
35 PBGRAWeightedPaletteEntry = ^TBGRAWeightedPaletteEntry;
36 ArrayOfWeightedColor = array of TBGRAWeightedPaletteEntry;
37
38 TBGRAPixelComparer = function (p1,p2 : PBGRAPixel): boolean;
39
40 { TBGRACustomPalette }
41
42 TBGRACustomPalette = class
43 private
44 function GetDominantColor: TBGRAPixel;
45 protected
46 function GetCount: integer; virtual; abstract;
47 function GetColorByIndex(AIndex: integer): TBGRAPixel; virtual; abstract;
48 public
49 function ContainsColor(AValue: TBGRAPixel): boolean; virtual; abstract;
50 function IndexOfColor(AValue: TBGRAPixel): integer; virtual; abstract;
51 function GetAsArrayOfColor: ArrayOfTBGRAPixel; virtual; abstract;
52 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; virtual; abstract;
53 procedure AssignTo(AImage: TFPCustomImage); overload;
54 procedure AssignTo(APalette: TFPPalette); overload;
55 property DominantColor: TBGRAPixel read GetDominantColor;
56 property Count: integer read GetCount;
57 property Color[AIndex: integer]: TBGRAPixel read GetColorByIndex;
58 end;
59
60 { TBGRAAvgLvlPalette }
61
62 TBGRAAvgLvlPalette = class(TBGRACustomPalette)
63 protected
64 FTree: TAvgLvlTree;
65 FArray: array of PBGRAPixel;
66 FLastAddedColors: packed array[0..MaxLastAddedColors-1] of PBGRAPixel;
67 FLastAddedColorCount: integer;
68 function GetCount: integer; override;
69 function GetColorByIndex(AIndex: integer): TBGRAPixel; override;
70 function OnCompareItems({%H-}Tree: TAvgLvlTree; Data1, Data2: Pointer): integer; virtual;
71 procedure FreeEntry(AEntry: PBGRAPixel); virtual; abstract;
72 procedure NeedArray; virtual;
73 procedure ClearArray; virtual;
74 procedure AddLastColor(AColor: PBGRAPixel);
75 function GetLastColor(AValue: TBGRAPixel): PBGRAPixel;
76 procedure ClearLastColors;
77 public
78 constructor Create; overload;
79 function ContainsColor(AValue: TBGRAPixel): boolean; override;
80 function IndexOfColor(AValue: TBGRAPixel): integer; override;
81 procedure Clear; virtual;
82 destructor Destroy; override;
83 function GetAsArrayOfColor: ArrayOfTBGRAPixel; override;
84 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
85 end;
86
87 { TBGRAPalette }
88
89 TBGRAPalette = class(TBGRAAvgLvlPalette)
90 protected
91 function CreateEntry(AColor: TBGRAPixel): PBGRAPixel; virtual;
92 procedure FreeEntry(AEntry: PBGRAPixel); override;
93 procedure IncludePixel(PPixel: PBGRAPixel); virtual;
94 procedure ExceptionUnknownPaletteFormat;
95 procedure ExceptionInvalidPaletteFormat;
96 public
97 constructor Create(ABitmap: TBGRACustomBitmap); overload; virtual;
98 constructor Create(APalette: TBGRACustomPalette); overload; virtual;
99 constructor Create(AColors: ArrayOfTBGRAPixel); overload; virtual;
100 constructor Create(AColors: ArrayOfWeightedColor); overload; virtual;
101 function AddColor(AValue: TBGRAPixel): boolean; virtual;
102 procedure AddColors(ABitmap: TBGRACustomBitmap); overload; virtual;
103 procedure AddColors(APalette: TBGRACustomPalette); overload; virtual;
104 function RemoveColor(AValue: TBGRAPixel): boolean; virtual;
105 procedure LoadFromFile(AFilenameUTF8: string); virtual;
106 procedure LoadFromStream(AStream: TStream; AFormat: TBGRAPaletteFormat); virtual;
107 procedure LoadFromResource(AFilename: string; AFormat: TBGRAPaletteFormat);
108 procedure SaveToFile(AFilenameUTF8: string); virtual;
109 procedure SaveToStream(AStream: TStream; AFormat: TBGRAPaletteFormat); virtual;
110 function DetectPaletteFormat(AStream: TStream): TBGRAPaletteFormat; overload; virtual;
111 function DetectPaletteFormat(AFilenameUTF8: string): TBGRAPaletteFormat; overload;
112 function SuggestPaletteFormat(AFilenameUTF8: string): TBGRAPaletteFormat; virtual;
113 end;
114
115 { TBGRAIndexedPalette }
116
117 TBGRAIndexedPalette = class(TBGRAPalette)
118 private
119 FCurrentIndex: UInt32;
120 protected
121 procedure NeedArray; override;
122 function CreateEntry(AColor: TBGRAPixel): PBGRAPixel; override;
123 procedure FreeEntry(AEntry: PBGRAPixel); override;
124 public
125 function RemoveColor({%H-}AValue: TBGRAPixel): boolean; override;
126 function IndexOfColor(AValue: TBGRAPixel): integer; override;
127 procedure Clear; override;
128 end;
129
130 { TBGRAWeightedPalette }
131
132 TBGRAWeightedPalette = class(TBGRAPalette)
133 private
134 protected
135 function CreateEntry(AColor: TBGRAPixel): PBGRAPixel; override;
136 procedure FreeEntry(AEntry: PBGRAPixel); override;
137 function GetWeightByIndex(AIndex: Integer): UInt32; virtual;
138 procedure IncludePixel(PPixel: PBGRAPixel); override;
139 public
140 constructor Create(AColors: ArrayOfWeightedColor); override;
141 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
142 function IncColor(AValue: TBGRAPixel; out NewWeight: UInt32): boolean;
143 function DecColor(AValue: TBGRAPixel; out NewWeight: UInt32): boolean;
144 property Weight[AIndex: Integer]: UInt32 read GetWeightByIndex;
145 end;
146
147 { TBGRAReferencePalette }
148
149 TBGRAReferencePalette = class(TBGRAAvgLvlPalette)
150 protected
151 procedure FreeEntry({%H-}AEntry: PBGRAPixel); override;
152 public
153 function AddColor(AValue: PBGRAPixel): boolean;
154 function RemoveColor(AValue: PBGRAPixel): boolean;
155 end;
156
157 { TBGRACustomApproxPalette }
158
159 TBGRACustomApproxPalette = class(TBGRACustomPalette)
160 private
161 function FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel; inline;
162 function FindNearestColorIndexIgnoreAlpha(AValue: TBGRAPixel): integer; inline;
163 protected
164 function GetWeightByIndex({%H-}AIndex: Integer): UInt32; virtual;
165 public
166 function FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel; overload;
167 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; overload; virtual; abstract;
168 function FindNearestColorIndex(AValue: TBGRAPixel; AIgnoreAlpha: boolean): integer; overload;
169 function FindNearestColorIndex(AValue: TBGRAPixel): integer; overload; virtual; abstract;
170 property Weight[AIndex: Integer]: UInt32 read GetWeightByIndex;
171 end;
172
173 { TBGRA16BitPalette }
174
175 TBGRA16BitPalette = class(TBGRACustomApproxPalette)
176 protected
177 function GetCount: integer; override;
178 function GetColorByIndex(AIndex: integer): TBGRAPixel; override;
179 public
180 function ContainsColor(AValue: TBGRAPixel): boolean; override;
181 function IndexOfColor(AValue: TBGRAPixel): integer; override;
182 function GetAsArrayOfColor: ArrayOfTBGRAPixel; override;
183 function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
184 function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override;
185 function FindNearestColorIndex(AValue: TBGRAPixel): integer; override;
186 end;
187
188 { TBGRACustomColorQuantizer }
189
190 TBGRACustomColorQuantizer = class
191 protected
192 function GetDominantColor: TBGRAPixel; virtual;
193 function GetPalette: TBGRACustomApproxPalette; virtual; abstract;
194 function GetSourceColor(AIndex: integer): TBGRAPixel; virtual; abstract;
195 function GetSourceColorCount: Integer; virtual; abstract;
196 function GetReductionColorCount: integer; virtual; abstract;
197 procedure SetReductionColorCount(AValue: Integer); virtual; abstract;
198 public
199 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); overload; virtual; abstract;
200 constructor Create(AColors: array of TBGRAPixel; ASeparateAlphaChannel: boolean); overload;
201 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload; virtual; abstract;
202 constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); overload; virtual; abstract;
203 constructor Create(AColors: array of TBGRAPixel; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); overload;
204 constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); overload; virtual; abstract;
205 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); overload; virtual; abstract;
206 procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap); overload;
207 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; overload; virtual; abstract;
208 function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; overload;
209 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string); overload;
210 procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string; AFormat: TBGRAImageFormat); overload;
211 procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); virtual; abstract;
212 function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; overload;
213 function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): Pointer; overload;
214 function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm;
215 ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; overload; virtual; abstract;
216 property SourceColorCount: Integer read GetSourceColorCount;
217 property SourceColor[AIndex: integer]: TBGRAPixel read GetSourceColor;
218 property ReductionColorCount: Integer read GetReductionColorCount write SetReductionColorCount;
219 property ReducedPalette: TBGRACustomApproxPalette read GetPalette;
220 property DominantColor: TBGRAPixel read GetDominantColor;
221 end;
222
223 TBGRAColorQuantizerAny = class of TBGRACustomColorQuantizer;
224
225var
226 BGRAColorQuantizerFactory: TBGRAColorQuantizerAny;
227
228function BGRARequiredBitDepth(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption): integer; overload;
229function BGRARequiredBitDepth(APalette: TBGRACustomPalette): integer; overload;
230
231type
232 TPaletteReaderProc = function(APalette: TBGRAPalette; AStream: TStream): boolean;
233 TPaletteWriterProc = procedure(APalette: TBGRAPalette; AStream: TStream);
234 TCheckPaletteFormatProc = function(ABuf256: string): boolean;
235
236procedure BGRARegisterPaletteFormat(AFormatIndex: TBGRAPaletteFormat; AExtension: string; ADescription: string;
237 AReadProc: TPaletteReaderProc; AWriteProc: TPaletteWriterProc; ACheckFormatProc: TCheckPaletteFormatProc);
238function BGRARegisteredPaletteFormatFilter(AAllSupportedDescription: string) : string;
239
240procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex,
241 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
242
243procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex,
244 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
245
246procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
247 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
248
249procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
250 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
251
252implementation
253
254uses BGRAUTF8, bufstream;
255
256function IsDWordGreater(p1, p2: PBGRAPixel): boolean;
257begin
258 result := DWord(p1^) > DWord(p2^);
259end;
260
261const
262 InsertionSortLimit = 10;
263
264procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex,
265 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
266var i,j,insertPos: NativeInt;
267 compared: TBGRAWeightedPaletteEntry;
268begin
269 if AComparer = nil then AComparer := @IsDWordGreater;
270 for i := AMinIndex+1 to AMaxIndex do
271 begin
272 insertPos := i;
273 compared := AColors[i];
274 while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1].Color,@compared.Color) do
275 dec(insertPos);
276 if insertPos <> i then
277 begin
278 for j := i downto insertPos+1 do
279 AColors[j] := AColors[j-1];
280 AColors[insertPos] := compared;
281 end;
282 end;
283end;
284
285procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex,
286 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
287var Pivot: TBGRAPixel;
288 CurMin,CurMax,i : NativeInt;
289
290 procedure Swap(a,b: NativeInt);
291 var Temp: TBGRAWeightedPaletteEntry;
292 begin
293 if a = b then exit;
294 Temp := AColors[a];
295 AColors[a] := AColors[b];
296 AColors[b] := Temp;
297 end;
298begin
299 if AComparer = nil then AComparer := @IsDWordGreater;
300 if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then
301 begin
302 ArrayOfWeightedColor_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer);
303 exit;
304 end;
305 Pivot := AColors[(AMinIndex+AMaxIndex) shr 1].Color;
306 CurMin := AMinIndex;
307 CurMax := AMaxIndex;
308 i := CurMin;
309 while i < CurMax do
310 begin
311 if AComparer(@AColors[i].Color, @Pivot) then
312 begin
313 Swap(i, CurMax);
314 dec(CurMax);
315 end else
316 begin
317 if AComparer(@Pivot, @AColors[i].Color) then
318 begin
319 Swap(i, CurMin);
320 inc(CurMin);
321 end;
322 inc(i);
323 end;
324 end;
325 if AComparer(@Pivot, @AColors[i].Color) then
326 begin
327 Swap(i, CurMin);
328 inc(CurMin);
329 end;
330 if CurMin > AMinIndex then ArrayOfWeightedColor_QuickSort(AColors,AMinIndex,CurMin,AComparer);
331 if CurMax < AMaxIndex then ArrayOfWeightedColor_QuickSort(AColors,CurMax,AMaxIndex,AComparer);
332end;
333
334procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
335 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
336var i,j,insertPos: NativeInt;
337 compared: TBGRAPixel;
338begin
339 if AComparer = nil then AComparer := @IsDWordGreater;
340 for i := AMinIndex+1 to AMaxIndex do
341 begin
342 insertPos := i;
343 compared := AColors[i];
344 while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1],@compared) do
345 dec(insertPos);
346 if insertPos <> i then
347 begin
348 for j := i downto insertPos+1 do
349 AColors[j] := AColors[j-1];
350 AColors[insertPos] := compared;
351 end;
352 end;
353end;
354
355procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
356 AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
357var Pivot: TBGRAPixel;
358 CurMin,CurMax,i : NativeInt;
359
360 procedure Swap(a,b: NativeInt);
361 var Temp: TBGRAPixel;
362 begin
363 if a = b then exit;
364 Temp := AColors[a];
365 AColors[a] := AColors[b];
366 AColors[b] := Temp;
367 end;
368begin
369 if AComparer = nil then AComparer := @IsDWordGreater;
370 if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then
371 begin
372 ArrayOfTBGRAPixel_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer);
373 exit;
374 end;
375 Pivot := AColors[(AMinIndex+AMaxIndex) shr 1];
376 CurMin := AMinIndex;
377 CurMax := AMaxIndex;
378 i := CurMin;
379 while i < CurMax do
380 begin
381 if AComparer(@AColors[i], @Pivot) then
382 begin
383 Swap(i, CurMax);
384 dec(CurMax);
385 end else
386 begin
387 if AComparer(@Pivot, @AColors[i]) then
388 begin
389 Swap(i, CurMin);
390 inc(CurMin);
391 end;
392 inc(i);
393 end;
394 end;
395 if AComparer(@Pivot, @AColors[i]) then
396 begin
397 Swap(i, CurMin);
398 inc(CurMin);
399 end;
400 if CurMin > AMinIndex then ArrayOfTBGRAPixel_QuickSort(AColors,AMinIndex,CurMin,AComparer);
401 if CurMax < AMaxIndex then ArrayOfTBGRAPixel_QuickSort(AColors,CurMax,AMaxIndex,AComparer);
402end;
403
404{$i paletteformats.inc}
405
406function BGRARequiredBitDepth(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption): integer;
407var
408 palette: TBGRAPalette;
409 p: PBGRAPixel;
410 i: NativeInt;
411 transparentEntry: boolean;
412begin
413 palette := TBGRAPalette.Create;
414 p := ABitmap.Data;
415 transparentEntry := false;
416 if AAlpha = acIgnore then
417 begin
418 for i := ABitmap.NbPixels-1 downto 0 do
419 begin
420 palette.AddColor(BGRA(p^.red,p^.green,p^.blue));
421 inc(p);
422 if palette.Count > 256 then break;
423 end;
424 end else
425 if AAlpha = acTransparentEntry then
426 begin
427 for i := ABitmap.NbPixels-1 downto 0 do
428 begin
429 if p^.alpha < 128 then
430 transparentEntry:= true
431 else
432 palette.AddColor(BGRA(p^.red,p^.green,p^.blue));
433 inc(p);
434 if palette.Count > 256 then break;
435 end;
436 end else
437 begin
438 for i := ABitmap.NbPixels-1 downto 0 do
439 begin
440 palette.AddColor(p^);
441 inc(p);
442 if palette.Count > 256 then break;
443 end;
444 end;
445
446 if palette.Count+byte(transparentEntry) > 256 then
447 begin
448 if (AAlpha = acFullChannelInPalette) and ABitmap.HasTransparentPixels then
449 result := 32
450 else
451 if (AAlpha = acTransparentEntry) and ABitmap.HasTransparentPixels then
452 result := 25
453 else
454 result := 24;
455 end else
456 begin
457 result := 8;
458 while (result > 0) and (1 shl (result shr 1) >= palette.Count) do result := result shr 1;
459 end;
460 palette.Free;
461end;
462
463function BGRARequiredBitDepth(APalette: TBGRACustomPalette): integer;
464var i: integer;
465 hasTransp: boolean;
466begin
467 if APalette.Count > 256 then
468 begin
469 hasTransp := false;
470 for i := 0 to APalette.Count-1 do
471 if APalette.Color[i].alpha <> 255 then
472 begin
473 hasTransp:= true;
474 break;
475 end;
476 if hasTransp then
477 result := 32
478 else
479 result := 24;
480 end else
481 begin
482 result := 8;
483 while (result > 0) and (1 shl (result shr 1) >= APalette.Count) do result := result shr 1;
484 end;
485end;
486
487{ TBGRA16BitPalette }
488
489function TBGRA16BitPalette.GetCount: integer;
490begin
491 result := 65537;
492end;
493
494function TBGRA16BitPalette.GetColorByIndex(AIndex: integer): TBGRAPixel;
495begin
496 if (AIndex >= 65536) or (AIndex < 0) then
497 result := BGRAPixelTransparent
498 else
499 result := Color16BitToBGRA(AIndex);
500end;
501
502function TBGRA16BitPalette.ContainsColor(AValue: TBGRAPixel): boolean;
503begin
504 if AValue.alpha = 0 then
505 result := true
506 else
507 result := (AValue.alpha = 255) and (FindNearestColor(AValue)=AValue);
508end;
509
510function TBGRA16BitPalette.IndexOfColor(AValue: TBGRAPixel): integer;
511var idx: integer;
512begin
513 if AValue.Alpha = 0 then
514 result := 65536
515 else
516 begin
517 idx := BGRAToColor16Bit(AValue);
518 if Color16BitToBGRA(idx)=AValue then
519 result := idx
520 else
521 result := -1;
522 end;
523end;
524
525function TBGRA16BitPalette.GetAsArrayOfColor: ArrayOfTBGRAPixel;
526begin
527 result := nil;
528 raise exception.Create('Palette too big');
529end;
530
531function TBGRA16BitPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
532begin
533 result := nil;
534 raise exception.Create('Palette too big');
535end;
536
537function TBGRA16BitPalette.FindNearestColor(AValue: TBGRAPixel): TBGRAPixel;
538begin
539 if AValue.alpha = 0 then result := BGRAPixelTransparent
540 else
541 result := GetColorByIndex(BGRAToColor16Bit(AValue));
542end;
543
544function TBGRA16BitPalette.FindNearestColorIndex(AValue: TBGRAPixel): integer;
545begin
546 result := BGRAToColor16Bit(AValue);
547end;
548
549{ TBGRAIndexedPalette }
550
551procedure TBGRAIndexedPalette.NeedArray;
552var Node: TAvgLvlTreeNode;
553 n: UInt32;
554begin
555 n := Count;
556 if UInt32(length(FArray)) <> n then
557 begin
558 setLength(FArray,n);
559 for Node in FTree do
560 with PBGRAIndexedPaletteEntry(Node.Data)^ do
561 begin
562 if Index < n then //index is unsigned so always >= 0
563 FArray[Index] := @Color;
564 end;
565 end;
566end;
567
568function TBGRAIndexedPalette.CreateEntry(AColor: TBGRAPixel): PBGRAPixel;
569begin
570 result := PBGRAPixel(GetMem(sizeOf(TBGRAIndexedPaletteEntry)));
571 result^ := AColor;
572 PBGRAIndexedPaletteEntry(result)^.Index := FCurrentIndex;
573 Inc(FCurrentIndex);
574end;
575
576procedure TBGRAIndexedPalette.FreeEntry(AEntry: PBGRAPixel);
577begin
578 FreeMem(AEntry);
579end;
580
581function TBGRAIndexedPalette.RemoveColor(AValue: TBGRAPixel): boolean;
582begin
583 Result:= false;
584 raise exception.Create('It is not possible to remove a color from an indexed palette');
585end;
586
587function TBGRAIndexedPalette.IndexOfColor(AValue: TBGRAPixel): integer;
588Var Node: TAvgLvlTreeNode;
589begin
590 Node := FTree.Find(@AValue);
591 if Assigned(Node) then
592 result := PBGRAIndexedPaletteEntry(Node.Data)^.Index
593 else
594 result := -1;
595end;
596
597procedure TBGRAIndexedPalette.Clear;
598begin
599 inherited Clear;
600 FCurrentIndex := 0;
601end;
602
603{ TBGRACustomColorQuantizer }
604
605function TBGRACustomColorQuantizer.GetDominantColor: TBGRAPixel;
606begin
607 result := ReducedPalette.DominantColor;
608end;
609
610constructor TBGRACustomColorQuantizer.Create(AColors: array of TBGRAPixel;
611 ASeparateAlphaChannel: boolean);
612var palette: TBGRAPalette;
613 i: Integer;
614begin
615 palette := TBGRAPalette.Create;
616 for i := 0 to high(AColors) do
617 palette.AddColor(AColors[i]);
618 Create(palette, ASeparateAlphaChannel);
619 palette.Free;
620end;
621
622constructor TBGRACustomColorQuantizer.Create(AColors: array of TBGRAPixel;
623 ASeparateAlphaChannel: boolean; AReductionColorCount: integer);
624var palette: TBGRAPalette;
625 i: Integer;
626begin
627 palette := TBGRAPalette.Create;
628 for i := 0 to high(AColors) do
629 palette.AddColor(AColors[i]);
630 Create(palette, ASeparateAlphaChannel, AReductionColorCount);
631 palette.Free;
632end;
633
634procedure TBGRACustomColorQuantizer.ApplyDitheringInplace(
635 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap);
636begin
637 ApplyDitheringInplace(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));
638end;
639
640function TBGRACustomColorQuantizer.GetDitheredBitmap(
641 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap
642 ): TBGRACustomBitmap;
643begin
644 result := GetDitheredBitmap(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));
645end;
646
647procedure TBGRACustomColorQuantizer.SaveBitmapToFile(
648 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
649 AFilenameUTF8: string);
650begin
651 SaveBitmapToFile(AAlgorithm, ABitmap, AFilenameUTF8, SuggestImageFormat(AFilenameUTF8));
652end;
653
654procedure TBGRACustomColorQuantizer.SaveBitmapToFile(
655 AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
656 AFilenameUTF8: string; AFormat: TBGRAImageFormat);
657var
658 stream: TFileStreamUTF8;
659begin
660 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmCreate);
661 try
662 SaveBitmapToStream(AAlgorithm, ABitmap, stream, AFormat);
663 finally
664 stream.Free;
665 end;
666end;
667
668function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData(
669 ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
670 out AScanlineSize: PtrInt): Pointer;
671begin
672 result := GetDitheredBitmapIndexedData(ABitDepth,
673 {$IFDEF ENDIAN_LITTLE}riboLSBFirst{$ELSE}riboMSBFirst{$endif},
674 AAlgorithm, ABitmap, AScanlineSize);
675end;
676
677function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData(
678 ABitDepth: integer; AAlgorithm: TDitheringAlgorithm;
679 ABitmap: TBGRACustomBitmap): Pointer;
680var dummy: PtrInt;
681begin
682 result := GetDitheredBitmapIndexedData(ABitDepth, AAlgorithm, ABitmap, dummy);
683end;
684
685{ TBGRACustomPalette }
686
687function TBGRACustomPalette.GetDominantColor: TBGRAPixel;
688var
689 w: ArrayOfWeightedColor;
690 i: Integer;
691 maxWeight, totalWeight: UInt32;
692begin
693 result := BGRAWhite;
694 maxWeight := 0;
695 w := GetAsArrayOfWeightedColor;
696 totalWeight:= 0;
697 for i := 0 to high(w) do
698 inc(totalWeight, w[i].Weight);
699 for i := 0 to high(w) do
700 if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).saturation > 16000) then
701 begin
702 maxWeight:= w[i].Weight;
703 result := w[i].Color;
704 end;
705 if maxWeight > totalWeight div 20 then exit;
706 for i := 0 to high(w) do
707 if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).lightness < 56000) and (BGRAToGSBA(w[i].Color).lightness > 16000) then
708 begin
709 maxWeight:= w[i].Weight;
710 result := w[i].Color;
711 end;
712 if maxWeight > 0 then exit;
713 for i := 0 to high(w) do
714 if (w[i].Weight > maxWeight) then
715 begin
716 maxWeight:= w[i].Weight;
717 result := w[i].Color;
718 end;
719end;
720
721procedure TBGRACustomPalette.AssignTo(AImage: TFPCustomImage);
722begin
723 AImage.UsePalette := true;
724 AssignTo(AImage.Palette);
725end;
726
727procedure TBGRACustomPalette.AssignTo(APalette: TFPPalette);
728var i: integer;
729begin
730 APalette.Clear;
731 APalette.Capacity := Count;
732 for i := 0 to Count-1 do
733 APalette.Color[i] := BGRAToFPColor(Color[i]);
734end;
735
736{ TBGRACustomApproxPalette }
737
738function TBGRACustomApproxPalette.FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel;
739var saveAlpha: byte;
740begin
741 if AValue.alpha = 0 then
742 result := BGRAPixelTransparent
743 else
744 begin
745 saveAlpha := AValue.alpha;
746 AValue.alpha := 255;
747 result := FindNearestColor(AValue);
748 result.alpha := saveAlpha;
749 end;
750end;
751
752function TBGRACustomApproxPalette.FindNearestColorIndexIgnoreAlpha(
753 AValue: TBGRAPixel): integer;
754begin
755 if AValue.alpha = 0 then
756 result := -1
757 else
758 begin
759 AValue.alpha := 255;
760 result := FindNearestColorIndex(AValue);
761 end;
762end;
763
764function TBGRACustomApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32;
765begin
766 result := 1;
767end;
768
769function TBGRACustomApproxPalette.FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel;
770begin
771 if AIgnoreAlpha then
772 result := FindNearestColorIgnoreAlpha(AValue)
773 else
774 result := FindNearestColor(AValue);
775end;
776
777function TBGRACustomApproxPalette.FindNearestColorIndex(AValue: TBGRAPixel;
778 AIgnoreAlpha: boolean): integer;
779begin
780 if AIgnoreAlpha then
781 result := FindNearestColorIndexIgnoreAlpha(AValue)
782 else
783 result := FindNearestColorIndex(AValue);
784end;
785
786{ TBGRAWeightedPalette }
787
788function TBGRAWeightedPalette.GetWeightByIndex(AIndex: Integer): UInt32;
789begin
790 NeedArray;
791 if (AIndex >= 0) and (AIndex < length(FArray)) then
792 result := PBGRAWeightedPaletteEntry(FArray[AIndex])^.Weight
793 else
794 raise ERangeError.Create('Index out of bounds');
795end;
796
797procedure TBGRAWeightedPalette.IncludePixel(PPixel: PBGRAPixel);
798var dummy: UInt32;
799begin
800 IncColor(PPixel^,dummy);
801end;
802
803constructor TBGRAWeightedPalette.Create(AColors: ArrayOfWeightedColor);
804var
805 i: Integer;
806begin
807 inherited Create;
808 for i := 0 to high(AColors) do
809 with AColors[i] do IncColor(Color,Weight);
810end;
811
812function TBGRAWeightedPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
813var
814 i: NativeInt;
815begin
816 NeedArray;
817 setlength(result, length(FArray));
818 for i := 0 to high(result) do
819 result[i] := PBGRAWeightedPaletteEntry(FArray[i])^;
820end;
821
822function TBGRAWeightedPalette.CreateEntry(AColor: TBGRAPixel): PBGRAPixel;
823begin
824 result := PBGRAPixel(GetMem(sizeOf(TBGRAWeightedPaletteEntry)));
825 result^ := AColor;
826 PBGRAWeightedPaletteEntry(result)^.Weight := 1;
827end;
828
829procedure TBGRAWeightedPalette.FreeEntry(AEntry: PBGRAPixel);
830begin
831 FreeMem(AEntry);
832end;
833
834function TBGRAWeightedPalette.IncColor(AValue: TBGRAPixel; out NewWeight: UInt32
835 ): boolean;
836Var Node: TAvgLvlTreeNode;
837 Entry: PBGRAPixel;
838begin
839 Entry := GetLastColor(AValue);
840 if Entry <> nil then
841 begin
842 NewWeight := PBGRAWeightedPaletteEntry(Entry)^.Weight+1;
843 PBGRAWeightedPaletteEntry(Entry)^.Weight := NewWeight;
844 result := false;
845 exit;
846 end;
847 Node := FTree.Find(@AValue);
848 if Assigned(Node) then
849 begin
850 Entry := PBGRAPixel(Node.Data);
851 NewWeight := PBGRAWeightedPaletteEntry(Entry)^.Weight+1;
852 PBGRAWeightedPaletteEntry(Entry)^.Weight := NewWeight;
853 AddLastColor(Entry);
854 result := false;
855 end
856 else
857 begin
858 Entry := CreateEntry(AValue);
859 FTree.Add(Entry);
860 ClearArray;
861 NewWeight := PBGRAWeightedPaletteEntry(Entry)^.Weight;
862 AddLastColor(Entry);
863 result := true;
864 end;
865end;
866
867function TBGRAWeightedPalette.DecColor(AValue: TBGRAPixel; out NewWeight: UInt32
868 ): boolean;
869var
870 Node : TAvgLvlTreeNode;
871 Entry: PBGRAPixel;
872begin
873 Node := FTree.Find(@AValue);
874 if Assigned(Node) then
875 begin
876 Entry := PBGRAPixel(Node.Data);
877 NewWeight := PBGRAWeightedPaletteEntry(Entry)^.Weight;
878 if NewWeight >= 2 then
879 begin
880 dec(NewWeight);
881 PBGRAWeightedPaletteEntry(Entry)^.Weight := NewWeight;
882 end
883 else
884 begin
885 NewWeight := 0;
886 FreeEntry(Entry);
887 FTree.Delete(Node);
888 ClearArray;
889 ClearLastColors;
890 end;
891 result := true;
892 end else
893 begin
894 result := false;
895 NewWeight := 0;
896 end;
897end;
898
899{ TBGRAReferencePalette }
900
901procedure TBGRAReferencePalette.FreeEntry(AEntry: PBGRAPixel);
902begin
903 //nothing
904end;
905
906function TBGRAReferencePalette.AddColor(AValue: PBGRAPixel): boolean;
907begin
908 if Assigned(GetLastColor(AValue^)) then
909 begin
910 result := false;
911 exit;
912 end;
913 AddLastColor(AValue);
914 if Assigned(FTree.Find(AValue)) then
915 begin
916 result := false;
917 end
918 else
919 begin
920 result := true;
921 FTree.Add(AValue);
922 ClearArray;
923 end;
924end;
925
926function TBGRAReferencePalette.RemoveColor(AValue: PBGRAPixel): boolean;
927var
928 Node : TAvgLvlTreeNode;
929begin
930 Node := FTree.Find(AValue);
931 if Assigned(Node) then
932 begin
933 FTree.Delete(Node);
934 result := true;
935 ClearArray;
936 ClearLastColors;
937 end else
938 result := false;
939end;
940
941{ TBGRAAvgLvlPalette }
942
943constructor TBGRAAvgLvlPalette.Create;
944begin
945 FTree := TAvgLvlTree.Create;
946 FTree.OnObjectCompare := @OnCompareItems;
947end;
948
949destructor TBGRAAvgLvlPalette.Destroy;
950begin
951 Clear;
952 FreeAndNil(FTree);
953 inherited Destroy;
954end;
955
956function TBGRAAvgLvlPalette.GetAsArrayOfColor: ArrayOfTBGRAPixel;
957var i: NativeInt;
958begin
959 NeedArray;
960 setlength(result, Length(FArray));
961 for i := 0 to high(result) do
962 result[i] := FArray[i]^;
963end;
964
965function TBGRAAvgLvlPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
966var i: NativeInt;
967begin
968 NeedArray;
969 setlength(result, Length(FArray));
970 for i := 0 to high(result) do
971 with result[i] do
972 begin
973 Color := FArray[i]^;
974 Weight:= 1;
975 end;
976end;
977
978procedure TBGRAAvgLvlPalette.Clear;
979var Node: TAvgLvlTreeNode;
980begin
981 For Node in FTree do
982 FreeEntry(PBGRAPixel(Node.Data));
983 FTree.Clear;
984 ClearArray;
985 FLastAddedColorCount := 0;
986end;
987
988function TBGRAAvgLvlPalette.GetCount: integer;
989begin
990 result := FTree.Count;
991end;
992
993function TBGRAAvgLvlPalette.ContainsColor(AValue: TBGRAPixel): boolean;
994Var Node: TAvgLvlTreeNode;
995begin
996 if Assigned(GetLastColor(AValue)) then
997 begin
998 result := true;
999 exit;
1000 end;
1001 Node := FTree.Find(@AValue);
1002 result := Assigned(Node);
1003 if result then AddLastColor(PBGRAPixel(Node.Data));
1004end;
1005
1006function TBGRAAvgLvlPalette.IndexOfColor(AValue: TBGRAPixel): integer;
1007Var Node: TAvgLvlTreeNode;
1008begin
1009 Node := FTree.Find(@AValue);
1010 if Assigned(Node) then
1011 begin
1012 result := 0;
1013 Node := Node.Precessor;
1014 while Assigned(Node) do
1015 begin
1016 inc(result);
1017 Node := Node.Precessor;
1018 end;
1019 end else
1020 result := -1;
1021end;
1022
1023function TBGRAAvgLvlPalette.GetColorByIndex(AIndex: integer): TBGRAPixel;
1024begin
1025 NeedArray;
1026 if (AIndex >= 0) and (AIndex < length(FArray)) then
1027 result := FArray[AIndex]^
1028 else
1029 raise ERangeError.Create('Index out of bounds');
1030end;
1031
1032function TBGRAAvgLvlPalette.OnCompareItems(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
1033var gray1, gray2: NativeUInt;
1034 c1, c2: TBGRAPixel;
1035begin
1036 c1 := PBGRAPixel(Data1)^;
1037 c2 := PBGRAPixel(Data2)^;
1038 if c1.alpha < c2.alpha then
1039 result := -1
1040 else if c1.alpha > c2.alpha then
1041 result := 1
1042 else
1043 begin
1044 gray1 := (GammaExpansionTab[c1.red] shl 8)+(GammaExpansionTab[c1.green] shl 9)+(GammaExpansionTab[c1.blue] shl 7);
1045 gray2 := (GammaExpansionTab[c2.red] shl 8)+(GammaExpansionTab[c2.green] shl 9)+(GammaExpansionTab[c2.blue] shl 7);
1046 if gray1<gray2 then
1047 result := -1
1048 else if gray1>gray2 then
1049 result := 1
1050 else
1051 begin
1052 if c1.green > c2.green then
1053 result := 1
1054 else if c1.green < c2.green then
1055 result := -1
1056 else if c1.red > c2.red then
1057 result := 1
1058 else if c1.red < c2.red then
1059 result := -1
1060 else if c1.blue > c2.blue then
1061 result := 1
1062 else if c1.blue < c2.blue then
1063 result := -1
1064 else
1065 result := 0;
1066 end;
1067 end;
1068end;
1069
1070procedure TBGRAAvgLvlPalette.NeedArray;
1071var Node: TAvgLvlTreeNode;
1072 i,n: integer;
1073begin
1074 n := Count;
1075 if length(FArray) <> n then
1076 begin
1077 setLength(FArray,n);
1078 i := 0;
1079 for Node in FTree do
1080 begin
1081 if i >= n then break;
1082 FArray[i] := PBGRAPixel(Node.Data);
1083 inc(i);
1084 end;
1085 end;
1086end;
1087
1088procedure TBGRAAvgLvlPalette.ClearArray;
1089begin
1090 FArray := nil;
1091end;
1092
1093procedure TBGRAAvgLvlPalette.AddLastColor(AColor: PBGRAPixel);
1094begin
1095 if FLastAddedColorCount < MaxLastAddedColors then
1096 begin
1097 FLastAddedColors[FLastAddedColorCount] := AColor;
1098 inc(FLastAddedColorCount);
1099 end else
1100 begin
1101 move(FLastAddedColors[1],FLastAddedColors[0],(FLastAddedColorCount-1)*sizeof(PBGRAPixel));
1102 FLastAddedColors[FLastAddedColorCount-1] := AColor;
1103 end;
1104end;
1105
1106function TBGRAAvgLvlPalette.GetLastColor(AValue: TBGRAPixel): PBGRAPixel;
1107var
1108 i: NativeInt;
1109begin
1110 for i := FLastAddedColorCount-1 downto 0 do
1111 if PDWord(FLastAddedColors[i])^ = DWord(AValue) then
1112 begin
1113 result := FLastAddedColors[i];
1114 exit;
1115 end;
1116 result := nil;
1117end;
1118
1119procedure TBGRAAvgLvlPalette.ClearLastColors;
1120begin
1121 FLastAddedColorCount := 0;
1122end;
1123
1124{ TBGRAPalette }
1125
1126function TBGRAPalette.CreateEntry(AColor: TBGRAPixel): PBGRAPixel;
1127begin
1128 result := PBGRAPixel(GetMem(sizeOf(TBGRAPixel)));
1129 result^ := AColor;
1130end;
1131
1132procedure TBGRAPalette.FreeEntry(AEntry: PBGRAPixel);
1133begin
1134 FreeMem(AEntry);
1135end;
1136
1137procedure TBGRAPalette.IncludePixel(PPixel: PBGRAPixel);
1138begin
1139 AddColor(PPixel^);
1140end;
1141
1142procedure TBGRAPalette.ExceptionUnknownPaletteFormat;
1143begin
1144 raise Exception.Create('Unknown palette format');
1145end;
1146
1147procedure TBGRAPalette.ExceptionInvalidPaletteFormat;
1148begin
1149 raise Exception.Create('Invalid palette format');
1150end;
1151
1152constructor TBGRAPalette.Create(ABitmap: TBGRACustomBitmap);
1153var p: PBGRAPixel;
1154 n: integer;
1155begin
1156 inherited Create;
1157 n:= ABitmap.NbPixels;
1158 p := ABitmap.Data;
1159 while n > 0 do
1160 begin
1161 IncludePixel(p);
1162 inc(p);
1163 dec(n);
1164 end;
1165end;
1166
1167constructor TBGRAPalette.Create(APalette: TBGRACustomPalette);
1168begin
1169 inherited Create;
1170 AddColors(APalette);
1171end;
1172
1173constructor TBGRAPalette.Create(AColors: ArrayOfTBGRAPixel);
1174var
1175 i: Integer;
1176begin
1177 inherited Create;
1178 for i := 0 to high(AColors) do
1179 AddColor(AColors[i]);
1180end;
1181
1182constructor TBGRAPalette.Create(AColors: ArrayOfWeightedColor);
1183var
1184 i: Integer;
1185begin
1186 inherited Create;
1187 for i := 0 to high(AColors) do
1188 AddColor(AColors[i].Color);
1189end;
1190
1191function TBGRAPalette.AddColor(AValue: TBGRAPixel): boolean;
1192Var Node: TAvgLvlTreeNode;
1193 Entry: PBGRAPixel;
1194begin
1195 if Assigned(GetLastColor(AValue)) then
1196 begin
1197 result := false;
1198 exit;
1199 end;
1200 Node := FTree.Find(@AValue);
1201 if Assigned(Node) then
1202 begin
1203 AddLastColor(PBGRAPixel(Node.Data));
1204 result := false;
1205 end
1206 else
1207 begin
1208 result := true;
1209 Entry := CreateEntry(AValue);
1210 FTree.Add(Entry);
1211 ClearArray;
1212 AddLastColor(Entry);
1213 end;
1214end;
1215
1216procedure TBGRAPalette.AddColors(ABitmap: TBGRACustomBitmap);
1217var p: PBGRAPixel;
1218 n: integer;
1219begin
1220 n := ABitmap.NbPixels;
1221 p := ABitmap.Data;
1222 while n > 0 do
1223 begin
1224 AddColor(p^);
1225 inc(p);
1226 dec(n);
1227 end;
1228end;
1229
1230procedure TBGRAPalette.AddColors(APalette: TBGRACustomPalette);
1231var i: NativeInt;
1232begin
1233 for i := 0 to APalette.Count- 1 do
1234 AddColor(APalette.Color[i]);
1235end;
1236
1237function TBGRAPalette.RemoveColor(AValue: TBGRAPixel): boolean;
1238var
1239 Node : TAvgLvlTreeNode;
1240begin
1241 Node := FTree.Find(@AValue);
1242 if Assigned(Node) then
1243 begin
1244 FreeEntry(Node.Data);
1245 FTree.Delete(Node);
1246 result := true;
1247 ClearArray;
1248 ClearLastColors;
1249 end else
1250 result := false;
1251end;
1252
1253procedure TBGRAPalette.LoadFromFile(AFilenameUTF8: string);
1254var
1255 stream: TFileStreamUTF8;
1256 format: TBGRAPaletteFormat;
1257begin
1258 format := DetectPaletteFormat(AFilenameUTF8);
1259 if format = palUnknown then
1260 begin
1261 ExceptionUnknownPaletteFormat;
1262 exit;
1263 end;
1264 stream:= TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead);
1265 try
1266 LoadFromStream(stream, format);
1267 finally
1268 stream.Free;
1269 end;
1270end;
1271
1272procedure TBGRAPalette.LoadFromStream(AStream: TStream;
1273 AFormat: TBGRAPaletteFormat);
1274var buf: TReadBufStream;
1275 handled: boolean;
1276 i: Integer;
1277begin
1278 RegisterDefaultPaletteFormats;
1279 Clear;
1280 buf := TReadBufStream.Create(AStream);
1281 try
1282 handled := false;
1283 for i := 0 to High(PaletteFormats) do
1284 if PaletteFormats[i].formatIndex = AFormat then
1285 begin
1286 if not PaletteFormats[i].reader(self, AStream) then
1287 ExceptionInvalidPaletteFormat;
1288 handled := true;
1289 break;
1290 end;
1291 if not handled then ExceptionUnknownPaletteFormat;
1292 finally
1293 buf.Free;
1294 end;
1295end;
1296
1297procedure TBGRAPalette.LoadFromResource(AFilename: string; AFormat: TBGRAPaletteFormat);
1298var
1299 stream: TStream;
1300begin
1301 stream := BGRAResource.GetResourceStream(AFilename);
1302 try
1303 LoadFromStream(stream, AFormat);
1304 finally
1305 stream.Free;
1306 end;
1307end;
1308
1309procedure TBGRAPalette.SaveToFile(AFilenameUTF8: string);
1310var
1311 stream: TFileStreamUTF8;
1312begin
1313 stream:= TFileStreamUTF8.Create(AFilenameUTF8,fmCreate);
1314 try
1315 SaveToStream(stream, SuggestPaletteFormat(AFilenameUTF8));
1316 finally
1317 stream.Free;
1318 end;
1319end;
1320
1321procedure TBGRAPalette.SaveToStream(AStream: TStream;
1322 AFormat: TBGRAPaletteFormat);
1323var buf: TWriteBufStream;
1324 handled: boolean;
1325 i: Integer;
1326begin
1327 RegisterDefaultPaletteFormats;
1328 buf := TWriteBufStream.Create(AStream);
1329 try
1330 handled := false;
1331 for i := 0 to High(PaletteFormats) do
1332 if PaletteFormats[i].formatIndex = AFormat then
1333 begin
1334 PaletteFormats[i].writer(self, AStream);
1335 handled := true;
1336 break;
1337 end;
1338 if not handled then ExceptionUnknownPaletteFormat;
1339 finally
1340 buf.Free;
1341 end;
1342end;
1343
1344function TBGRAPalette.DetectPaletteFormat(AStream: TStream): TBGRAPaletteFormat;
1345var buf: string;
1346 oldPos: int64;
1347 i: Integer;
1348begin
1349 result := palUnknown;
1350 setlength(buf,256);
1351 fillchar(buf[1],length(buf),#0);
1352 oldPos := AStream.Position;
1353 AStream.Read(buf[1],length(buf));
1354 AStream.Position := oldPos;
1355 if length(buf)>0 then
1356 begin
1357 RegisterDefaultPaletteFormats;
1358 for i := 0 to high(PaletteFormats) do
1359 if PaletteFormats[i].checkFormat(buf) then
1360 begin
1361 result := PaletteFormats[i].formatIndex;
1362 exit;
1363 end;
1364 end;
1365end;
1366
1367function TBGRAPalette.DetectPaletteFormat(AFilenameUTF8: string
1368 ): TBGRAPaletteFormat;
1369var stream: TFileStreamUTF8;
1370begin
1371 result := SuggestPaletteFormat(AFilenameUTF8);
1372 if not FileExists(AFilenameUTF8) then exit;
1373 try
1374 stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
1375 except
1376 exit;
1377 end;
1378 try
1379 result := DetectPaletteFormat(stream);
1380 if result = palUnknown then
1381 result := SuggestPaletteFormat(AFilenameUTF8);
1382 finally
1383 stream.Free;
1384 end;
1385end;
1386
1387function TBGRAPalette.SuggestPaletteFormat(AFilenameUTF8: string
1388 ): TBGRAPaletteFormat;
1389var ext: string;
1390 i: Integer;
1391begin
1392 RegisterDefaultPaletteFormats;
1393 ext := ExtractFileExt(AFilenameUTF8);
1394 if ext <> '' then
1395 begin
1396 for i := 0 to high(PaletteFormats) do
1397 if CompareText(PaletteFormats[i].ext,ext) = 0 then
1398 begin
1399 result := PaletteFormats[i].formatIndex;
1400 exit;
1401 end;
1402 end;
1403 result := palUnknown;
1404end;
1405
1406end.
1407
Note: See TracBrowser for help on using the repository browser.