PngImageList.pas 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576
  1. unit PngImageList;
  2. {$IF RTLVersion < 15.0 }
  3. This unit only compiles with Delphi 7 and higher!
  4. {$IFEND}
  5. interface
  6. uses
  7. Windows, Classes, SysUtils, Controls, Graphics, ImgList,
  8. {$IF CompilerVersion >= 34.0 Delphi 10.4 }
  9. System.UITypes,
  10. {$ENDIF}
  11. PngImage, PngFunctions;
  12. type
  13. INameMapping = interface
  14. ['{38EECDD8-7440-4EA2-BFD0-424E5BB2C1D5}']
  15. function GetName(Index: Integer): string;
  16. function IndexOfName(const AName: string): Integer;
  17. procedure ListNames(Target: TStrings);
  18. property Name[Index: Integer]: string read GetName;
  19. end;
  20. type
  21. TPngImageCollection = class;
  22. TPngImageCollectionItem = class;
  23. TPngImageCollectionItems = class;
  24. TPngImageList = class(TImageList, INameMapping)
  25. function INameMapping.GetName = GetImageName;
  26. function INameMapping.IndexOfName = FindIndexByName;
  27. procedure INameMapping.ListNames = ListImageNames;
  28. private
  29. FEnabledImages: Boolean;
  30. FImageNameAvailable: Boolean;
  31. FLocked: Integer;
  32. FOverlayIndex: array[TOverlay] of Integer;
  33. FPngImages: TPngImageCollectionItems;
  34. FPngOptions: TPngOptions;
  35. function ExtractOverlayIndex(Style: Cardinal): Integer;
  36. function GetHeight: Integer;
  37. function GetImageName(Index: Integer): string;
  38. function GetWidth: Integer;
  39. procedure SetHeight(const Value: Integer);
  40. procedure SetPngOptions(const Value: TPngOptions);
  41. procedure SetWidth(const Value: Integer);
  42. protected
  43. procedure AssignTo(Dest: TPersistent); override;
  44. procedure CopyPngs; virtual;
  45. procedure DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean = True); override;
  46. procedure InternalInsertPng(Index: Integer; const Png: TPngImage; Background:
  47. TColor = clNone);
  48. procedure InternalAddPng(const Png: TPngImage; Background: TColor = clNone);
  49. function PngToIcon(const Png: TPngImage; Background: TColor = clNone): HICON;
  50. procedure ReadData(Stream: TStream); override;
  51. procedure SetEnabledImages(const Value: Boolean); virtual;
  52. procedure SetPngImages(const Value: TPngImageCollectionItems); virtual;
  53. procedure WriteData(Stream: TStream); override;
  54. public
  55. constructor Create(AOwner: TComponent); override;
  56. destructor Destroy; override;
  57. //Patched methods
  58. function Add(Image, Mask: TBitmap): Integer; virtual;
  59. function AddIcon(Image: TIcon): Integer; virtual;
  60. function AddPng(Image: TPngImage; Background: TColor = clNone): Integer;
  61. function AddImage(Value: TCustomImageList; Index: Integer): Integer; virtual;
  62. procedure AddImages(Value: TCustomImageList); virtual;
  63. function AddMasked(Image: TBitmap; MaskColor: TColor): Integer; virtual;
  64. {$IF CompilerVersion >= 34.0 Delphi 10.4 Sydney }
  65. function AddDisabledImage(Value: TCustomImageList; Index: Integer): Integer; virtual;
  66. procedure AddDisabledImages(Value: TCustomImageList); virtual;
  67. {$ENDIF}
  68. procedure Assign(Source: TPersistent); override;
  69. procedure BeginUpdate;
  70. procedure Clear; virtual;
  71. procedure Delete(Index: Integer); virtual;
  72. procedure EndUpdate(Update: Boolean = True);
  73. function FindIndexByName(const AName: string): Integer;
  74. procedure Insert(Index: Integer; Image, Mask: TBitmap); virtual;
  75. procedure InsertIcon(Index: Integer; Image: TIcon); virtual;
  76. procedure InsertPng(Index: Integer; Image: TPngImage; Background: TColor = clNone);
  77. procedure InsertMasked(Index: Integer; Image: TBitmap; MaskColor: TColor); virtual;
  78. procedure ListImageNames(Target: TStrings);
  79. procedure Move(CurIndex, NewIndex: Integer); virtual;
  80. function Overlay(ImageIndex: Integer; Overlay: TOverlay): Boolean;
  81. procedure Replace(Index: Integer; Image, Mask: TBitmap); virtual;
  82. procedure ReplaceIcon(Index: Integer; Image: TIcon); virtual;
  83. procedure ReplaceMasked(Index: Integer; NewImage: TBitmap; MaskColor: TColor); virtual;
  84. {$IF CompilerVersion >= 34.0 Delphi 10.4 }
  85. function IsImageNameAvailable: Boolean; override;
  86. function GetIndexByName(const AName: TImageName): TImageIndex; override;
  87. function GetNameByIndex(AIndex: TImageIndex): TImageName; override;
  88. {$ENDIF}
  89. property ImageName[Index: Integer]: string read GetImageName;
  90. published
  91. property ColorDepth default cd32Bit;
  92. property EnabledImages: Boolean read FEnabledImages write SetEnabledImages default True;
  93. property Height read GetHeight write SetHeight default 16;
  94. property ImageNameAvailable: Boolean read FImageNameAvailable write FImageNameAvailable default False;
  95. property PngImages: TPngImageCollectionItems read FPngImages write SetPngImages;
  96. property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled];
  97. property Width read GetWidth write SetWidth default 16;
  98. end;
  99. TPngImageCollection = class(TComponent)
  100. private
  101. FItems: TPngImageCollectionItems;
  102. public
  103. constructor Create(AOwner: TComponent); override;
  104. destructor Destroy; override;
  105. published
  106. property Items: TPngImageCollectionItems read FItems write FItems;
  107. end;
  108. TPngImageCollectionItems = class(TCollection)
  109. private
  110. FOwner: TPersistent;
  111. function GetItem(Index: Integer): TPngImageCollectionItem;
  112. procedure SetItem(Index: Integer; const Value: TPngImageCollectionItem);
  113. protected
  114. function GetOwner: TPersistent; override;
  115. procedure Update(Item: TCollectionItem); override;
  116. public
  117. constructor Create(AOwner: TPersistent);
  118. function Add(DontCreatePNG: Boolean = False): TPngImageCollectionItem; reintroduce;
  119. procedure Assign(Source: TPersistent); override;
  120. function Insert(Index: Integer; DontCreatePNG: Boolean = False): TPngImageCollectionItem; reintroduce;
  121. property Items[index: Integer]: TPngImageCollectionItem read GetItem write SetItem; default;
  122. end;
  123. TPngImageCollectionItem = class(TCollectionItem)
  124. private
  125. FBackground: TColor;
  126. FName: string;
  127. FPngImage: TPngImage;
  128. procedure SetBackground(const Value: TColor);
  129. procedure SetPngImage(const Value: TPngImage);
  130. protected
  131. procedure AssignTo(Dest: TPersistent); override;
  132. function GetDisplayName: string; override;
  133. public
  134. constructor Create(Collection: TCollection); overload; override;
  135. constructor Create(Collection: TCollection; DontCreatePNG: Boolean = False); reintroduce; overload;
  136. destructor Destroy; override;
  137. procedure Assign(Source: TPersistent); override;
  138. function Duplicate: TPngImage;
  139. published
  140. property Background: TColor read FBackground write SetBackground default clBtnFace;
  141. property Name: string read FName write FName;
  142. property PngImage: TPngImage read FPngImage write SetPngImage;
  143. end;
  144. procedure CopyImageFromImageList(Dest: TPngImage; ImageList: TCustomImageList; Index: Integer);
  145. implementation
  146. uses
  147. Math, Contnrs, CommCtrl, ComCtrls;
  148. var
  149. ImageListCount: Integer = 0;
  150. MethodPatches: TObjectList = nil;
  151. type
  152. TMethodPatch = class
  153. private
  154. Name: string;
  155. OldBody: packed array[0..4] of Byte;
  156. OldPointer: Pointer;
  157. NewPointer: Pointer;
  158. public
  159. constructor Create;
  160. destructor Destroy; override;
  161. procedure BeginInvokeOldMethod;
  162. procedure FinishInvokeOldMethod;
  163. function PatchBack: Boolean;
  164. end;
  165. { Global }
  166. function FindMethodPatch(const Name: string): TMethodPatch;
  167. var
  168. I: Integer;
  169. begin
  170. Result := nil;
  171. for I := 0 to MethodPatches.Count - 1 do begin
  172. if TMethodPatch(MethodPatches[I]).Name = Name then begin
  173. Result := TMethodPatch(MethodPatches[I]);
  174. Break;
  175. end;
  176. end;
  177. end;
  178. {$IF RTLVersion > 18.0 }
  179. {$POINTERMATH ON}
  180. {$IFEND}
  181. function PatchPtr(OldPtr, NewPtr: Pointer; const Name: string; Patch: TMethodPatch): Boolean;
  182. var
  183. Access: Cardinal;
  184. memSize: Integer;
  185. opCode: PByte;
  186. operand: PInteger;
  187. begin
  188. Result := False;
  189. Patch.Name := Name;
  190. if OldPtr <> NewPtr then begin
  191. Patch.OldPointer := OldPtr;
  192. Patch.NewPointer := NewPtr;
  193. opCode := OldPtr;
  194. operand := OldPtr;
  195. Inc(PByte(operand));
  196. memSize := SizeOf(Patch.OldBody);
  197. Move(opCode^, Patch.OldBody[0], memSize);
  198. if VirtualProtect(OldPtr, 16, PAGE_EXECUTE_READWRITE, @Access) then begin
  199. opCode^ := $E9; // Near jump
  200. {$IF RTLVersion > 18.0 }
  201. operand^ := PByte(NewPtr) - PByte(OldPtr) - 5;
  202. {$ELSE}
  203. operand^ := PChar(NewPtr) - PChar(OldPtr) - 5;
  204. {$IFEND}
  205. VirtualProtect(OldPtr, 16, Access, @Access);
  206. // {$IF not (defined(CPU386) or defined(CPUX86) or defined(CPUX64)) }
  207. // FlushInstructionCache(GetCurrentProcess, OldPtr, memSize);
  208. // {$IFEND}
  209. Result := True;
  210. end;
  211. end;
  212. if not Result then
  213. Patch.OldPointer := nil;
  214. end;
  215. {$IF RTLVersion > 18.0 }
  216. {$POINTERMATH OFF}
  217. {$IFEND}
  218. procedure ApplyMethodPatches;
  219. type
  220. TPointerCombo = record
  221. OldPtr: Pointer;
  222. NewPtr: Pointer;
  223. Name: string;
  224. end;
  225. function Combo(const OldPtr, NewPtr: Pointer; const Name: string): TPointerCombo;
  226. begin
  227. Result.OldPtr := OldPtr;
  228. Result.NewPtr := NewPtr;
  229. Result.Name := Name;
  230. end;
  231. const
  232. EmptyCombo: TPointerCombo = (OldPtr: nil; NewPtr: nil; Name: '');
  233. var
  234. Pointers: array of TPointerCombo;
  235. Patch: TMethodPatch;
  236. I: Integer;
  237. begin
  238. if ImageListCount = 0 then begin
  239. {$IF CompilerVersion >= 34.0 Delphi 10.4 Sydney }
  240. SetLength(Pointers, 17);
  241. {$ELSE}
  242. SetLength(Pointers, 15);
  243. {$ENDIF}
  244. Pointers[0] := Combo(@TCustomImageList.Add, @TPngImageList.Add, 'Add');
  245. Pointers[1] := Combo(@TCustomImageList.AddIcon, @TPngImageList.AddIcon, 'AddIcon');
  246. Pointers[2] := Combo(@TCustomImageList.AddImage, @TPngImageList.AddImage, 'AddImage');
  247. Pointers[3] := Combo(@TCustomImageList.AddImages, @TPngImageList.AddImages, 'AddImages');
  248. Pointers[4] := Combo(@TCustomImageList.AddMasked, @TPngImageList.AddMasked, 'AddMasked');
  249. Pointers[5] := Combo(@TCustomImageList.Clear, @TPngImageList.Clear, 'Clear');
  250. Pointers[6] := Combo(@TCustomImageList.Delete, @TPngImageList.Delete, 'Delete');
  251. Pointers[7] := Combo(@TCustomImageList.Insert, @TPngImageList.Insert, 'Insert');
  252. Pointers[8] := Combo(@TCustomImageList.InsertIcon, @TPngImageList.InsertIcon, 'InsertIcon');
  253. Pointers[9] := Combo(@TCustomImageList.InsertMasked, @TPngImageList.InsertMasked, 'InsertMasked');
  254. Pointers[10] := Combo(@TCustomImageList.Move, @TPngImageList.Move, 'Move');
  255. Pointers[11] := Combo(@TCustomImageList.Replace, @TPngImageList.Replace, 'Replace');
  256. Pointers[12] := Combo(@TCustomImageList.ReplaceIcon, @TPngImageList.ReplaceIcon, 'ReplaceIcon');
  257. Pointers[13] := Combo(@TCustomImageList.ReplaceMasked, @TPngImageList.ReplaceMasked, 'ReplaceMasked');
  258. Pointers[14] := Combo(@TCustomImageList.Overlay, @TPngImageList.Overlay, 'Overlay');
  259. {$IF CompilerVersion >= 34.0 Delphi 10.4 Sydney }
  260. Pointers[15] := Combo(@TCustomImageList.AddDisabledImage, @TPngImageList.AddDisabledImage, 'AddDisabledImage');
  261. Pointers[16] := Combo(@TCustomImageList.AddDisabledImages, @TPngImageList.AddDisabledImages, 'AddDisabledImages');
  262. {$ENDIF}
  263. MethodPatches := TObjectList.Create;
  264. for I := Low(Pointers) to High(Pointers) do begin
  265. if Pointers[I].OldPtr <> nil then begin
  266. Patch := TMethodPatch.Create;
  267. if PatchPtr(Pointers[I].OldPtr, Pointers[I].NewPtr, Pointers[I].Name, Patch) then
  268. MethodPatches.Add(Patch)
  269. else
  270. Patch.Free;
  271. end;
  272. end;
  273. end;
  274. end;
  275. procedure RevertPatchedMethods;
  276. begin
  277. if ImageListCount = 0 then
  278. FreeAndNil(MethodPatches);
  279. end;
  280. procedure CopyImageFromImageList(Dest: TPngImage; ImageList: TCustomImageList; Index: Integer);
  281. var
  282. Icon: TIcon;
  283. IconInfo: TIconInfo;
  284. ColorBitmap, MaskBitmap: TBitmap;
  285. X, Y: Integer;
  286. AlphaLine: pngimage.PByteArray;
  287. Png: TPngImageCollectionItem;
  288. begin
  289. if ImageList is TPngImageList then begin
  290. //This is easy, just copy the PNG object from the imagelist to the PNG object
  291. //from the button
  292. Png := TPNGImageList(ImageList).PngImages[Index];
  293. if Png <> nil then
  294. Dest.Assign(Png.PngImage);
  295. end
  296. else begin
  297. Icon := TIcon.Create;
  298. ColorBitmap := TBitmap.Create;
  299. MaskBitmap := TBitmap.Create;
  300. try
  301. //Try to copy an icon to a PNG object, including transparency
  302. ImageList.GetIcon(Index, Icon);
  303. if GetIconInfo(Icon.Handle, IconInfo) then begin
  304. //First, pump the colors into the PNG object
  305. ColorBitmap.Handle := IconInfo.hbmColor;
  306. ColorBitmap.PixelFormat := pf24bit;
  307. Dest.Assign(ColorBitmap);
  308. //Finally, copy the transparency
  309. Dest.CreateAlpha;
  310. MaskBitmap.Handle := IconInfo.hbmMask;
  311. for Y := 0 to Dest.Height - 1 do begin
  312. AlphaLine := Dest.AlphaScanline[Y];
  313. for X := 0 to Dest.Width - 1 do
  314. AlphaLine^[X] := Integer(GetPixel(MaskBitmap.Canvas.Handle, X, Y) = COLORREF(clBlack)) * $FF;
  315. end;
  316. end;
  317. finally
  318. MaskBitmap.Free;
  319. ColorBitmap.Free;
  320. Icon.Free;
  321. end;
  322. end;
  323. end;
  324. { TMethodPatch }
  325. constructor TMethodPatch.Create;
  326. begin
  327. inherited Create;
  328. OldPointer := nil;
  329. end;
  330. destructor TMethodPatch.Destroy;
  331. begin
  332. if OldPointer <> nil then
  333. PatchBack;
  334. inherited Destroy;
  335. end;
  336. procedure TMethodPatch.BeginInvokeOldMethod;
  337. begin
  338. PatchBack;
  339. end;
  340. procedure TMethodPatch.FinishInvokeOldMethod;
  341. begin
  342. PatchPtr(OldPointer, NewPointer, Name, Self);
  343. end;
  344. function TMethodPatch.PatchBack: Boolean;
  345. var
  346. Access: Cardinal;
  347. begin
  348. Result := False;
  349. if VirtualProtect(OldPointer, 16, PAGE_EXECUTE_READWRITE, @Access) then begin
  350. Move(OldBody[0], OldPointer^, SizeOf(OldBody));
  351. VirtualProtect(OldPointer, 16, Access, @Access);
  352. Result := True;
  353. end;
  354. end;
  355. constructor TPngImageList.Create(AOwner: TComponent);
  356. var
  357. I: Integer;
  358. begin
  359. for I := Low(FOverlayIndex) to High(FOverlayIndex) do begin
  360. FOverlayIndex[I] := -1;
  361. end;
  362. inherited Create(AOwner);
  363. {$IF CompilerVersion >= 33.0 Delphi 10.3 Rio }
  364. StoreBitmap := False;
  365. {$ENDIF}
  366. FImageNameAvailable := False;
  367. ColorDepth := cd32Bit;
  368. if ImageListCount = 0 then
  369. ApplyMethodPatches;
  370. Inc(ImageListCount);
  371. FEnabledImages := True;
  372. FPngOptions := [pngBlendOnDisabled];
  373. FPngImages := TPngImageCollectionItems.Create(Self);
  374. FLocked := 0;
  375. end;
  376. destructor TPngImageList.Destroy;
  377. begin
  378. FPngImages.Free;
  379. Dec(ImageListCount);
  380. if ImageListCount = 0 then
  381. RevertPatchedMethods;
  382. inherited Destroy;
  383. end;
  384. //--- Patched methods ---
  385. function TPngImageList.Add(Image, Mask: TBitmap): Integer;
  386. var
  387. Patch: TMethodPatch;
  388. Png: TPngImage;
  389. begin
  390. if TObject(Self) is TPngImageList then begin
  391. Png := TPngImage.Create;
  392. try
  393. CreatePNG(Image, Mask, Png);
  394. result := AddPng(Png);
  395. finally
  396. Png.Free;
  397. end;
  398. end
  399. else begin
  400. Patch := FindMethodPatch('Add');
  401. if Patch <> nil then begin
  402. Patch.BeginInvokeOldMethod;
  403. try
  404. Result := TCustomImageList(Self).Add(Image, Mask);
  405. finally
  406. Patch.FinishInvokeOldMethod;
  407. end;
  408. end
  409. else
  410. Result := -1;
  411. end;
  412. end;
  413. function TPngImageList.AddIcon(Image: TIcon): Integer;
  414. var
  415. Patch: TMethodPatch;
  416. Png: TPngImage;
  417. begin
  418. if TObject(Self) is TPngImageList then begin
  419. Png := TPngImage.Create;
  420. try
  421. ConvertToPNG(Image, Png);
  422. result := AddPng(Png);
  423. finally
  424. Png.Free;
  425. end;
  426. end
  427. else begin
  428. Patch := FindMethodPatch('AddIcon');
  429. if Patch <> nil then begin
  430. Patch.BeginInvokeOldMethod;
  431. try
  432. Result := TCustomImageList(Self).AddIcon(Image);
  433. finally
  434. Patch.FinishInvokeOldMethod;
  435. end;
  436. end
  437. else
  438. Result := -1;
  439. end;
  440. end;
  441. function TPngImageList.AddPng(Image: TPngImage; Background: TColor = clNone):
  442. Integer;
  443. var
  444. Item: TPngImageCollectionItem;
  445. begin
  446. Result := -1;
  447. if Image = nil then Exit;
  448. BeginUpdate;
  449. try
  450. Item := FPngImages.Add(True);
  451. Item.PngImage := Image;
  452. Item.Background := Background;
  453. Result := Item.Index;
  454. InternalAddPng(Item.PngImage, Item.Background);
  455. Change;
  456. finally
  457. EndUpdate(false);
  458. end;
  459. end;
  460. function TPngImageList.AddImage(Value: TCustomImageList; Index: Integer): Integer;
  461. var
  462. Patch: TMethodPatch;
  463. Png: TPngImage;
  464. begin
  465. if TObject(Self) is TPngImageList then begin
  466. Png := TPngImage.Create;
  467. try
  468. CopyImageFromImageList(Png, Value, Index);
  469. if RTLVersion < 31.00 then begin
  470. result := AddPng(Png);
  471. end
  472. else begin
  473. { Since Berlin AddImage returns the new size of the list, while before it returned the index of the added image.
  474. Although this behaviour seems somewhat strange, it actually matches the documentation. }
  475. AddPng(Png);
  476. result := FPngImages.Count;
  477. end;
  478. finally
  479. Png.Free;
  480. end;
  481. end
  482. else begin
  483. Patch := FindMethodPatch('AddImage');
  484. if Patch <> nil then begin
  485. Patch.BeginInvokeOldMethod;
  486. try
  487. Result := TCustomImageList(Self).AddImage(Value, Index);
  488. finally
  489. Patch.FinishInvokeOldMethod;
  490. end;
  491. end
  492. else
  493. Result := -1;
  494. end;
  495. end;
  496. procedure TPngImageList.AddImages(Value: TCustomImageList);
  497. var
  498. Patch: TMethodPatch;
  499. I: Integer;
  500. Png: TPngImage;
  501. begin
  502. if TObject(Self) is TPngImageList then begin
  503. BeginUpdate;
  504. try
  505. //Copy every image from Value into this imagelist.
  506. Png := TPngImage.Create;
  507. try
  508. for I := 0 to Value.Count - 1 do begin
  509. CopyImageFromImageList(Png, Value, I);
  510. AddPng(Png);
  511. end;
  512. finally
  513. Png.Free;
  514. end;
  515. finally
  516. EndUpdate;
  517. end;
  518. end
  519. else begin
  520. Patch := FindMethodPatch('AddImages');
  521. if Patch <> nil then begin
  522. Patch.BeginInvokeOldMethod;
  523. try
  524. TCustomImageList(Self).AddImages(Value);
  525. finally
  526. Patch.FinishInvokeOldMethod;
  527. end;
  528. end;
  529. end;
  530. end;
  531. {$IF CompilerVersion >= 34.0 Delphi 10.4 Sydney }
  532. function TPngImageList.AddDisabledImage(Value: TCustomImageList; Index: Integer): Integer;
  533. var
  534. Patch: TMethodPatch;
  535. Png: TPngImage;
  536. begin
  537. if TObject(Self) is TPngImageList then begin
  538. Png := TPngImage.Create;
  539. try
  540. CopyImageFromImageList(Png, Value, Index);
  541. MakeDisabledImage(Png, PngOptions);
  542. AddPng(Png);
  543. result := FPngImages.Count;
  544. finally
  545. Png.Free;
  546. end;
  547. end
  548. else begin
  549. Patch := FindMethodPatch('AddDisabledImage');
  550. if Patch <> nil then begin
  551. Patch.BeginInvokeOldMethod;
  552. try
  553. Result := TCustomImageList(Self).AddDisabledImage(Value, Index);
  554. finally
  555. Patch.FinishInvokeOldMethod;
  556. end;
  557. end
  558. else
  559. Result := -1;
  560. end;
  561. end;
  562. procedure TPngImageList.AddDisabledImages(Value: TCustomImageList);
  563. var
  564. Patch: TMethodPatch;
  565. I: Integer;
  566. Png: TPngImage;
  567. begin
  568. if TObject(Self) is TPngImageList then begin
  569. BeginUpdate;
  570. try
  571. //Copy every image from Value into this imagelist.
  572. Png := TPngImage.Create;
  573. try
  574. for I := 0 to Value.Count - 1 do begin
  575. CopyImageFromImageList(Png, Value, I);
  576. MakeDisabledImage(Png, PngOptions);
  577. AddPng(Png);
  578. end;
  579. finally
  580. Png.Free;
  581. end;
  582. finally
  583. EndUpdate;
  584. end;
  585. end
  586. else begin
  587. Patch := FindMethodPatch('AddDisabledImages');
  588. if Patch <> nil then begin
  589. Patch.BeginInvokeOldMethod;
  590. try
  591. TCustomImageList(Self).AddDisabledImages(Value);
  592. finally
  593. Patch.FinishInvokeOldMethod;
  594. end;
  595. end;
  596. end;
  597. end;
  598. {$ENDIF}
  599. function TPngImageList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
  600. var
  601. Patch: TMethodPatch;
  602. Png: TPngImage;
  603. begin
  604. if TObject(Self) is TPngImageList then begin
  605. Png := TPngImage.Create;
  606. try
  607. CreatePNGMasked(Image, MaskColor, Png);
  608. result := AddPng(Png);
  609. finally
  610. Png.Free;
  611. end;
  612. end
  613. else begin
  614. Patch := FindMethodPatch('AddMasked');
  615. if Patch <> nil then begin
  616. Patch.BeginInvokeOldMethod;
  617. try
  618. Result := TCustomImageList(Self).AddMasked(Image, MaskColor);
  619. finally
  620. Patch.FinishInvokeOldMethod;
  621. end;
  622. end
  623. else
  624. Result := -1;
  625. end;
  626. end;
  627. procedure TPngImageList.Assign(Source: TPersistent);
  628. var
  629. pngSource: TPngImageList;
  630. begin
  631. if Source is TPngImageList then begin
  632. pngSource := TPngImageList(Source);
  633. BeginUpdate;
  634. try
  635. PngImages := pngSource.PngImages;
  636. EnabledImages := pngSource.EnabledImages;
  637. PngOptions := pngSource.PngOptions;
  638. finally
  639. EndUpdate(true);
  640. end;
  641. end;
  642. if Source is TCustomImageList then begin
  643. ColorDepth := TCustomImageList(Source).ColorDepth;
  644. end;
  645. inherited;
  646. end;
  647. procedure TPngImageList.AssignTo(Dest: TPersistent);
  648. var
  649. pngDest: TPngImageList;
  650. begin
  651. inherited;
  652. if Dest is TPngImageList then begin
  653. pngDest := TPngImageList(Dest);
  654. pngDest.PngImages := PngImages;
  655. pngDest.EnabledImages := EnabledImages;
  656. pngDest.PngOptions := PngOptions;
  657. end;
  658. end;
  659. procedure TPngImageList.BeginUpdate;
  660. begin
  661. Inc(FLocked);
  662. end;
  663. procedure TPngImageList.Clear;
  664. var
  665. Patch: TMethodPatch;
  666. begin
  667. if TObject(Self) is TPngImageList then begin
  668. //Clear the PngImages collection and the internal imagelist.
  669. BeginUpdate;
  670. try
  671. FPngImages.Clear;
  672. ImageList_Remove(Handle, -1);
  673. Change;
  674. finally
  675. EndUpdate(False);
  676. end;
  677. end
  678. else begin
  679. Patch := FindMethodPatch('Clear');
  680. if Patch <> nil then begin
  681. Patch.BeginInvokeOldMethod;
  682. try
  683. TCustomImageList(Self).Clear;
  684. finally
  685. Patch.FinishInvokeOldMethod;
  686. end;
  687. end;
  688. end;
  689. end;
  690. procedure TPngImageList.CopyPngs;
  691. var
  692. I: Integer;
  693. Png: TPngImage;
  694. Icon: HIcon;
  695. item: TPngImageCollectionItem;
  696. begin
  697. //Are we adding a bunch of images?
  698. if FLocked > 0 then
  699. Exit;
  700. //Copy PNG images to the imagelist. These images will not be stored in the DFM.
  701. ImageList_Remove(Handle, -1);
  702. Handle := ImageList_Create(Width, Height, ILC_COLOR32 or (Integer(Masked) *
  703. ILC_MASK), 0, AllocBy);
  704. Png := TPngImage.Create;
  705. try
  706. for I := 0 to FPngImages.Count - 1 do begin
  707. item := FPngImages.Items[I];
  708. if (item.PngImage = nil) or item.PngImage.Empty then
  709. Continue;
  710. if FEnabledImages or (FPngOptions = []) then begin
  711. Icon := PngToIcon(item.PngImage, item.Background);
  712. end
  713. else begin
  714. //Basically the same as in the DrawPNG function
  715. Png.Assign(item.PngImage);
  716. MakeDisabledImage(Png, PngOptions);
  717. Icon := PngToIcon(Png);
  718. end;
  719. ImageList_AddIcon(Handle, Icon);
  720. DestroyIcon(Icon);
  721. end;
  722. finally
  723. Png.Free;
  724. end;
  725. end;
  726. procedure TPngImageList.Delete(Index: Integer);
  727. var
  728. Patch: TMethodPatch;
  729. begin
  730. if TObject(Self) is TPngImageList then begin
  731. //Delete an image from the PngImages collection and from the internal imagelist.
  732. if (Index >= 0) and (Index < Count) then begin
  733. BeginUpdate;
  734. try
  735. FPngImages.Delete(Index);
  736. ImageList_Remove(Handle, Index);
  737. Change;
  738. finally
  739. EndUpdate(False);
  740. end;
  741. end;
  742. end
  743. else begin
  744. Patch := FindMethodPatch('Delete');
  745. if Patch <> nil then begin
  746. Patch.BeginInvokeOldMethod;
  747. try
  748. TCustomImageList(Self).Delete(Index);
  749. finally
  750. Patch.FinishInvokeOldMethod;
  751. end;
  752. end;
  753. end;
  754. end;
  755. //--- End of patched methods ---
  756. procedure TPngImageList.DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean);
  757. var
  758. PaintRect: TRect;
  759. Options: TPngOptions;
  760. IndexOfOverlay: Integer;
  761. Png: TPngImageCollectionItem;
  762. begin
  763. //Draw a PNG directly to the Canvas. This is the preferred method to call,
  764. //because this omits the API calls that use a fixed background.
  765. PaintRect := Bounds(X, Y, Width, Height);
  766. if Enabled then
  767. Options := []
  768. else
  769. Options := FPngOptions;
  770. Png := FPngImages.Items[Index];
  771. if Png <> nil then begin
  772. DrawPNG(Png.PngImage, Canvas, PaintRect, Options);
  773. IndexOfOverlay := ExtractOverlayIndex(Style);
  774. if (IndexOfOverlay >= 0) and (IndexOfOverlay < Count) then begin
  775. Png := PngImages.Items[IndexOfOverlay];
  776. if Png <> nil then begin
  777. DrawPNG(Png.PngImage, Canvas, PaintRect, Options);
  778. end;
  779. end;
  780. end;
  781. end;
  782. procedure TPngImageList.EndUpdate(Update: Boolean);
  783. begin
  784. Dec(FLocked);
  785. if Update and (FLocked = 0) then
  786. CopyPngs;
  787. end;
  788. function TPngImageList.ExtractOverlayIndex(Style: Cardinal): Integer;
  789. var
  790. idx: Cardinal;
  791. begin
  792. Result := -1;
  793. idx := Style and ILD_OVERLAYMASK;
  794. if idx > 0 then begin
  795. idx := idx shr 8;
  796. if (idx > 0) then begin
  797. Dec(idx);
  798. {$WARN COMPARISON_TRUE OFF }
  799. if (idx >= Low(FOverlayIndex)) and (idx <= High(FOverlayIndex)) then begin
  800. Result := FOverlayIndex[idx];
  801. end;
  802. {$WARN COMPARISON_TRUE DEFAULT }
  803. end;
  804. end;
  805. end;
  806. function TPngImageList.FindIndexByName(const AName: string): Integer;
  807. var
  808. I: Integer;
  809. begin
  810. Result := -1;
  811. for I := 0 to PngImages.Count - 1 do begin
  812. if SameText(PngImages[I].Name, AName) then begin
  813. Result := I;
  814. Break;
  815. end;
  816. end;
  817. end;
  818. function TPngImageList.GetHeight: Integer;
  819. begin
  820. Result := inherited Height;
  821. end;
  822. function TPngImageList.GetImageName(Index: Integer): string;
  823. var
  824. item: TPngImageCollectionItem;
  825. begin
  826. Result := '';
  827. item := PngImages[Index];
  828. if item <> nil then
  829. Result := item.Name;
  830. end;
  831. {$IF CompilerVersion >= 34.0 Delphi 10.4 }
  832. function TPngImageList.IsImageNameAvailable: Boolean;
  833. begin
  834. Result := FImageNameAvailable;
  835. end;
  836. function TPngImageList.GetIndexByName(const AName: TImageName): TImageIndex;
  837. begin
  838. Result := FindIndexByName(AName);
  839. end;
  840. function TPngImageList.GetNameByIndex(AIndex: TImageIndex): TImageName;
  841. begin
  842. Result := ImageName[AIndex];
  843. end;
  844. {$ENDIF}
  845. function TPngImageList.GetWidth: Integer;
  846. begin
  847. Result := inherited Width;
  848. end;
  849. procedure TPngImageList.Insert(Index: Integer; Image, Mask: TBitmap);
  850. var
  851. Patch: TMethodPatch;
  852. Png: TPngImage;
  853. begin
  854. if TObject(Self) is TPngImageList then begin
  855. //Insert a new PNG based on the image and its mask.
  856. if Image <> nil then begin
  857. Png := TPngImage.Create;
  858. try
  859. CreatePNG(Image, Mask, Png);
  860. InsertPng(Index, Png);
  861. finally
  862. Png.Free;
  863. end;
  864. end;
  865. end
  866. else begin
  867. Patch := FindMethodPatch('Insert');
  868. if Patch <> nil then begin
  869. Patch.BeginInvokeOldMethod;
  870. try
  871. TCustomImageList(Self).Insert(Index, Image, Mask);
  872. finally
  873. Patch.FinishInvokeOldMethod;
  874. end;
  875. end;
  876. end;
  877. end;
  878. procedure TPngImageList.InsertIcon(Index: Integer; Image: TIcon);
  879. var
  880. Patch: TMethodPatch;
  881. Png: TPngImage;
  882. begin
  883. if TObject(Self) is TPngImageList then begin
  884. //Insert a new PNG based on the image.
  885. if Image <> nil then begin
  886. Png := TPngImage.Create;
  887. try
  888. ConvertToPNG(Image, Png);
  889. InsertPng(Index, Png);
  890. finally
  891. Png.Free;
  892. end;
  893. end;
  894. end
  895. else begin
  896. Patch := FindMethodPatch('InsertIcon');
  897. if Patch <> nil then begin
  898. Patch.BeginInvokeOldMethod;
  899. try
  900. TCustomImageList(Self).InsertIcon(Index, Image);
  901. finally
  902. Patch.FinishInvokeOldMethod;
  903. end;
  904. end;
  905. end;
  906. end;
  907. procedure TPngImageList.InsertPng(Index: Integer; Image: TPngImage; Background:
  908. TColor = clNone);
  909. var
  910. Item: TPngImageCollectionItem;
  911. begin
  912. if Image <> nil then begin
  913. BeginUpdate;
  914. try
  915. Item := PngImages.Insert(Index, True);
  916. Item.PngImage := Image;
  917. Item.Background := Background;
  918. InternalInsertPng(Index, Item.PngImage, Item.Background);
  919. Change;
  920. finally
  921. EndUpdate(False);
  922. end;
  923. end;
  924. end;
  925. procedure TPngImageList.InsertMasked(Index: Integer; Image: TBitmap; MaskColor: TColor);
  926. var
  927. Patch: TMethodPatch;
  928. Png: TPngImage;
  929. begin
  930. if TObject(Self) is TPngImageList then begin
  931. //Insert a new PNG based on the image and a colored mask.
  932. if Image <> nil then begin
  933. Png := TPngImage.Create;
  934. try
  935. CreatePNGMasked(Image, MaskColor, Png);
  936. InsertPng(Index, Png);
  937. finally
  938. Png.Free;
  939. end;
  940. end;
  941. end
  942. else begin
  943. Patch := FindMethodPatch('InsertMasked');
  944. if Patch <> nil then begin
  945. Patch.BeginInvokeOldMethod;
  946. try
  947. TCustomImageList(Self).InsertMasked(Index, Image, MaskColor);
  948. finally
  949. Patch.FinishInvokeOldMethod;
  950. end;
  951. end;
  952. end;
  953. end;
  954. procedure TPngImageList.InternalInsertPng(Index: Integer; const Png: TPngImage;
  955. Background: TColor);
  956. var
  957. I: Integer;
  958. Icon: HICON;
  959. TempList: TPngImageList;
  960. begin
  961. TempList := TPngImageList(TComponentClass(ClassType).Create(nil));
  962. try
  963. TempList.Assign(Self);
  964. ImageList_RemoveAll(Handle);
  965. for I := 0 to Index - 1 do begin
  966. Icon := ImageList_GetIcon(TempList.Handle, I, ILD_NORMAL);
  967. ImageList_AddIcon(Handle, Icon);
  968. DestroyIcon(Icon);
  969. end;
  970. Icon := PngToIcon(Png, Background);
  971. ImageList_AddIcon(Handle, Icon);
  972. DestroyIcon(Icon);
  973. for I := Index to TempList.Count - 1 do begin
  974. Icon := ImageList_GetIcon(TempList.Handle, I, ILD_NORMAL);
  975. ImageList_AddIcon(Handle, Icon);
  976. DestroyIcon(Icon);
  977. end;
  978. finally
  979. TempList.Free;
  980. end;
  981. end;
  982. procedure TPngImageList.InternalAddPng(const Png: TPngImage; Background: TColor
  983. = clNone);
  984. var
  985. Icon: HICON;
  986. begin
  987. Icon := PngToIcon(Png, Background);
  988. try
  989. ImageList_AddIcon(Handle, Icon);
  990. finally
  991. DestroyIcon(Icon);
  992. end;
  993. end;
  994. procedure TPngImageList.ListImageNames(Target: TStrings);
  995. var
  996. I: Integer;
  997. begin
  998. for I := 0 to PngImages.Count - 1 do begin
  999. Target.Add(PngImages[I].Name);
  1000. end;
  1001. end;
  1002. procedure TPngImageList.Move(CurIndex, NewIndex: Integer);
  1003. var
  1004. Patch: TMethodPatch;
  1005. begin
  1006. if TObject(Self) is TPngImageList then begin
  1007. //Move an image from one position to another. Don't try doing so in the internal
  1008. //imagelist, just recreate it, since this method won't be called very often.
  1009. BeginUpdate;
  1010. try
  1011. ImageList_Remove(Handle, CurIndex);
  1012. InternalInsertPng(NewIndex, FPngImages[CurIndex].PngImage,
  1013. FPngImages[CurIndex].Background);
  1014. FPngImages[CurIndex].Index := NewIndex;
  1015. Change;
  1016. finally
  1017. EndUpdate(False);
  1018. end;
  1019. end
  1020. else begin
  1021. Patch := FindMethodPatch('Move');
  1022. if Patch <> nil then begin
  1023. Patch.BeginInvokeOldMethod;
  1024. try
  1025. TCustomImageList(Self).Move(CurIndex, NewIndex);
  1026. finally
  1027. Patch.FinishInvokeOldMethod;
  1028. end;
  1029. end;
  1030. end;
  1031. end;
  1032. function TPngImageList.Overlay(ImageIndex: Integer; Overlay: TOverlay): Boolean;
  1033. begin
  1034. Result := (ImageIndex >= 0) and (ImageIndex < Count);
  1035. FOverlayIndex[Overlay] := ImageIndex;
  1036. end;
  1037. function TPngImageList.PngToIcon(const Png: TPngImage; Background: TColor): HICON;
  1038. const
  1039. MaxRGBQuads = MaxInt div SizeOf(TRGBQuad) - 1;
  1040. type
  1041. TRGBQuadArray = array[0..MaxRGBQuads] of TRGBQuad;
  1042. PRGBQuadArray = ^TRGBQuadArray;
  1043. TBitmapInfo4 = packed record
  1044. bmiHeader: TBitmapV4Header;
  1045. bmiColors: array[0..0] of TRGBQuad;
  1046. end;
  1047. function PngToIcon32(Png: TPngImage): HIcon;
  1048. var
  1049. ImageBits: PRGBQuadArray;
  1050. BitmapInfo: TBitmapInfo4;
  1051. IconInfo: TIconInfo;
  1052. AlphaBitmap: HBitmap;
  1053. MaskBitmap: TBitmap;
  1054. X, Y: Integer;
  1055. AlphaLine: PByteArray;
  1056. HasAlpha, HasBitmask: Boolean;
  1057. Color, TransparencyColor: TColor;
  1058. begin
  1059. //Convert a PNG object to an alpha-blended icon resource
  1060. ImageBits := nil;
  1061. //Allocate a DIB for the color data and alpha channel
  1062. with BitmapInfo.bmiHeader do begin
  1063. bV4Size := SizeOf(BitmapInfo.bmiHeader);
  1064. bV4Width := Png.Width;
  1065. bV4Height := Png.Height;
  1066. bV4Planes := 1;
  1067. bV4BitCount := 32;
  1068. bV4V4Compression := BI_BITFIELDS;
  1069. bV4SizeImage := 0;
  1070. bV4XPelsPerMeter := 0;
  1071. bV4YPelsPerMeter := 0;
  1072. bV4ClrUsed := 0;
  1073. bV4ClrImportant := 0;
  1074. bV4RedMask := $00FF0000;
  1075. bV4GreenMask := $0000FF00;
  1076. bV4BlueMask := $000000FF;
  1077. bV4AlphaMask := $FF000000;
  1078. end;
  1079. AlphaBitmap := CreateDIBSection(0, PBitmapInfo(@BitmapInfo)^,
  1080. DIB_RGB_COLORS, Pointer(ImageBits), 0, 0);
  1081. try
  1082. //Spin through and fill it with a wash of color and alpha.
  1083. AlphaLine := nil;
  1084. HasAlpha := Png.Header.ColorType in [COLOR_GRAYSCALEALPHA,
  1085. COLOR_RGBALPHA];
  1086. HasBitmask := Png.TransparencyMode = ptmBit;
  1087. TransparencyColor := Png.TransparentColor;
  1088. for Y := 0 to Png.Height - 1 do begin
  1089. if HasAlpha then
  1090. AlphaLine := Png.AlphaScanline[Png.Height - Y - 1];
  1091. for X := 0 to Png.Width - 1 do begin
  1092. Color := Png.Pixels[X, Png.Height - Y - 1];
  1093. ImageBits^[Y * Png.Width + X].rgbRed := Color and $FF;
  1094. ImageBits^[Y * Png.Width + X].rgbGreen := Color shr 8 and $FF;
  1095. ImageBits^[Y * Png.Width + X].rgbBlue := Color shr 16 and $FF;
  1096. if HasAlpha then
  1097. ImageBits^[Y * Png.Width + X].rgbReserved := AlphaLine^[X]
  1098. else if HasBitmask then
  1099. ImageBits^[Y * Png.Width + X].rgbReserved := Integer(Color <>
  1100. TransparencyColor) * 255;
  1101. end;
  1102. end;
  1103. //Create an empty mask
  1104. MaskBitmap := TBitmap.Create;
  1105. try
  1106. MaskBitmap.Width := Png.Width;
  1107. MaskBitmap.Height := Png.Height;
  1108. MaskBitmap.PixelFormat := pf1bit;
  1109. MaskBitmap.Canvas.Brush.Color := clBlack;
  1110. MaskBitmap.Canvas.FillRect(Rect(0, 0, MaskBitmap.Width,
  1111. MaskBitmap.Height));
  1112. //Create the alpha blended icon
  1113. IconInfo.fIcon := True;
  1114. IconInfo.hbmColor := AlphaBitmap;
  1115. IconInfo.hbmMask := MaskBitmap.Handle;
  1116. Result := CreateIconIndirect(IconInfo);
  1117. finally
  1118. MaskBitmap.Free;
  1119. end;
  1120. finally
  1121. DeleteObject(AlphaBitmap);
  1122. end;
  1123. end;
  1124. function PngToIcon24(Png: TPngImage; Background: TColor): HIcon;
  1125. var
  1126. ColorBitmap, MaskBitmap: TBitmap;
  1127. X, Y: Integer;
  1128. AlphaLine: PByteArray;
  1129. IconInfo: TIconInfo;
  1130. TransparencyColor: TColor;
  1131. begin
  1132. ColorBitmap := TBitmap.Create;
  1133. MaskBitmap := TBitmap.Create;
  1134. try
  1135. ColorBitmap.Width := Png.Width;
  1136. ColorBitmap.Height := Png.Height;
  1137. ColorBitmap.PixelFormat := pf32bit;
  1138. MaskBitmap.Width := Png.Width;
  1139. MaskBitmap.Height := Png.Height;
  1140. MaskBitmap.PixelFormat := pf32bit;
  1141. //Draw the color bitmap
  1142. ColorBitmap.Canvas.Brush.Color := Background;
  1143. ColorBitmap.Canvas.FillRect(Rect(0, 0, Png.Width, Png.Height));
  1144. Png.Draw(ColorBitmap.Canvas, Rect(0, 0, Png.Width, Png.Height));
  1145. //Create the mask bitmap
  1146. if Png.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then
  1147. for Y := 0 to Png.Height - 1 do begin
  1148. AlphaLine := Png.AlphaScanline[Y];
  1149. for X := 0 to Png.Width - 1 do
  1150. if AlphaLine^[X] = 0 then
  1151. SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clWhite)
  1152. else
  1153. SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clBlack);
  1154. end
  1155. else if Png.TransparencyMode = ptmBit then begin
  1156. TransparencyColor := Png.TransparentColor;
  1157. for Y := 0 to Png.Height - 1 do
  1158. for X := 0 to Png.Width - 1 do
  1159. if Png.Pixels[X, Y] = TransparencyColor then
  1160. SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clWhite)
  1161. else
  1162. SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clBlack);
  1163. end;
  1164. //Create the icon
  1165. IconInfo.fIcon := True;
  1166. IconInfo.hbmColor := ColorBitmap.Handle;
  1167. IconInfo.hbmMask := MaskBitmap.Handle;
  1168. Result := CreateIconIndirect(IconInfo);
  1169. finally
  1170. ColorBitmap.Free;
  1171. MaskBitmap.Free;
  1172. end;
  1173. end;
  1174. begin
  1175. if GetComCtlVersion >= ComCtlVersionIE6 then begin
  1176. //Windows XP or later, using the modern method: convert every PNG to
  1177. //an icon resource with alpha channel
  1178. Result := PngToIcon32(Png);
  1179. end
  1180. else begin
  1181. //No Windows XP, using the legacy method: copy every PNG to a normal
  1182. //bitmap using a fixed background color
  1183. Result := PngToIcon24(Png, Background);
  1184. end;
  1185. end;
  1186. procedure TPngImageList.ReadData(Stream: TStream);
  1187. begin
  1188. if not (csReading in ComponentState) then
  1189. inherited;
  1190. //Make sure nothing gets read from the DFM
  1191. end;
  1192. procedure TPngImageList.Replace(Index: Integer; Image, Mask: TBitmap);
  1193. var
  1194. Item: TPngImageCollectionItem;
  1195. Patch: TMethodPatch;
  1196. Icon: HICON;
  1197. begin
  1198. if TObject(Self) is TPngImageList then begin
  1199. //Replace an existing PNG based with a new image and its mask.
  1200. if Image <> nil then begin
  1201. BeginUpdate;
  1202. try
  1203. Item := FPngImages[Index];
  1204. CreatePNG(Image, Mask, Item.PngImage);
  1205. Icon := PngToIcon(Item.PngImage, Item.Background);
  1206. ImageList_ReplaceIcon(Handle, Index, Icon);
  1207. DestroyIcon(Icon);
  1208. Change;
  1209. finally
  1210. EndUpdate(False);
  1211. end;
  1212. end;
  1213. end
  1214. else begin
  1215. Patch := FindMethodPatch('Replace');
  1216. if Patch <> nil then begin
  1217. Patch.BeginInvokeOldMethod;
  1218. try
  1219. TCustomImageList(Self).Replace(Index, Image, Mask);
  1220. finally
  1221. Patch.FinishInvokeOldMethod;
  1222. end;
  1223. end;
  1224. end;
  1225. end;
  1226. procedure TPngImageList.ReplaceIcon(Index: Integer; Image: TIcon);
  1227. var
  1228. Item: TPngImageCollectionItem;
  1229. Patch: TMethodPatch;
  1230. Icon: HICON;
  1231. begin
  1232. if TObject(Self) is TPngImageList then begin
  1233. //Replace an existing PNG based with a new image.
  1234. if Image <> nil then begin
  1235. BeginUpdate;
  1236. try
  1237. Item := FPngImages[Index];
  1238. ConvertToPNG(Image, Item.PngImage);
  1239. Icon := PngToIcon(Item.PngImage, Item.Background);
  1240. ImageList_ReplaceIcon(Handle, Index, Icon);
  1241. DestroyIcon(Icon);
  1242. Change;
  1243. finally
  1244. EndUpdate(False);
  1245. end;
  1246. end
  1247. end
  1248. else begin
  1249. Patch := FindMethodPatch('ReplaceIcon');
  1250. if Patch <> nil then begin
  1251. Patch.BeginInvokeOldMethod;
  1252. try
  1253. TCustomImageList(Self).ReplaceIcon(Index, Image);
  1254. finally
  1255. Patch.FinishInvokeOldMethod;
  1256. end;
  1257. end;
  1258. end;
  1259. end;
  1260. procedure TPngImageList.ReplaceMasked(Index: Integer; NewImage: TBitmap; MaskColor: TColor);
  1261. var
  1262. Item: TPngImageCollectionItem;
  1263. Patch: TMethodPatch;
  1264. Icon: HICON;
  1265. begin
  1266. if TObject(Self) is TPngImageList then begin
  1267. //Replace an existing PNG based with a new image and a colored mask.
  1268. if NewImage <> nil then begin
  1269. BeginUpdate;
  1270. try
  1271. Item := FPngImages[Index];
  1272. CreatePNGMasked(NewImage, MaskColor, Item.PngImage);
  1273. Icon := PngToIcon(Item.PngImage, Item.Background);
  1274. ImageList_ReplaceIcon(Handle, Index, Icon);
  1275. DestroyIcon(Icon);
  1276. Change;
  1277. finally
  1278. EndUpdate(False);
  1279. end;
  1280. end
  1281. end
  1282. else begin
  1283. Patch := FindMethodPatch('ReplaceMasked');
  1284. if Patch <> nil then begin
  1285. Patch.BeginInvokeOldMethod;
  1286. try
  1287. TCustomImageList(Self).ReplaceMasked(Index, NewImage, MaskColor);
  1288. finally
  1289. Patch.FinishInvokeOldMethod;
  1290. end;
  1291. end;
  1292. end;
  1293. end;
  1294. procedure TPngImageList.SetEnabledImages(const Value: Boolean);
  1295. begin
  1296. if FEnabledImages xor Value then begin
  1297. FEnabledImages := Value;
  1298. CopyPngs;
  1299. end;
  1300. end;
  1301. procedure TPngImageList.SetHeight(const Value: Integer);
  1302. begin
  1303. if inherited Height <> Value then begin
  1304. inherited Height := Value;
  1305. Clear;
  1306. end;
  1307. end;
  1308. procedure TPngImageList.SetPngImages(const Value: TPngImageCollectionItems);
  1309. begin
  1310. if FPngImages <> Value then begin
  1311. FPngImages.Assign(Value);
  1312. Change;
  1313. end;
  1314. end;
  1315. procedure TPngImageList.SetPngOptions(const Value: TPngOptions);
  1316. begin
  1317. if FPngOptions <> Value then begin
  1318. FPngOptions := Value;
  1319. CopyPngs;
  1320. end;
  1321. end;
  1322. procedure TPngImageList.SetWidth(const Value: Integer);
  1323. begin
  1324. if inherited Width <> Value then begin
  1325. inherited Width := Value;
  1326. Clear;
  1327. end;
  1328. end;
  1329. procedure TPngImageList.WriteData(Stream: TStream);
  1330. begin
  1331. if not (csWriting in ComponentState) then
  1332. inherited;
  1333. //Make sure nothing gets written to the DFM
  1334. end;
  1335. { TPngImageCollection }
  1336. constructor TPngImageCollection.Create(AOwner: TComponent);
  1337. begin
  1338. inherited Create(AOwner);
  1339. FItems := TPngImageCollectionItems.Create(Self);
  1340. end;
  1341. destructor TPngImageCollection.Destroy;
  1342. begin
  1343. FItems.Free;
  1344. inherited Destroy;
  1345. end;
  1346. { TPngImageCollectionItems }
  1347. constructor TPngImageCollectionItems.Create(AOwner: TPersistent);
  1348. begin
  1349. inherited Create(TPngImageCollectionItem);
  1350. FOwner := AOwner;
  1351. end;
  1352. function TPngImageCollectionItems.Add(DontCreatePNG: Boolean = False): TPngImageCollectionItem;
  1353. begin
  1354. {$WARN SYMBOL_DEPRECATED OFF}
  1355. Result := TPngImageCollectionItem.Create(Self, DontCreatePNG);
  1356. Added(TCollectionItem(Result));
  1357. end;
  1358. procedure TPngImageCollectionItems.Assign(Source: TPersistent);
  1359. begin
  1360. inherited Assign(Source);
  1361. Update(nil);
  1362. end;
  1363. function TPngImageCollectionItems.GetItem(Index: Integer): TPngImageCollectionItem;
  1364. begin
  1365. if (Index >= 0) and (Index < Count) then
  1366. Result := TPngImageCollectionItem(inherited Items[Index])
  1367. else
  1368. Result := nil;
  1369. end;
  1370. function TPngImageCollectionItems.GetOwner: TPersistent;
  1371. begin
  1372. Result := FOwner;
  1373. end;
  1374. function TPngImageCollectionItems.Insert(Index: Integer; DontCreatePNG: Boolean = False): TPngImageCollectionItem;
  1375. begin
  1376. Result := Add(DontCreatePNG);
  1377. Result.Index := Index;
  1378. end;
  1379. procedure TPngImageCollectionItems.SetItem(Index: Integer; const Value: TPngImageCollectionItem);
  1380. begin
  1381. if (Index >= 0) and (Index < Count) then
  1382. inherited Items[Index] := Value;
  1383. end;
  1384. procedure TPngImageCollectionItems.Update(Item: TCollectionItem);
  1385. begin
  1386. inherited Update(Item);
  1387. if FOwner is TPngImageList then
  1388. TPngImageList(FOwner).CopyPngs;
  1389. end;
  1390. constructor TPngImageCollectionItem.Create(Collection: TCollection);
  1391. begin
  1392. inherited Create(Collection);
  1393. FPngImage := TPngImage.Create;
  1394. FName := Format('PngImage%d', [Index]);
  1395. FBackground := clBtnFace;
  1396. end;
  1397. constructor TPngImageCollectionItem.Create(Collection: TCollection; DontCreatePNG: Boolean = False);
  1398. begin
  1399. inherited Create(Collection);
  1400. if DontCreatePng then
  1401. FPngImage := nil
  1402. else
  1403. FPngImage := TPngImage.Create;
  1404. FName := Format('PngImage%d', [Index]);
  1405. FBackground := clBtnFace;
  1406. end;
  1407. destructor TPngImageCollectionItem.Destroy;
  1408. begin
  1409. FPngImage.Free;
  1410. inherited Destroy;
  1411. end;
  1412. procedure TPngImageCollectionItem.Assign(Source: TPersistent);
  1413. begin
  1414. if Source is TPngImageCollectionItem then begin
  1415. PngImage.Assign(TPngImageCollectionItem(Source).PngImage);
  1416. Background := TPngImageCollectionItem(Source).Background;
  1417. Name := TPngImageCollectionItem(Source).Name;
  1418. end
  1419. else
  1420. inherited Assign(Source);
  1421. end;
  1422. { TPngImageCollectionItem }
  1423. procedure TPngImageCollectionItem.AssignTo(Dest: TPersistent);
  1424. begin
  1425. inherited AssignTo(Dest);
  1426. if (Dest is TPngImageCollectionItem) then
  1427. TPngImageCollectionItem(Dest).PngImage := PngImage;
  1428. end;
  1429. function TPngImageCollectionItem.Duplicate: TPngImage;
  1430. begin
  1431. Result := TPngImage.Create;
  1432. Result.Assign(FPngImage);
  1433. end;
  1434. function TPngImageCollectionItem.GetDisplayName: string;
  1435. begin
  1436. if Length(FName) = 0 then
  1437. Result := inherited GetDisplayName
  1438. else
  1439. Result := FName;
  1440. end;
  1441. procedure TPngImageCollectionItem.SetBackground(const Value: TColor);
  1442. begin
  1443. if FBackground <> Value then begin
  1444. FBackground := Value;
  1445. Changed(False);
  1446. end;
  1447. end;
  1448. procedure TPngImageCollectionItem.SetPngImage(const Value: TPngImage);
  1449. begin
  1450. if FPngImage = nil then
  1451. FPngImage := TPngImage.Create;
  1452. FPngImage.Assign(Value);
  1453. Changed(False);
  1454. end;
  1455. initialization
  1456. finalization
  1457. MethodPatches.Free;
  1458. end.