BitmapImage.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 without the TPicture bloat
  8. Also see TBitmapButton which is the TWinControl version
  9. }
  10. interface
  11. uses
  12. Windows, Controls, Graphics, Classes;
  13. type
  14. TPaintEvent = procedure(Sender: TObject; Canvas: TCanvas; var ARect: TRect) of object;
  15. TBitmapImageImplementation = record
  16. private
  17. FControl: TControl;
  18. public
  19. AutoSize: Boolean;
  20. AutoSizeExtraWidth, AutoSizeExtraHeight: Integer;
  21. BackColor: TColor;
  22. Bitmap: TBitmap;
  23. Center: Boolean;
  24. ReplaceColor: TColor;
  25. ReplaceWithColor: TColor;
  26. Stretch: Boolean;
  27. StretchedBitmap: TBitmap;
  28. StretchedBitmapValid: Boolean;
  29. OnPaint: TPaintEvent;
  30. procedure Init(const AControl: TControl; const AAutoSizeExtraWidth: Integer = 0;
  31. const AAutoSizeExtraHeight: Integer = 0);
  32. procedure DeInit;
  33. function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  34. procedure BitmapChanged(Sender: TObject);
  35. procedure SetAutoSize(Sender: TObject; Value: Boolean);
  36. procedure SetBackColor(Sender: TObject; Value: TColor);
  37. procedure SetBitmap(Value: TBitmap);
  38. procedure SetCenter(Sender: TObject; Value: Boolean);
  39. procedure SetReplaceColor(Sender: TObject; Value: TColor);
  40. procedure SetReplaceWithColor(Sender: TObject; Value: TColor);
  41. procedure SetStretch(Sender: TObject; Value: Boolean);
  42. function GetPalette: HPALETTE;
  43. procedure Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
  44. end;
  45. TBitmapImage = class(TGraphicControl)
  46. private
  47. FImpl: TBitmapImageImplementation;
  48. procedure SetBackColor(Value: TColor);
  49. procedure SetBitmap(Value: TBitmap);
  50. procedure SetCenter(Value: Boolean);
  51. procedure SetReplaceColor(Value: TColor);
  52. procedure SetReplaceWithColor(Value: TColor);
  53. procedure SetStretch(Value: Boolean);
  54. protected
  55. function GetPalette: HPALETTE; override;
  56. procedure Paint; override;
  57. procedure SetAutoSize(Value: Boolean); override;
  58. public
  59. constructor Create(AOwner: TComponent); override;
  60. destructor Destroy; override;
  61. function InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  62. published
  63. property Align;
  64. property Anchors;
  65. property AutoSize: Boolean read FImpl.AutoSize write SetAutoSize default False;
  66. property BackColor: TColor read FImpl.BackColor write SetBackColor default clBtnFace;
  67. property Center: Boolean read FImpl.Center write SetCenter default False;
  68. property DragCursor;
  69. property DragMode;
  70. property Enabled;
  71. property ParentShowHint;
  72. property Bitmap: TBitmap read FImpl.Bitmap write SetBitmap;
  73. property PopupMenu;
  74. property ShowHint;
  75. property Stretch: Boolean read FImpl.Stretch write SetStretch default False;
  76. property ReplaceColor: TColor read FImpl.ReplaceColor write SetReplaceColor default clNone;
  77. property ReplaceWithColor: TColor read FImpl.ReplaceWithColor write SetReplaceWithColor default clNone;
  78. property Visible;
  79. property OnClick;
  80. property OnDblClick;
  81. property OnDragDrop;
  82. property OnDragOver;
  83. property OnEndDrag;
  84. property OnMouseDown;
  85. property OnMouseMove;
  86. property OnMouseUp;
  87. property OnPaint: TPaintEvent read FImpl.OnPaint write FImpl.OnPaint;
  88. property OnStartDrag;
  89. end;
  90. procedure Register;
  91. implementation
  92. uses
  93. SysUtils, Math, Resample;
  94. procedure Register;
  95. begin
  96. RegisterComponents('JR', [TBitmapImage]);
  97. end;
  98. { TBitmapImageImplementation }
  99. procedure TBitmapImageImplementation.Init(const AControl: TControl;
  100. const AAutoSizeExtraWidth, AAutoSizeExtraHeight: Integer);
  101. begin
  102. FControl := AControl;
  103. AutoSizeExtraWidth := AAutoSizeExtraWidth;
  104. AutoSizeExtraHeight := AAutoSizeExtraHeight;
  105. Bitmap := TBitmap.Create;
  106. Bitmap.OnChange := BitmapChanged;
  107. BackColor := clNone;
  108. ReplaceColor := clNone;
  109. ReplaceWithColor := clNone;
  110. StretchedBitmap := TBitmap.Create;
  111. end;
  112. procedure TBitmapImageImplementation.DeInit;
  113. begin
  114. FreeAndNil(StretchedBitmap);
  115. FreeAndNil(Bitmap);
  116. end;
  117. function TBitmapImageImplementation.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  118. begin
  119. { Find the largest regular icon size smaller than the scaled image }
  120. var Size := 0;
  121. for var I := Length(AscendingTrySizes)-1 downto 0 do begin
  122. if (FControl.Width >= AscendingTrySizes[I]) and (FControl.Height >= AscendingTrySizes[I]) then begin
  123. Size := AscendingTrySizes[I];
  124. Break;
  125. end;
  126. end;
  127. if Size = 0 then
  128. Size := Min(FControl.Width, FControl.Height);
  129. { Load the desired icon }
  130. var Flags := LR_DEFAULTCOLOR;
  131. if Instance = 0 then
  132. Flags := Flags or LR_LOADFROMFILE;
  133. var Handle := LoadImage(Instance, Name, IMAGE_ICON, Size, Size, Flags);
  134. if Handle = 0 then
  135. Handle := LoadImage(Instance, Name, IMAGE_ICON, 0, 0, Flags);
  136. if Handle <> 0 then begin
  137. const Icon = TIcon.Create;
  138. try
  139. Icon.Handle := Handle;
  140. { Set sizes (overrides any scaling) }
  141. FControl.Width := Icon.Width;
  142. FControl.Height := Icon.Height;
  143. { Draw icon into bitmap }
  144. Bitmap.Canvas.Brush.Color := BkColor;
  145. Bitmap.Width := FControl.Width;
  146. Bitmap.Height := FControl.Height;
  147. Bitmap.Canvas.Draw(0, 0, Icon);
  148. Result := True;
  149. finally
  150. Icon.Free;
  151. end;
  152. end else
  153. Result := False;
  154. end;
  155. procedure TBitmapImageImplementation.BitmapChanged(Sender: TObject);
  156. begin
  157. StretchedBitmapValid := False;
  158. if AutoSize and (Bitmap.Width > 0) and (Bitmap.Height > 0) then
  159. FControl.SetBounds(FControl.Left, FControl.Top, Bitmap.Width + AutoSizeExtraWidth,
  160. Bitmap.Height + AutoSizeExtraHeight);
  161. if (Bitmap.Width >= FControl.Width) and (Bitmap.Height >= FControl.Height) then
  162. FControl.ControlStyle := FControl.ControlStyle + [csOpaque] - [csParentBackground]
  163. else
  164. FControl.ControlStyle := FControl.ControlStyle - [csOpaque] + [csParentBackground];
  165. FControl.Invalidate;
  166. end;
  167. procedure TBitmapImageImplementation.SetAutoSize(Sender: TObject; Value: Boolean);
  168. begin
  169. AutoSize := Value;
  170. BitmapChanged(Sender);
  171. end;
  172. procedure TBitmapImageImplementation.SetBackColor(Sender: TObject; Value: TColor);
  173. begin
  174. if BackColor <> Value then begin
  175. BackColor := Value;
  176. BitmapChanged(Sender);
  177. end;
  178. end;
  179. procedure TBitmapImageImplementation.SetBitmap(Value: TBitmap);
  180. begin
  181. Bitmap.Assign(Value);
  182. end;
  183. procedure TBitmapImageImplementation.SetCenter(Sender: TObject; Value: Boolean);
  184. begin
  185. if Center <> Value then begin
  186. Center := Value;
  187. BitmapChanged(Sender);
  188. end;
  189. end;
  190. procedure TBitmapImageImplementation.SetReplaceColor(Sender: TObject; Value: TColor);
  191. begin
  192. if ReplaceColor <> Value then begin
  193. ReplaceColor := Value;
  194. BitmapChanged(Sender);
  195. end;
  196. end;
  197. procedure TBitmapImageImplementation.SetReplaceWithColor(Sender: TObject; Value: TColor);
  198. begin
  199. if ReplaceWithColor <> Value then begin
  200. ReplaceWithColor := Value;
  201. BitmapChanged(Sender);
  202. end;
  203. end;
  204. procedure TBitmapImageImplementation.SetStretch(Sender: TObject; Value: Boolean);
  205. begin
  206. if Stretch <> Value then begin
  207. Stretch := Value;
  208. StretchedBitmap.Assign(nil);
  209. BitmapChanged(Sender);
  210. end;
  211. end;
  212. function TBitmapImageImplementation.GetPalette: HPALETTE;
  213. begin
  214. Result := Bitmap.Palette;
  215. end;
  216. procedure TBitmapImageImplementation.Paint(const Sender: TObject; const Canvas: TCanvas; var R: TRect);
  217. begin
  218. const Is32bit = (Bitmap.PixelFormat = pf32bit) and
  219. (Bitmap.AlphaFormat in [afDefined, afPremultiplied]);
  220. var W, H: Integer;
  221. var Bmp: TBitmap;
  222. if Stretch then begin
  223. W := R.Width;
  224. H := R.Height;
  225. Bmp := StretchedBitmap;
  226. if not StretchedBitmapValid or (StretchedBitmap.Width <> W) or
  227. (StretchedBitmap.Height <> H) then begin
  228. StretchedBitmapValid := True;
  229. if (Bitmap.Width = W) and (Bitmap.Height = H) then
  230. StretchedBitmap.Assign(Bitmap)
  231. else begin
  232. StretchedBitmap.Assign(nil);
  233. if not StretchBmp(Bitmap, StretchedBitmap, W, H, Is32bit) then begin
  234. if Is32bit then begin
  235. StretchedBitmapValid := False;
  236. Bmp := Bitmap;
  237. end else begin
  238. StretchedBitmap.Palette := CopyPalette(Bitmap.Palette);
  239. StretchedBitmap.Width := W;
  240. StretchedBitmap.Height := H;
  241. StretchedBitmap.Canvas.StretchDraw(Rect(0, 0, W, H), Bitmap);
  242. end;
  243. end;
  244. end;
  245. end;
  246. end else begin
  247. Bmp := Bitmap;
  248. W := Bmp.Width;
  249. H := Bmp.Height;
  250. end;
  251. if (BackColor <> clNone) and (Is32Bit or (Bmp.Width < FControl.Width) or (Bmp.Height < FControl.Height)) then begin
  252. Canvas.Brush.Style := bsSolid;
  253. Canvas.Brush.Color := BackColor;
  254. Canvas.FillRect(R);
  255. end;
  256. if csDesigning in FControl.ComponentState then begin
  257. Canvas.Pen.Style := psDash;
  258. Canvas.Brush.Style := bsClear;
  259. Canvas.Rectangle(0, 0, FControl.Width, FControl.Height);
  260. end;
  261. var X := R.Left;
  262. var Y := R.Top;
  263. if Center then begin
  264. Inc(X, (R.Width - W) div 2);
  265. if X < 0 then
  266. X := 0;
  267. Inc(Y, (R.Height - H) div 2);
  268. if Y < 0 then
  269. Y := 0;
  270. end;
  271. if not Is32bit and (ReplaceColor <> clNone) and (ReplaceWithColor <> clNone) then begin
  272. Canvas.Brush.Color := ReplaceWithColor;
  273. Canvas.BrushCopy(Rect(X, Y, X + W, Y + H), Bmp, Rect(0, 0, Bmp.Width, Bmp.Height), ReplaceColor);
  274. end else
  275. Canvas.Draw(X, Y, Bmp);
  276. if Assigned(OnPaint) then
  277. OnPaint(Sender, Canvas, R);
  278. end;
  279. { TBitmapImage }
  280. constructor TBitmapImage.Create(AOwner: TComponent);
  281. begin
  282. inherited;
  283. ControlStyle := ControlStyle + [csReplicatable];
  284. FImpl.Init(Self);
  285. FImpl.BackColor := clBtnFace;
  286. Width := 105;
  287. Height := 105;
  288. end;
  289. destructor TBitmapImage.Destroy;
  290. begin
  291. FImpl.DeInit;
  292. inherited;
  293. end;
  294. function TBitmapImage.InitializeFromIcon(const Instance: HINST; const Name: PChar; const BkColor: TColor; const AscendingTrySizes: array of Integer): Boolean;
  295. begin
  296. Result := FImpl.InitializeFromIcon(HInstance, Name, BkColor, AscendingTrySizes);
  297. end;
  298. procedure TBitmapImage.SetAutoSize(Value: Boolean);
  299. begin
  300. FImpl.SetAutoSize(Self, Value);
  301. end;
  302. procedure TBitmapImage.SetBackColor(Value: TColor);
  303. begin
  304. FImpl.SetBackColor(Self, Value);
  305. end;
  306. procedure TBitmapImage.SetBitmap(Value: TBitmap);
  307. begin
  308. FImpl.SetBitmap(Value);
  309. end;
  310. procedure TBitmapImage.SetCenter(Value: Boolean);
  311. begin
  312. FImpl.SetCenter(Self, Value);
  313. end;
  314. procedure TBitmapImage.SetReplaceColor(Value: TColor);
  315. begin
  316. FImpl.SetReplaceColor(Self, Value);
  317. end;
  318. procedure TBitmapImage.SetReplaceWithColor(Value: TColor);
  319. begin
  320. FImpl.SetReplaceWithColor(Self, Value);
  321. end;
  322. procedure TBitmapImage.SetStretch(Value: Boolean);
  323. begin
  324. FImpl.SetStretch(Self, Value);
  325. end;
  326. function TBitmapImage.GetPalette: HPALETTE;
  327. begin
  328. Result := FImpl.GetPalette;
  329. end;
  330. procedure TBitmapImage.Paint;
  331. begin
  332. var R := ClientRect;
  333. FImpl.Paint(Self, Canvas, R);
  334. end;
  335. end.