BitmapImage.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. unit BitmapImage;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. A TImage-like component for bitmaps and png files without the TPicture bloat
  8. Also supports other TGraphic types which can be assigned to a TBitmap
  9. Also see TBitmapButton which is the TWinControl version
  10. }
  11. interface
  12. uses
  13. Windows, ShellAPI, Controls, Graphics, Classes, Imaging.pngimage;
  14. type
  15. TPaintEvent = procedure(Sender: TObject; Canvas: TCanvas; var ARect: TRect) of object;
  16. TBitmapImageImplementation = record
  17. private
  18. FControl: TControl;
  19. public
  20. AutoSize: Boolean;
  21. AutoSizeExtraWidth, AutoSizeExtraHeight: Integer;
  22. BackColor: TColor;
  23. Bitmap: TBitmap;
  24. Center: Boolean;
  25. PngImage: TPngImage;
  26. ReplaceColor: TColor;
  27. ReplaceWithColor: TColor;
  28. Stretch: Boolean;
  29. StretchedBitmap: TBitmap;
  30. StretchedBitmapValid: Boolean;
  31. OnPaint: TPaintEvent;
  32. procedure Init(const AControl: TControl; const AAutoSizeExtraWidth: Integer = 0;
  33. const AAutoSizeExtraHeight: Integer = 0);
  34. procedure DeInit;
  35. function GetInitializeSize(const AscendingTrySizes: array of Integer): Integer;
  36. function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  37. function InitializeFromStockIcon(const Siid: SHSTOCKICONID; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  38. procedure BitmapChanged(Sender: TObject);
  39. procedure PngImageChanged(Sender: TObject);
  40. procedure SetAutoSize(Sender: TObject; Value: Boolean);
  41. procedure SetBackColor(Sender: TObject; Value: TColor);
  42. procedure SetBitmap(Value: TBitmap);
  43. procedure SetCenter(Sender: TObject; Value: Boolean);
  44. procedure SetGraphic(Value: TGraphic);
  45. procedure SetPngImage(Value: TPngImage);
  46. procedure SetReplaceColor(Sender: TObject; Value: TColor);
  47. procedure SetReplaceWithColor(Sender: TObject; Value: TColor);
  48. procedure SetStretch(Sender: TObject; Value: Boolean);
  49. function GetPalette: HPALETTE;
  50. procedure Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
  51. end;
  52. TBitmapImage = class(TGraphicControl)
  53. private
  54. FImpl: TBitmapImageImplementation;
  55. procedure SetBackColor(Value: TColor);
  56. procedure SetBitmap(Value: TBitmap);
  57. procedure SetCenter(Value: Boolean);
  58. procedure SetGraphic(Value: TGraphic);
  59. procedure SetPngImage(Value: TPngImage);
  60. procedure SetReplaceColor(Value: TColor);
  61. procedure SetReplaceWithColor(Value: TColor);
  62. procedure SetStretch(Value: Boolean);
  63. protected
  64. function GetPalette: HPALETTE; override;
  65. procedure Paint; override;
  66. procedure SetAutoSize(Value: Boolean); override;
  67. public
  68. constructor Create(AOwner: TComponent); override;
  69. destructor Destroy; override;
  70. function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  71. function InitializeFromStockIcon(const Siid: SHSTOCKICONID; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  72. property Bitmap: TBitmap read FImpl.Bitmap write SetBitmap;
  73. property Graphic: TGraphic write SetGraphic;
  74. published
  75. property Align;
  76. property Anchors;
  77. property AutoSize: Boolean read FImpl.AutoSize write SetAutoSize default False;
  78. property BackColor: TColor read FImpl.BackColor write SetBackColor default clBtnFace;
  79. property Center: Boolean read FImpl.Center write SetCenter default False;
  80. property DragCursor;
  81. property DragMode;
  82. property Enabled;
  83. property ParentShowHint;
  84. property PngImage: TPngImage read FImpl.PngImage write SetPngImage;
  85. property PopupMenu;
  86. property ShowHint;
  87. property Stretch: Boolean read FImpl.Stretch write SetStretch default False;
  88. property ReplaceColor: TColor read FImpl.ReplaceColor write SetReplaceColor default clNone;
  89. property ReplaceWithColor: TColor read FImpl.ReplaceWithColor write SetReplaceWithColor default clNone;
  90. property Visible;
  91. property OnClick;
  92. property OnDblClick;
  93. property OnDragDrop;
  94. property OnDragOver;
  95. property OnEndDrag;
  96. property OnMouseDown;
  97. property OnMouseMove;
  98. property OnMouseUp;
  99. property OnPaint: TPaintEvent read FImpl.OnPaint write FImpl.OnPaint;
  100. property OnStartDrag;
  101. end;
  102. procedure Register;
  103. implementation
  104. uses
  105. CommCtrl, SysUtils, Math, Themes, Resample;
  106. procedure Register;
  107. begin
  108. RegisterComponents('JR', [TBitmapImage]);
  109. end;
  110. { TBitmapImageImplementation }
  111. procedure TBitmapImageImplementation.Init(const AControl: TControl;
  112. const AAutoSizeExtraWidth, AAutoSizeExtraHeight: Integer);
  113. begin
  114. FControl := AControl;
  115. AutoSizeExtraWidth := AAutoSizeExtraWidth;
  116. AutoSizeExtraHeight := AAutoSizeExtraHeight;
  117. BackColor := clNone;
  118. Bitmap := TBitmap.Create;
  119. Bitmap.OnChange := BitmapChanged;
  120. PngImage := TPngImage.Create;
  121. PngImage.OnChange := PngImageChanged;
  122. ReplaceColor := clNone;
  123. ReplaceWithColor := clNone;
  124. StretchedBitmap := TBitmap.Create;
  125. end;
  126. procedure TBitmapImageImplementation.DeInit;
  127. begin
  128. FreeAndNil(StretchedBitmap);
  129. FreeAndNil(PngImage);
  130. FreeAndNil(Bitmap);
  131. end;
  132. function TBitmapImageImplementation.GetInitializeSize(const AscendingTrySizes: array of Integer): Integer;
  133. begin
  134. { Find the largest regular icon size smaller than the scaled image }
  135. Result := 0;
  136. for var I := Length(AscendingTrySizes)-1 downto 0 do begin
  137. if (FControl.Width >= AscendingTrySizes[I]) and (FControl.Height >= AscendingTrySizes[I]) then begin
  138. Result := AscendingTrySizes[I];
  139. Break;
  140. end;
  141. end;
  142. if Result = 0 then
  143. Result := Min(FControl.Width, FControl.Height);
  144. end;
  145. function TBitmapImageImplementation.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  146. begin
  147. const Size = GetInitializeSize(AscendingTrySizes);
  148. { Load the desired icon }
  149. var Flags := LR_DEFAULTCOLOR;
  150. if Instance = 0 then
  151. Flags := Flags or LR_LOADFROMFILE;
  152. var Handle := LoadImage(Instance, Name, IMAGE_ICON, Size, Size, Flags);
  153. if Handle = 0 then
  154. Handle := LoadImage(Instance, Name, IMAGE_ICON, 0, 0, Flags);
  155. if Handle <> 0 then begin
  156. const Icon = TIcon.Create;
  157. try
  158. Icon.Handle := Handle;
  159. { Set sizes (overrides any scaling) }
  160. FControl.Width := Icon.Width;
  161. FControl.Height := Icon.Height;
  162. { Set bitmap }
  163. AutoSize := False;
  164. BackColor := BkColor;
  165. Stretch := True;
  166. Bitmap.Assign(Icon);
  167. Result := True;
  168. finally
  169. Icon.Free;
  170. end;
  171. end else
  172. Result := False;
  173. end;
  174. const
  175. IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
  176. function TBitmapImageImplementation.InitializeFromStockIcon(const Siid: SHSTOCKICONID; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  177. begin
  178. Result := False;
  179. var SHStockIconInfo: TSHStockIconInfo;
  180. SHStockIconInfo.cbSize := SizeOf(SHStockIconInfo);
  181. if Succeeded(SHGetStockIconInfo(siid, SHGSI_SYSICONINDEX, SHStockIconInfo)) then begin
  182. var ImageList: HIMAGELIST;
  183. { The SHGetImageList documentation remarks that SHIL_SMALL and SHIL_LARGE are DPI-aware. However
  184. because this does not provide per-monitor DPI awareness, we always use SHIL_JUMBO and perform
  185. scaling ourselves. It also remarks that "the IImageList pointer type, such as that returned in
  186. the ppv parameter can be cast as an HIMAGELIST as needed", and we make use of that. }
  187. const Size = GetInitializeSize(AscendingTrySizes);
  188. var iImageList: Integer;
  189. if Size > 24 then
  190. iImageList := SHIL_JUMBO
  191. else
  192. iImageList := SHIL_EXTRALARGE; { For small images use SHIL_EXTRALARGE, which should be 48x48 at least }
  193. if Succeeded(SHGetImageList(iImageList, IID_IImageList, Pointer(ImageList))) then begin
  194. var Handle := ImageList_GetIcon(ImageList, SHStockIconInfo.iSysImageIndex, ILD_TRANSPARENT);
  195. if Handle <> 0 then begin
  196. const Icon = TIcon.Create;
  197. try
  198. Icon.Handle := Handle;
  199. { Set sizes (overrides any scaling) }
  200. FControl.Width := Size;
  201. FControl.Height := Size;
  202. { Set bitmap }
  203. AutoSize := False;
  204. BackColor := BkColor;
  205. Stretch := True;
  206. Bitmap.Assign(Icon);
  207. Result := True;
  208. finally
  209. Icon.Free;
  210. end;
  211. end;
  212. end;
  213. end;
  214. end;
  215. procedure TBitmapImageImplementation.BitmapChanged(Sender: TObject);
  216. begin
  217. StretchedBitmapValid := False;
  218. if AutoSize and (Bitmap.Width > 0) and (Bitmap.Height > 0) then
  219. FControl.SetBounds(FControl.Left, FControl.Top, Bitmap.Width + AutoSizeExtraWidth,
  220. Bitmap.Height + AutoSizeExtraHeight);
  221. FControl.Invalidate;
  222. end;
  223. procedure TBitmapImageImplementation.PngImageChanged(Sender: TObject);
  224. begin
  225. Bitmap.Assign(PngImage);
  226. end;
  227. procedure TBitmapImageImplementation.SetAutoSize(Sender: TObject; Value: Boolean);
  228. begin
  229. AutoSize := Value;
  230. BitmapChanged(Sender);
  231. end;
  232. procedure TBitmapImageImplementation.SetBackColor(Sender: TObject; Value: TColor);
  233. begin
  234. if BackColor <> Value then begin
  235. BackColor := Value;
  236. BitmapChanged(Sender);
  237. end;
  238. end;
  239. procedure TBitmapImageImplementation.SetBitmap(Value: TBitmap);
  240. begin
  241. Bitmap.Assign(Value);
  242. end;
  243. procedure TBitmapImageImplementation.SetCenter(Sender: TObject; Value: Boolean);
  244. begin
  245. if Center <> Value then begin
  246. Center := Value;
  247. BitmapChanged(Sender);
  248. end;
  249. end;
  250. procedure TBitmapImageImplementation.SetGraphic(Value: TGraphic);
  251. begin
  252. if Value is TPngImage then
  253. SetPngImage(Value as TPngImage)
  254. else
  255. Bitmap.Assign(Value);
  256. end;
  257. procedure TBitmapImageImplementation.SetPngImage(Value: TPngImage);
  258. begin
  259. PngImage.Assign(Value);
  260. end;
  261. procedure TBitmapImageImplementation.SetReplaceColor(Sender: TObject; Value: TColor);
  262. begin
  263. if ReplaceColor <> Value then begin
  264. ReplaceColor := Value;
  265. BitmapChanged(Sender);
  266. end;
  267. end;
  268. procedure TBitmapImageImplementation.SetReplaceWithColor(Sender: TObject; Value: TColor);
  269. begin
  270. if ReplaceWithColor <> Value then begin
  271. ReplaceWithColor := Value;
  272. BitmapChanged(Sender);
  273. end;
  274. end;
  275. procedure TBitmapImageImplementation.SetStretch(Sender: TObject; Value: Boolean);
  276. begin
  277. if Stretch <> Value then begin
  278. Stretch := Value;
  279. StretchedBitmap.Assign(nil);
  280. BitmapChanged(Sender);
  281. end;
  282. end;
  283. function TBitmapImageImplementation.GetPalette: HPALETTE;
  284. begin
  285. Result := Bitmap.Palette;
  286. end;
  287. procedure TBitmapImageImplementation.Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
  288. begin
  289. const Is32bit = Bitmap.SupportsPartialTransparency;
  290. var W, H: Integer;
  291. var Bmp: TBitmap;
  292. if Stretch then begin
  293. W := R.Width;
  294. H := R.Height;
  295. Bmp := StretchedBitmap;
  296. if not StretchedBitmapValid or (StretchedBitmap.Width <> W) or
  297. (StretchedBitmap.Height <> H) then begin
  298. StretchedBitmapValid := True;
  299. if (Bitmap.Width = W) and (Bitmap.Height = H) then
  300. StretchedBitmap.Assign(Bitmap)
  301. else begin
  302. StretchedBitmap.Assign(nil);
  303. if not StretchBmp(Bitmap, StretchedBitmap, W, H, Is32bit) then begin
  304. if Is32bit then begin
  305. StretchedBitmapValid := False;
  306. Bmp := Bitmap;
  307. end else begin
  308. StretchedBitmap.Palette := CopyPalette(Bitmap.Palette);
  309. StretchedBitmap.Width := W;
  310. StretchedBitmap.Height := H;
  311. StretchedBitmap.Canvas.StretchDraw(Rect(0, 0, W, H), Bitmap);
  312. end;
  313. end;
  314. end;
  315. end;
  316. end else begin
  317. Bmp := Bitmap;
  318. W := Bmp.Width;
  319. H := Bmp.Height;
  320. end;
  321. if (BackColor <> clNone) and (Is32Bit or (Bmp.Width < FControl.Width) or (Bmp.Height < FControl.Height)) then begin
  322. var BrushColor := BackColor;
  323. if ((BrushColor = clBtnFace) or (BrushColor = clWindow)) and (Sender is TControl) then begin
  324. var LStyle := StyleServices(TControl(Sender));
  325. if not LStyle.Enabled or LStyle.IsSystemStyle then
  326. LStyle := nil;
  327. if LStyle <> nil then begin
  328. if BrushColor = clBtnFace then
  329. BrushColor := LStyle.GetStyleColor(scPanel)
  330. else
  331. BrushColor := LStyle.GetStyleColor(scWindow);
  332. end;
  333. end;
  334. Canvas.Brush.Style := bsSolid;
  335. Canvas.Brush.Color := BrushColor;
  336. Canvas.FillRect(R);
  337. end;
  338. if csDesigning in FControl.ComponentState then begin
  339. Canvas.Pen.Style := psDash;
  340. Canvas.Brush.Style := bsClear;
  341. Canvas.Rectangle(0, 0, FControl.Width, FControl.Height);
  342. end;
  343. var X := R.Left;
  344. var Y := R.Top;
  345. if Center then begin
  346. Inc(X, (R.Width - W) div 2);
  347. if X < 0 then
  348. X := 0;
  349. Inc(Y, (R.Height - H) div 2);
  350. if Y < 0 then
  351. Y := 0;
  352. end;
  353. if not Is32bit and (ReplaceColor <> clNone) and (ReplaceWithColor <> clNone) then begin
  354. Canvas.Brush.Color := ReplaceWithColor;
  355. Canvas.BrushCopy(Rect(X, Y, X + W, Y + H), Bmp, Rect(0, 0, Bmp.Width, Bmp.Height), ReplaceColor);
  356. end else
  357. Canvas.Draw(X, Y, Bmp);
  358. if Assigned(OnPaint) then
  359. OnPaint(Sender, Canvas, R);
  360. end;
  361. { TBitmapImage }
  362. constructor TBitmapImage.Create(AOwner: TComponent);
  363. begin
  364. inherited;
  365. ControlStyle := ControlStyle + [csParentBackground, csReplicatable];
  366. FImpl.Init(Self);
  367. if IsCustomStyleActive then
  368. FImpl.BackColor := StyleServices(Self).GetStyleColor(scWindow)
  369. else
  370. FImpl.BackColor := clBtnFace;
  371. Width := 105;
  372. Height := 105;
  373. end;
  374. destructor TBitmapImage.Destroy;
  375. begin
  376. FImpl.DeInit;
  377. inherited;
  378. end;
  379. function TBitmapImage.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  380. begin
  381. Result := FImpl.InitializeFromIcon(HInstance, Name, BkColor, AscendingTrySizes);
  382. end;
  383. function TBitmapImage.InitializeFromStockIcon(const Siid: SHSTOCKICONID; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  384. begin
  385. Result := FImpl.InitializeFromStockIcon(siid, BkColor, AscendingTrySizes);
  386. end;
  387. procedure TBitmapImage.SetAutoSize(Value: Boolean);
  388. begin
  389. FImpl.SetAutoSize(Self, Value);
  390. end;
  391. procedure TBitmapImage.SetBackColor(Value: TColor);
  392. begin
  393. FImpl.SetBackColor(Self, Value);
  394. end;
  395. procedure TBitmapImage.SetBitmap(Value: TBitmap);
  396. begin
  397. FImpl.SetBitmap(Value);
  398. end;
  399. procedure TBitmapImage.SetCenter(Value: Boolean);
  400. begin
  401. FImpl.SetCenter(Self, Value);
  402. end;
  403. procedure TBitmapImage.SetGraphic(Value: TGraphic);
  404. begin
  405. FImpl.SetGraphic(Value);
  406. end;
  407. procedure TBitmapImage.SetPngImage(Value: TPngImage);
  408. begin
  409. FImpl.SetPngImage(Value);
  410. end;
  411. procedure TBitmapImage.SetReplaceColor(Value: TColor);
  412. begin
  413. FImpl.SetReplaceColor(Self, Value);
  414. end;
  415. procedure TBitmapImage.SetReplaceWithColor(Value: TColor);
  416. begin
  417. FImpl.SetReplaceWithColor(Self, Value);
  418. end;
  419. procedure TBitmapImage.SetStretch(Value: Boolean);
  420. begin
  421. FImpl.SetStretch(Self, Value);
  422. end;
  423. function TBitmapImage.GetPalette: HPALETTE;
  424. begin
  425. Result := FImpl.GetPalette;
  426. end;
  427. procedure TBitmapImage.Paint;
  428. begin
  429. var R := ClientRect;
  430. FImpl.Paint(Self, Canvas, R);
  431. end;
  432. end.