fpcolhash.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. {*****************************************************************************}
  2. {
  3. This file is part of the Free Pascal's "Free Components Library".
  4. Copyright (c) 2005 by Giulio Bernardi
  5. This file contains a color hash table.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. }
  12. {*****************************************************************************}
  13. {$mode objfpc}{$h+}
  14. unit FPColHash;
  15. interface
  16. uses sysutils, classes, fpimage;
  17. type TFPColorHashException = class(Exception);
  18. type
  19. PColHashSubNode = ^TColHashSubNode;
  20. TColHashSubNode = packed record
  21. index : byte;
  22. data : pointer;
  23. next : PColHashSubNode;
  24. end;
  25. type
  26. PColHashMainNode = ^TColHashMainNode;
  27. TColHashMainNode = packed record
  28. childs : array[0..16] of pointer; { can be either another MainNode or a SubNode }
  29. end;
  30. {
  31. HashMap configuration:
  32. childs[MSN(A)] level 0
  33. |_childs[LSN(A)] level 1
  34. |_childs[LSN(R)] level 2
  35. |_childs[LSN(G)] level 3
  36. |_childs[LSN(B)] level 4
  37. |_childs[(MSN(R) MSN(G) MSN (B)) div 256] level 5
  38. |_element [(MSN(R) MSN(G) MSN (B)) mod 256]
  39. Very low accesses to reach an element, not much memory occupation if alpha is rarely used, event with
  40. images with 500.000 colors.
  41. For extremely colorful images (near 2^24 colors used) using only 5 bits per channel keeps the map
  42. small and efficient
  43. }
  44. type
  45. TFPPackedColor = record
  46. R, G, B, A : byte;
  47. end;
  48. type
  49. TFPColorWeight = record
  50. Col : TFPPackedColor;
  51. Num : integer;
  52. end;
  53. PFPColorWeight = ^TFPColorWeight;
  54. TFPColorWeightArray = array of PFPColorWeight;
  55. type
  56. TFPColorHashTable = class
  57. private
  58. Root : PColHashMainNode;
  59. AllIntegers : boolean;
  60. FCount : longword;
  61. procedure FreeAllData;
  62. function AllocateMainNode : PColHashMainNode;
  63. function AllocateSubNode : PColHashSubNode;
  64. procedure DeallocateLinkedList(node : PColHashSubNode);
  65. procedure DeallocateMainNode(node : PColHashMainNode; level : byte);
  66. procedure CalculateIndexes(Col : TFPPackedColor; var ahi, alo, ri, gi, bi, partial, sub : byte);
  67. function CalculateColor(const ahi, alo, ri, gi, bi, partial, sub : byte) : TFPPackedColor;
  68. function SearchSubNode(start : PColHashSubNode; const index : byte ) : PColHashSubNode;
  69. function SearchSubNodeAllocate(var start : PColHashSubNode; const index : byte ) : PColHashSubNode;
  70. function Search(const Col : TFPPackedColor) : PColHashSubNode;
  71. function SearchAllocate(const Col : TFPPackedColor) : PColHashSubNode;
  72. protected
  73. public
  74. procedure Insert(const Col : TFPColor; const Value : integer);
  75. procedure Insert(const Col : TFPColor; const Value : pointer);
  76. procedure Add(const Col : TFPColor; const Value : integer);
  77. function Get(const Col : TFPColor) : pointer;
  78. procedure Clear;
  79. function GetArray : TFPColorWeightArray;
  80. property Count : longword read FCount;
  81. constructor Create;
  82. destructor Destroy; override;
  83. end;
  84. function FPColor2Packed(Col : TFPColor) : TFPPackedColor;
  85. function Packed2FPColor(Col : TFPPackedColor) : TFPColor;
  86. implementation
  87. function FPColor2Packed(Col : TFPColor) : TFPPackedColor;
  88. begin
  89. Result.R:=(Col.Red and $FF00) shr 8;
  90. Result.G:=(Col.Green and $FF00) shr 8;
  91. Result.B:=(Col.Blue and $FF00) shr 8;
  92. Result.A:=(Col.Alpha and $FF00) shr 8;
  93. end;
  94. function Packed2FPColor(Col : TFPPackedColor) : TFPColor;
  95. begin
  96. Result.Red:=(Col.R shl 8) + Col.R;
  97. Result.Green:=(Col.G shl 8) + Col.G;
  98. Result.Blue:=(Col.B shl 8) + Col.B;
  99. Result.Alpha:=(Col.A shl 8) + Col.A;
  100. end;
  101. constructor TFPColorHashTable.Create;
  102. begin
  103. Fcount:=0;
  104. AllIntegers:=true;
  105. Root:=nil;
  106. end;
  107. destructor TFPColorHashTable.Destroy;
  108. begin
  109. FreeAllData;
  110. inherited Destroy;
  111. end;
  112. procedure TFPColorHashTable.CalculateIndexes(Col : TFPPackedColor; var ahi, alo, ri, gi, bi, partial, sub : byte);
  113. var tmp : longword;
  114. begin
  115. ahi := (Col.A and $F0) shr 4;
  116. alo := (Col.A and $F);
  117. ri := (Col.R and $F);
  118. gi := (Col.G and $F);
  119. bi := (Col.B and $F);
  120. tmp:=((Col.R and $F0) shl 4) or (Col.G and $F0) or ((Col.B and $F0) shr 4);
  121. partial:=tmp div 256;
  122. sub:=tmp mod 256;
  123. end;
  124. function TFPColorHashTable.CalculateColor(const ahi, alo, ri, gi, bi, partial, sub : byte) : TFPPackedColor;
  125. var tmp : longword;
  126. col : TFPPackedColor;
  127. begin
  128. tmp:=(partial shl 8) + sub; //partial*256 + sub;
  129. col.A:=(ahi shl 4) or alo;
  130. col.R:=((tmp and $F00) shr 4) + ri;
  131. col.G:=(tmp and $0F0) + gi;
  132. col.B:=((tmp and $00F) shl 4) + bi;
  133. Result:=col;
  134. end;
  135. procedure TFPColorHashTable.FreeAllData;
  136. begin
  137. DeallocateMainNode(Root,0);
  138. Root:=nil;
  139. FCount:=0;
  140. AllIntegers:=true;
  141. end;
  142. function TFPColorHashTable.AllocateMainNode : PColHashMainNode;
  143. var tmp : PColHashMainNode;
  144. i : byte;
  145. begin
  146. Result:=nil;
  147. tmp:=getmem(sizeof(TColHashMainNode));
  148. if tmp=nil then raise TFPColorHashException.Create('Out of memory');
  149. for i:=0 to high(tmp^.childs) do
  150. tmp^.childs[i]:=nil;
  151. Result:=tmp;
  152. end;
  153. function TFPColorHashTable.AllocateSubNode : PColHashSubNode;
  154. var tmp : PColHashSubNode;
  155. begin
  156. Result:=nil;
  157. tmp:=getmem(sizeof(TColHashSubNode));
  158. if tmp=nil then raise TFPColorHashException.Create('Out of memory');
  159. tmp^.index:=0;
  160. tmp^.data:=nil;
  161. tmp^.next:=nil;
  162. inc(FCount);
  163. Result:=tmp;
  164. end;
  165. procedure TFPColorHashTable.DeallocateLinkedList(node : PColHashSubNode);
  166. var tmp : PColHashSubNode;
  167. begin
  168. while (node<>nil) do
  169. begin
  170. tmp:=node^.next;
  171. if node^.data<>nil then
  172. FreeMem(node^.data);
  173. FreeMem(node);
  174. node:=tmp;
  175. end;
  176. end;
  177. procedure TFPColorHashTable.DeallocateMainNode(node : PColHashMainNode; level : byte);
  178. var i : byte;
  179. begin
  180. if node=nil then exit;
  181. if level=5 then
  182. begin
  183. for i:=0 to high(node^.childs) do
  184. DeallocateLinkedList(node^.childs[i]);
  185. end
  186. else
  187. for i:=0 to high(node^.childs) do
  188. DeallocateMainNode(node^.childs[i],level+1);
  189. FreeMem(node);
  190. end;
  191. function TFPColorHashTable.SearchSubNode(start : PColHashSubNode; const index : byte ) : PColHashSubNode;
  192. var cur : PColHashSubNode;
  193. begin
  194. Result:=nil;
  195. cur:=start;
  196. while cur<>nil do
  197. begin
  198. if cur^.index=index then break
  199. else if cur^.index>index then exit; { exit and returns nil}
  200. cur:=cur^.next;
  201. end;
  202. Result:=cur;
  203. end;
  204. function TFPColorHashTable.SearchSubNodeAllocate(var start : PColHashSubNode; const index : byte ) : PColHashSubNode;
  205. var tmp, cur, prev : PColHashSubNode;
  206. begin
  207. Result:=nil;
  208. prev:=nil;
  209. cur:=start;
  210. while cur<>nil do
  211. begin
  212. if cur^.index=index then break
  213. else if cur^.index>index then {whoops, we must insert the new node before this one}
  214. begin
  215. tmp:=AllocateSubNode;
  216. tmp^.index:=index;
  217. tmp^.next:=cur;
  218. if prev<>nil then prev^.next:=tmp
  219. else start:=tmp;
  220. cur:=tmp;
  221. break;
  222. end;
  223. prev:=cur;
  224. cur:=cur^.next;
  225. end;
  226. if cur=nil then { not found! append to the end }
  227. begin
  228. cur:=AllocateSubNode;
  229. cur^.index:=index;
  230. prev^.next:=cur { start is always <> nil}
  231. end;
  232. Result:=cur;
  233. end;
  234. function TFPColorHashTable.Search(const Col : TFPPackedColor) : PColHashSubNode;
  235. var ahi, alo, ri, gi, bi, partial, sub : byte;
  236. tmpmain : PColHashMainNode;
  237. begin
  238. Result:=nil;
  239. CalculateIndexes(Col, ahi, alo, ri, gi, bi, partial, sub);
  240. if Root=nil then exit;
  241. if Root^.childs[ahi]=nil then exit;
  242. tmpmain:=Root^.childs[ahi];
  243. if tmpmain^.childs[alo]=nil then exit;
  244. tmpmain:=tmpmain^.childs[alo];
  245. if tmpmain^.childs[ri]=nil then exit;
  246. tmpmain:=tmpmain^.childs[ri];
  247. if tmpmain^.childs[gi]=nil then exit;
  248. tmpmain:=tmpmain^.childs[gi];
  249. if tmpmain^.childs[bi]=nil then exit;
  250. tmpmain:=tmpmain^.childs[bi];
  251. if tmpmain^.childs[partial]=nil then exit;
  252. Result:=SearchSubNode(tmpmain^.childs[partial],sub);
  253. end;
  254. { get the node; if there isn't, build the part of the tree }
  255. function TFPColorHashTable.SearchAllocate(const Col : TFPPackedColor) : PColHashSubNode;
  256. var ahi, alo, ri, gi, bi, partial, sub : byte;
  257. tmpmain : PColHashMainNode;
  258. begin
  259. Result:=nil;
  260. CalculateIndexes(Col, ahi, alo, ri, gi, bi, partial, sub);
  261. if Root=nil then Root:=AllocateMainNode;
  262. if Root^.childs[ahi]=nil then Root^.childs[ahi]:=AllocateMainNode;
  263. tmpmain:=Root^.childs[ahi];
  264. if tmpmain^.childs[alo]=nil then tmpmain^.childs[alo]:=AllocateMainNode;
  265. tmpmain:=tmpmain^.childs[alo];
  266. if tmpmain^.childs[ri]=nil then tmpmain^.childs[ri]:=AllocateMainNode;
  267. tmpmain:=tmpmain^.childs[ri];
  268. if tmpmain^.childs[gi]=nil then tmpmain^.childs[gi]:=AllocateMainNode;
  269. tmpmain:=tmpmain^.childs[gi];
  270. if tmpmain^.childs[bi]=nil then tmpmain^.childs[bi]:=AllocateMainNode;
  271. tmpmain:=tmpmain^.childs[bi];
  272. if tmpmain^.childs[partial]=nil then { newly-created linked list. }
  273. begin
  274. tmpmain^.childs[partial]:=AllocateSubNode;
  275. Result:=tmpmain^.childs[partial];
  276. Result^.index:=sub;
  277. exit;
  278. end;
  279. Result:=SearchSubNodeAllocate(tmpmain^.childs[partial],sub)
  280. end;
  281. procedure TFPColorHashTable.Insert(const Col : TFPColor; const Value : integer);
  282. var node : PColHashSubNode;
  283. begin
  284. node:=SearchAllocate(FPColor2Packed(col));
  285. node^.data:=getmem(sizeof(Value));
  286. integer(node^.data^):=value;
  287. end;
  288. procedure TFPColorHashTable.Insert(const Col : TFPColor; const Value : pointer);
  289. var node : PColHashSubNode;
  290. begin
  291. node:=SearchAllocate(FPColor2Packed(col));
  292. node^.data:=Value;
  293. AllIntegers:=false;
  294. end;
  295. procedure TFPColorHashTable.Add(const Col : TFPColor; const Value : integer);
  296. var node : PColHashSubNode;
  297. begin
  298. node:=SearchAllocate(FPColor2Packed(col));
  299. if node^.data=nil then
  300. begin
  301. node^.data:=getmem(sizeof(Value));
  302. integer(node^.data^):=0;
  303. end;
  304. inc(integer(node^.data^),value);
  305. end;
  306. function TFPColorHashTable.Get(const Col : TFPColor) : pointer;
  307. var node : PColHashSubNode;
  308. begin
  309. node:=Search(FPColor2Packed(col));
  310. if node<>nil then
  311. Result:=node^.data
  312. else
  313. Result:=nil;
  314. end;
  315. procedure TFPColorHashTable.Clear;
  316. begin
  317. FreeAllData;
  318. end;
  319. function TFPColorHashTable.GetArray : TFPColorWeightArray;
  320. var ahi, alo, ri, gi, bi, partial : byte;
  321. node : PColHashSubNode;
  322. i : longword;
  323. cw : PFPColorWeight;
  324. tmp1,tmp2,tmp3,tmp4,tmp5 : PColHashMainNode;
  325. begin
  326. if not AllIntegers then
  327. raise TFPColorHashException.Create('Hashtable data is not made by integers.');
  328. SetLength(Result,FCount);
  329. if Root=nil then exit;
  330. i:=0;
  331. for ahi:=0 to 15 do
  332. begin
  333. if Root^.childs[ahi]=nil then continue;
  334. tmp1:=Root^.childs[ahi];
  335. for alo:=0 to 15 do
  336. begin
  337. if tmp1^.childs[alo]=nil then continue;
  338. tmp2:=tmp1^.childs[alo];
  339. for ri:=0 to 15 do
  340. begin
  341. if tmp2^.childs[ri]=nil then continue;
  342. tmp3:=tmp2^.childs[ri];
  343. for gi:=0 to 15 do
  344. begin
  345. if tmp3^.childs[gi]=nil then continue;
  346. tmp4:=tmp3^.childs[gi];
  347. for bi:=0 to 15 do
  348. begin
  349. if tmp4^.childs[bi]=nil then continue;
  350. tmp5:=tmp4^.childs[bi];
  351. for partial:=0 to 15 do
  352. begin
  353. node:=tmp5^.childs[partial];
  354. while (node<>nil) do
  355. begin
  356. getmem(cw,sizeof(TFPColorWeight));
  357. if cw=nil then
  358. raise TFPColorHashException.Create('Out of memory');
  359. cw^.Col:=CalculateColor(ahi,alo,ri,gi,bi,partial,node^.index);
  360. cw^.Num:=integer(node^.data^);
  361. Result[i]:=cw;
  362. inc(i);
  363. node:=node^.next;
  364. end;
  365. end;
  366. end;
  367. end;
  368. end;
  369. end;
  370. end;
  371. end;
  372. end.