PngImageList.pas 44 KB

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