123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- {*****************************************************************************}
- {
- This file is part of the Free Pascal's "Free Components Library".
- Copyright (c) 2005 by Giulio Bernardi
- This file contains classes used to quantize images.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- }
- {*****************************************************************************}
- {$mode objfpc}{$h+}
- unit FPQuantizer;
- interface
- uses sysutils, classes, fpimage, fpcolhash;
- type
- FPQuantizerException = class (exception);
- type
- TFPQuantizerProgressEvent = procedure (Sender: TObject; Stage: TFPImgProgressStage; PercentDone: Byte;
- const Msg: AnsiString; var Continue : Boolean) of object;
- type
- TFPColorQuantizer = class
- private
- FOnProgress : TFPQuantizerProgressEvent;
- protected
- FColNum : longword;
- FSupportsAlpha : boolean;
- FImages : array of TFPCustomImage;
- FCount : integer;
- function InternalQuantize : TFPPalette; virtual; abstract;
- procedure SetColNum(AColNum : longword); virtual;
- procedure Progress (Sender: TObject; Stage: TFPImgProgressStage; PercentDone: Byte; const Msg: AnsiString; var Continue : Boolean); virtual;
- function GetImage(Index : integer) : TFPCustomImage;
- procedure SetImage(Index : integer; const Img : TFPCustomImage);
- procedure SetCount(Value : integer);
- public
- property OnProgress : TFPQuantizerProgressEvent read FOnProgress write FOnProgress;
- property Images[Index : integer] : TFPCustomImage read GetImage write SetImage;
- property Count : integer read FCount write SetCount;
- property ColorNumber : longword read FColNum write SetColNum;
- property SupportsAlpha : boolean read FSupportsAlpha;
- procedure Clear;
- procedure Add(const Img : TFPCustomImage);
- function Quantize : TFPPalette;
- constructor Create; virtual;
- destructor Destroy; override;
- end;
- type
- POctreeQNode = ^TOctreeQNode;
- TOctreeQChilds = array[0..7] of POctreeQNode;
- TOctreeQNode = record
- isleaf : boolean;
- count : longword;
- R, G, B : longword;
- Next : POctreeQNode; //used in the reduction list.
- Childs : TOctreeQChilds;
- end;
- type
- TFPOctreeQuantizer = class(TFPColorQuantizer)
- private
- Root : POctreeQNode;
- ReductionList : TOctreeQChilds;
- LeafTot, MaxLeaf : longword;
- percent : byte; { these values are used to call OnProgress event }
- percentinterval : longword;
- percentacc : longword;
- FContinue : boolean;
- procedure DisposeNode(var Node : POctreeQNode);
- procedure AddColor(var Node : POctreeQNode; const R, G, B, Level : byte);
- procedure AddToPalette(var Node : POctreeQNode; Palette : TFPPalette; var Current : integer);
- procedure Reduce;
- function BuildPalette : TFPPalette;
- protected
- function InternalQuantize : TFPPalette; override;
- public
- end;
- type
- TMCBox = record
- total, startindex, endindex : longword;
- end;
- const mcSlow = 0;
- mcNormal = 1;
- mcFast = 2;
- type
- TFPMedianCutQuantizer = class(TFPColorQuantizer)
- private
- HashTable, palcache : TFPColorHashTable;
- arr : TFPColorWeightArray;
- boxes : array of TMCBox;
- Used : integer;
- percent : byte; { these values are used to call OnProgress event }
- percentinterval : longword;
- percentacc : longword;
- FContinue : boolean;
- FMode : byte;
- function ColorCompare(const c1, c2 : TFPPackedColor; const Dim : byte) : shortint;
- function FindLargestDimension(const Box : TMCBox) : byte;
- procedure QuickSort(const l, r : integer; const Dim : byte);
- procedure QuickSortBoxes(const l, r : integer);
- function MeanBox(const box : TMCBox) : TFPColor;
- function BuildPalette : TFPPalette;
- procedure SetMode(const Amode : byte);
- function MaskColor(const col : TFPColor) : TFPColor;
- protected
- function InternalQuantize : TFPPalette; override;
- public
- constructor Create; override;
- property Mode : byte read FMode write SetMode;
- end;
- implementation
- function RGB2FPColor(const R, G, B : longword) : TFPColor;
- begin
- Result.Red:=(R shl 8) + R;
- Result.Green:=(G shl 8) + G;
- Result.Blue:=(B shl 8) + B;
- Result.Alpha := AlphaOpaque;
- end;
- { TFPColorQuantizer }
- function TFPColorQuantizer.Quantize : TFPPalette;
- begin
- Result:=InternalQuantize;
- end;
- constructor TFPColorQuantizer.Create;
- begin
- FSupportsAlpha:=false;
- FColNum:=256; //default setting.
- FCount:=0;
- setlength(FImages,0);
- end;
- destructor TFPColorQuantizer.Destroy;
- begin
- Setlength(FImages,0);
- inherited Destroy;
- end;
- procedure TFPColorQuantizer.SetColNum(AColNum : longword);
- begin
- if AColNum<2 then
- raise FPQuantizerException.Create('Invalid color depth: '+IntToStr(AColNum));
- FColNum:=AColNum;
- end;
- procedure TFPColorQuantizer.Progress(Sender: TObject; Stage: TFPImgProgressStage; PercentDone: Byte; const Msg: AnsiString; var Continue : Boolean);
- begin
- if Assigned(FOnProgress) then
- FOnProgress(Sender,Stage,PercentDone,Msg,Continue);
- end;
- function TFPColorQuantizer.GetImage(Index : integer) : TFPCustomImage;
- begin
- if Index>=FCount then
- raise FPQuantizerException.Create('Invalid image index: '+IntToStr(Index));
- Result:=FImages[index];
- end;
- procedure TFPColorQuantizer.SetImage(Index : integer; const Img : TFPCustomImage);
- begin
- if Index>=FCount then
- raise FPQuantizerException.Create('Invalid image index: '+IntToStr(Index));
- FImages[Index]:=Img;
- end;
- procedure TFPColorQuantizer.SetCount(Value : integer);
- var old, i : integer;
- begin
- old:=FCount;
- setlength(FImages,Value);
- for i:=old to Value-1 do
- FImages[i]:=nil;
- FCount:=Value;
- end;
- procedure TFPColorQuantizer.Clear;
- begin
- setlength(FImages,0);
- FCount:=0;
- end;
- procedure TFPColorQuantizer.Add(const Img : TFPCustomImage);
- var i : integer;
- begin
- { Find first unused slot }
- for i:=0 to FCount-1 do
- if FImages[i]=nil then
- begin
- Fimages[i]:=Img;
- exit;
- end;
- { If we reached this point there are no unused slot: let's enlarge the array }
- SetCount(Fcount+1);
- FImages[FCount-1]:=Img;
- end;
- { TFPOctreeQuantizer }
- const Mask : array[0..7] of byte = ($80, $40, $20, $10, $08, $04, $02, $01);
- procedure TFPOctreeQuantizer.AddColor(var Node : POctreeQNode; const R, G, B, Level : byte);
- var index, shift : byte;
- begin
- if Node=nil then
- begin
- Node:=getmem(sizeof(TOctreeQNode));
- if Node=nil then
- raise FPQuantizerException.Create('Out of memory');
- FillByte(Node^,sizeof(TOctreeQNode),0);
- if level=7 then
- begin
- Node^.isleaf:=true;
- inc(LeafTot); { we just created a new leaf }
- end
- else
- begin { we don't put leaves in reduction list since this is unuseful }
- Node^.isleaf:=false;
- Node^.Next:=ReductionList[level]; { added on top of the reduction list for its level }
- ReductionList[level]:=Node;
- end;
- end;
- if Node^.isleaf then
- begin
- inc(Node^.R,R);
- inc(Node^.G,G);
- inc(Node^.B,B);
- inc(Node^.count);
- end
- else
- begin
- shift:=7-level;
- index:=((R and mask[level]) shr shift) shl 2;
- index:=index+((G and mask[level]) shr shift) shl 1;
- index:=index+((B and mask[level]) shr shift);
- AddColor(Node^.Childs[index],R,G,B,Level+1);
- end;
- end;
- procedure TFPOctreeQuantizer.DisposeNode(var Node : POctreeQNode);
- var i : integer;
- begin
- if Node=nil then exit;
- if not (Node^.isleaf) then
- for i:=0 to 7 do
- if Node^.childs[i]<>nil then
- DisposeNode(Node^.childs[i]);
- FreeMem(Node);
- Node:=nil;
- end;
- procedure TFPOctreeQuantizer.Reduce;
- var i : integer;
- Node : POctreeQNode;
- begin
- i:=6; { level 7 nodes don't have childs, start from 6 and go backward }
- while ((i>0) and (ReductionList[i]=nil)) do
- dec(i);
- { remove this node from the list}
- Node:=ReductionList[i];
- ReductionList[i]:=Node^.Next;
- for i:=0 to 7 do
- if Node^.childs[i]<>nil then
- begin
- inc(Node^.count,Node^.childs[i]^.count);
- inc(Node^.r,Node^.childs[i]^.r);
- inc(Node^.g,Node^.childs[i]^.g);
- inc(Node^.b,Node^.childs[i]^.b);
- DisposeNode(Node^.childs[i]);
- dec(LeafTot);
- end;
- Node^.isleaf:=true;
- inc(LeafTot); { this node is now a leaf! }
- end;
- procedure TFPOctreeQuantizer.AddToPalette(var Node : POctreeQNode; Palette : TFPPalette; var Current : integer);
- var i : byte;
- begin
- if not FContinue then exit;
- if Node^.isleaf then
- begin
- if (current >= LeafTot) then
- raise FPQuantizerException.Create('Octree Quantizer internal error: palette index too high.');
- Node^.r:= Node^.r div Node^.count;
- Node^.g:= Node^.g div Node^.count;
- Node^.b:= Node^.b div Node^.count;
- Palette.Color[Current]:=RGB2FPColor(Node^.r,Node^.g,Node^.b);
- inc(current);
- { ************************************************ }
- inc(percentacc);
- if percentacc>=percentinterval then
- begin
- dec(percentacc,percentinterval);
- inc(percent);
- Progress(self,psRunning,percent,'',FContinue);
- end;
- { ************************************************ }
- end
- else
- for i:=0 to 7 do
- if Node^.childs[i]<>nil then
- AddToPalette(Node^.childs[i],Palette,Current);
- end;
- function TFPOctreeQuantizer.BuildPalette : TFPPalette;
- var pal : TFPPalette;
- i : integer;
- begin
- if Root=nil then exit;
- pal:=TFPPalette.Create(LeafTot);
- i:=0;
- try
- AddToPalette(Root,pal,i);
- except
- pal.Free;
- pal:=nil;
- raise;
- end;
- if not FContinue then
- begin
- pal.Free;
- pal:=nil;
- end;
- Result:=pal;
- end;
- function TFPOctreeQuantizer.InternalQuantize : TFPPalette;
- var i, j, k : integer;
- color : TFPColor;
- begin
- Root:=nil;
- for i:=0 to high(ReductionList) do
- ReductionList[i]:=nil;
- LeafTot:=0;
- MaxLeaf:=FColNum;
- { ************************************************************** }
- { set up some values useful when calling OnProgress event }
- { number of operations is: }
- { width*heigth for population }
- { initial palette count - final palette count for reduction }
- { final palette count for building the palette }
- { total: width*heigth+initial palette count. }
- { if source image doesn't have a palette assume palette count as }
- { width*height (worst scenario) if it is < 2^24, or 2^24 else }
- percentinterval:=0;
- percentacc:=0;
- for i:=0 to FCount-1 do
- if FImages[i]<>nil then
- begin
- percentinterval:=percentinterval+FImages[i].Width*FImages[i].Height;
- if FImages[i].UsePalette then
- percentacc:=percentacc+FImages[i].Palette.Count
- else
- percentacc:=percentacc+FImages[i].Width*FImages[i].Height;
- end;
- if percentacc>$1000000 then percentacc:=$1000000;
- percentinterval:=(percentacc+percentinterval) div 100; { how many operations for 1% }
- if percentinterval=0 then percentinterval:=$FFFFFFFF; { it's quick, call progress only when starting and ending }
- percent:=0;
- percentacc:=0;
- FContinue:=true;
- Progress (self,psStarting,0,'',FContinue);
- Result:=nil;
- if not FContinue then exit;
- { ************************************************************** }
- { populate the octree with colors }
- try
- for k:=0 to FCount-1 do
- if FImages[k]<>nil then
- for j:=0 to FImages[k].Height-1 do
- for i:=0 to FImages[k].Width-1 do
- begin
- Color:=FImages[k][i,j];
- AddColor(Root,(Color.Red and $FF00) shr 8,(Color.Green and $FF00) shr 8,(Color.Blue and $FF00) shr 8,0);
- { ************************************************* }
- inc(percentacc);
- if percentacc>=percentinterval then
- begin
- dec(percentacc,percentinterval);
- inc(percent);
- Progress(self,psRunning,percent,'',FContinue);
- if not FContinue then exit;
- end;
- { ************************************************* }
- end;
- { reduce number of colors until it is <= MaxLeaf }
- while LeafTot > MaxLeaf do
- begin
- Reduce;
- { ************************************************* }
- inc(percentacc);
- if percentacc>=percentinterval then
- begin
- dec(percentacc,percentinterval);
- inc(percent);
- Progress(self,psRunning,percent,'',FContinue);
- if not FContinue then exit;
- end;
- { ************************************************* }
- end;
- { build the palette }
- Result:=BuildPalette;
- if FContinue then Progress (self,psEnding,100,'',FContinue);
- finally
- DisposeNode(Root);
- end;
- end;
- { TFPMedianCutQuantizer }
- const DIM_ALPHA = 0;
- DIM_RED = 1;
- DIM_GREEN = 2;
- DIM_BLUE = 3;
- constructor TFPMedianCutQuantizer.Create;
- begin
- inherited Create;
- FSupportsAlpha:=true;
- FMode:=mcNormal;
- end;
- procedure TFPMedianCutQuantizer.SetMode(const Amode : byte);
- begin
- if not (Amode in [mcSlow,mcNormal,mcFast]) then
- raise FPQuantizerException.Create('Invalid quantizer mode: '+IntToStr(Amode));
- FMode:=Amode;
- end;
- function TFPMedianCutQuantizer.FindLargestDimension(const Box : TMCBox) : byte;
- var i : longword;
- col : TFPPackedColor;
- maxa, mina, maxr, minr, maxg, ming, maxb, minb : byte;
- begin
- maxa:=0; maxr:=0; maxg:=0; maxb:=0;
- mina:=$FF; minr:=$FF; ming:=$FF; minb:=$FF;
- for i:=box.startindex to box.endindex do
- begin
- col:=arr[i]^.Col;
- if col.A<mina then mina:=col.A;
- if col.A>maxa then maxa:=col.A;
- if col.R<minr then minr:=col.R;
- if col.R>maxr then maxr:=col.R;
- if col.G<ming then ming:=col.G;
- if col.G>maxg then maxg:=col.G;
- if col.B<minb then minb:=col.B;
- if col.B>maxb then maxb:=col.B;
- end;
- maxa:=maxa-mina;
- maxr:=maxr-minr;
- maxg:=maxg-ming;
- maxb:=maxb-minb;
- if ((maxa>maxr) and (maxa>maxg) and (maxa>maxb)) then Result:=DIM_ALPHA
- else if ((maxr>maxa) and (maxr>maxg) and (maxr>maxb)) then Result:=DIM_RED
- else if ((maxg>maxa) and (maxg>maxr) and (maxg>maxb)) then Result:=DIM_GREEN
- else Result:=DIM_BLUE;
- end;
- function TFPMedianCutQuantizer.ColorCompare(const c1, c2 : TFPPackedColor; const Dim : byte) : shortint;
- var tmp : integer;
- begin
- case Dim of
- DIM_ALPHA : tmp:=(c1.A-c2.A);
- DIM_RED : tmp:=(c1.R-c2.R);
- DIM_GREEN : tmp:=(c1.G-c2.G);
- DIM_BLUE : tmp:=(c1.B-c2.B)
- else raise FPQuantizerException.Create('Invalid dimension: '+IntToStr(Dim));
- end;
- if tmp>0 then Result:=1
- else if tmp<0 then Result:=-1
- else Result:=0;
- end;
- procedure TFPMedianCutQuantizer.QuickSort(const l, r : integer; const Dim : byte);
- var i, j : integer;
- pivot, temp : PFPColorWeight;
- begin
- if l<r then
- begin
- pivot:=arr[l];
- i:=l+1;
- j:=r;
- repeat
- while ((i<=r) and (ColorCompare(arr[i]^.Col,pivot^.Col,dim)<=0)) do
- inc(i);
- while (ColorCompare(arr[j]^.Col,pivot^.Col,dim)=1) do
- dec(j);
- if i<j then
- begin
- temp:=arr[i];
- arr[i]:=arr[j];
- arr[j]:=temp;
- end;
- until i > j;
- { don't swap if they are equal }
- if ColorCompare(arr[j]^.Col,pivot^.Col,dim)<>0 then
- begin
- arr[l]:=arr[j];
- arr[j]:=pivot;
- end;
- Quicksort(l,j-1,dim);
- Quicksort(i,r,dim);
- end;
- end;
- procedure TFPMedianCutQuantizer.QuickSortBoxes(const l, r : integer);
- var i, j : integer;
- pivot, temp : TMCBox;
- begin
- if l<r then
- begin
- pivot:=boxes[l];
- i:=l+1;
- j:=r;
- repeat
- while ((i<=r) and (boxes[i].total>=pivot.total)) do
- inc(i);
- while (boxes[j].total<pivot.total) do
- dec(j);
- if i<j then
- begin
- temp:=boxes[i];
- boxes[i]:=boxes[j];
- boxes[j]:=temp;
- end;
- until i > j;
- { don't swap if they are equal }
- if boxes[j].total<>pivot.total then
- begin
- boxes[l]:=boxes[j];
- boxes[j]:=pivot;
- end;
- QuicksortBoxes(l,j-1);
- QuicksortBoxes(i,r);
- end;
- end;
- function TFPMedianCutQuantizer.MeanBox(const box : TMCBox) : TFPColor;
- var tota,totr,totg,totb, pixcount : longword;
- i : integer;
- col : TFPPackedColor;
- fpcol : TFPColor;
- begin
- tota:=0; totr:=0; totg:=0; totb:=0; pixcount:=0;
- for i:=box.startindex to box.endindex do
- begin
- tota:=tota+(arr[i]^.Col.A*arr[i]^.Num);
- totr:=totr+(arr[i]^.Col.R*arr[i]^.Num);
- totg:=totg+(arr[i]^.Col.G*arr[i]^.Num);
- totb:=totb+(arr[i]^.Col.B*arr[i]^.Num);
- inc(pixcount,arr[i]^.Num);
- end;
- tota:=round(tota / pixcount);
- totr:=round(totr / pixcount);
- totg:=round(totg / pixcount);
- totb:=round(totb / pixcount);
- if tota>$FF then tota:=$FF;
- if totr>$FF then totr:=$FF;
- if totg>$FF then totg:=$FF;
- if totb>$FF then totb:=$FF;
- col.a:=tota;
- col.r:=totr;
- col.g:=totg;
- col.b:=totb;
- fpcol:=Packed2FPColor(col);
- if palcache.Get(fpcol)<>nil then { already found, try the middle color }
- begin
- fpcol:=Packed2FPColor(arr[(box.startindex+box.endindex) div 2]^.Col);
- if palcache.Get(fpcol)<>nil then { already found, try the first unused color }
- for i:=box.startindex to box.endindex do
- begin
- col.a:=arr[i]^.Col.A;
- col.r:=arr[i]^.Col.R;
- col.g:=arr[i]^.Col.G;
- col.b:=arr[i]^.Col.B;
- fpcol:=Packed2FPColor(col);
- if palcache.Get(fpcol)=nil then break;
- end;
- end;
- palcache.Insert(fpcol,nil);
- Result:=fpcol;
- end;
- function TFPMedianCutQuantizer.BuildPalette : TFPPalette;
- var pal : TFPPalette;
- i : integer;
- begin
- pal:=TFPPalette.Create(Used);
- try
- palcache:=TFPColorHashTable.Create;
- try
- for i:=0 to Used-1 do
- begin
- pal.Color[i]:=MeanBox(boxes[i]);
- { ************************************************* }
- inc(percentacc);
- if percentacc>=percentinterval then
- begin
- percentacc:=percentacc mod percentinterval;
- inc(percent);
- Progress(self,psRunning,percent,'',FContinue);
- if not FContinue then exit;
- end;
- { ************************************************* }
- end
- finally
- palcache.Free;
- end;
- except
- pal.Free;
- raise;
- end;
- Result:=pal;
- end;
- { slow mode: no filtering
- normal mode: 8 bit r, 6 bit g, 6 bit b
- fast mode: 5 bit r, 5 bit g, 5 bit b }
- const mask_r_normal = $FFFF;
- mask_g_normal = $FCFC;
- mask_b_normal = $FCFC;
- mask_r_fast = $F8F8;
- mask_g_fast = $F8F8;
- mask_b_fast = $F8F8;
- function TFPMedianCutQuantizer.MaskColor(const col : TFPColor) : TFPColor;
- begin
- case FMode of
- mcNormal:
- begin
- Result.Red:=Col.Red and mask_r_normal;
- Result.Green:=Col.Green and mask_g_normal;
- Result.Blue:=Col.Blue and mask_b_normal;
- end;
- mcFast:
- begin
- Result.Red:=Col.Red and mask_r_fast;
- Result.Green:=Col.Green and mask_g_fast;
- Result.Blue:=Col.Blue and mask_b_fast;
- end
- else Result:=Col;
- end;
- end;
- function TFPMedianCutQuantizer.InternalQuantize : TFPPalette;
- var box : ^TMCBox;
- i, j, k : integer;
- dim : byte;
- boxpercent : longword;
- begin
- HashTable:=TFPColorHashTable.Create;
- try
- { *****************************************************************************
- Operations:
- width*height of each image (populate the hash table)
- number of desired colors for the box creation process (this should weight as the previous step)
- number of desired colors for building the palette.
- }
- percentinterval:=0;
- for k:=0 to FCount-1 do
- if FImages[k]<>nil then
- percentinterval:=percentinterval+FImages[k].Height*FImages[k].Width;
- boxpercent:=percentinterval div FColNum;
- percentinterval:=percentinterval*2+FColNum;
- percentinterval:=percentinterval div 100; { how many operations for 1% }
- if percentinterval=0 then percentinterval:=$FFFFFFFF; { it's quick, call progress only when starting and ending }
- percent:=0;
- percentacc:=0;
- FContinue:=true;
- Progress (self,psStarting,0,'',FContinue);
- if not FContinue then exit;
- { ***************************************************************************** }
- { For every color in the images, count how many pixels use it}
- for k:=0 to FCount-1 do
- if FImages[k]<>nil then
- for j:=0 to FImages[k].Height-1 do
- for i:=0 to FImages[k].Width-1 do
- begin
- HashTable.Add(MaskColor(FImages[k][i,j]),1);
- { ************************************************* }
- inc(percentacc);
- if percentacc>=percentinterval then
- begin
- percentacc:=percentacc mod percentinterval;
- inc(percent);
- Progress(self,psRunning,percent,'',FContinue);
- if not FContinue then exit;
- end;
- { ************************************************* }
- end;
- { Then let's have the list in array form }
- setlength(arr,0);
- arr:=HashTable.GetArray;
- try
- HashTable.Clear; { free some resources }
- setlength(boxes,FColNum);
- boxes[0].startindex:=0;
- boxes[0].endindex:=length(arr)-1;
- boxes[0].total:=boxes[0].endindex+1;
- Used:=1;
- while (used<FColNum) do
- begin
- box:=nil;
- { find a box with at least 2 colors }
- for i:=0 to Used-1 do
- if (boxes[i].total)>=2 then
- begin
- box:=@boxes[i];
- break;
- end;
- if box=nil then break;
- dim:=FindLargestDimension(box^);
- { sort the colors of the box along the largest dimension }
- QuickSort(box^.startindex,box^.endindex,dim);
- { Split the box: half of the colors in the first one, the rest in the second one }
- j:=(box^.startindex+box^.endindex) div 2;
- { This is the second box }
- boxes[Used].startindex:=j+1;
- boxes[Used].endindex:=box^.endindex;
- boxes[Used].total:=box^.endindex-j;
- { And here we update the first box }
- box^.endindex:=j;
- box^.total:=box^.endindex-box^.startindex+1;
- { Sort the boxes so that the first one is the one with higher number of colors }
- QuickSortBoxes(0,Used);
- inc(Used);
- { ************************************************* }
- inc(percentacc,boxpercent);
- if percentacc>=percentinterval then
- begin
- inc(percent,percentacc div percentinterval);
- percentacc:=percentacc mod percentinterval;
- Progress(self,psRunning,percent,'',FContinue);
- if not FContinue then exit;
- end;
- { ************************************************* }
- end;
- Result:=BuildPalette;
- if FContinue then Progress (self,psEnding,100,'',FContinue);
- finally
- setlength(boxes,0);
- for i:=0 to length(arr)-1 do
- FreeMem(arr[i]);
- setlength(arr,0);
- end;
- finally
- HashTable.Free;
- end;
- end;
- end.
|