GR32.Design.BitmapEditor.pas 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. unit GR32.Design.BitmapEditor;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Alex A. Denisov
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2009
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$include GR32.inc}
  33. uses
  34. {$IFDEF FPC}
  35. LCLIntf, LCLType, RtlConsts, Buttons, LazIDEIntf, PropEdits,
  36. ComponentEditors,
  37. {$ELSE}
  38. Windows, ExtDlgs, ToolWin, Registry, ImgList, Consts, DesignIntf,
  39. DesignEditors, VCLEditors, Actions, System.ImageList,
  40. {$ENDIF}
  41. Forms, Controls, ComCtrls, ExtCtrls, StdCtrls, Graphics, Dialogs, Menus,
  42. SysUtils, Classes, Clipbrd, ActnList,
  43. GR32,
  44. GR32_Image,
  45. GR32_Layers;
  46. type
  47. TPictureEditorForm = class(TForm)
  48. TabSheetAlpha: TTabSheet;
  49. Bevel1: TBevel;
  50. Cancel: TButton;
  51. ButtonClear: TToolButton;
  52. ButtonCopy: TToolButton;
  53. ImageList: TImageList;
  54. TabSheetRGB: TTabSheet;
  55. ButtonLoad: TToolButton;
  56. MenuItemClear: TMenuItem;
  57. MenuItemCopy: TMenuItem;
  58. MenuItemInvert: TMenuItem;
  59. MenuItemLoad: TMenuItem;
  60. MenuItemPaste: TMenuItem;
  61. MenuItemSave: TMenuItem;
  62. mnSeparator: TMenuItem;
  63. mnSeparator2: TMenuItem;
  64. OKButton: TButton;
  65. PageControl: TPageControl;
  66. Panel1: TPanel;
  67. ButtonPaste: TToolButton;
  68. PopupMenu: TPopupMenu;
  69. ButtonSave: TToolButton;
  70. ToolBar: TToolBar;
  71. ToolButton2: TToolButton;
  72. ActionList: TActionList;
  73. ActionLoad: TAction;
  74. ActionSave: TAction;
  75. ActionClear: TAction;
  76. ActionCopy: TAction;
  77. ActionPaste: TAction;
  78. ActionInvert: TAction;
  79. TabSheetRGBA: TTabSheet;
  80. StatusBar: TStatusBar;
  81. LabelZoom: TLabel;
  82. ToolButton1: TToolButton;
  83. ButtonHelp: TToolButton;
  84. ActionHelp: TAction;
  85. ButtonGrid: TToolButton;
  86. ActionGrid: TAction;
  87. procedure ActionLoadExecute(Sender: TObject);
  88. procedure ActionSaveExecute(Sender: TObject);
  89. procedure ActionHasBitmapUpdate(Sender: TObject);
  90. procedure ActionClearExecute(Sender: TObject);
  91. procedure ActionPasteUpdate(Sender: TObject);
  92. procedure ActionCopyExecute(Sender: TObject);
  93. procedure ActionPasteExecute(Sender: TObject);
  94. procedure ActionInvertExecute(Sender: TObject);
  95. procedure ActionHelpExecute(Sender: TObject);
  96. procedure ActionGridExecute(Sender: TObject);
  97. procedure ActionGridUpdate(Sender: TObject);
  98. protected
  99. {$IFDEF PLATFORM_INDEPENDENT}
  100. FOpenDialog: TOpenDialog;
  101. FSaveDialog: TSaveDialog;
  102. {$ELSE}
  103. FOpenDialog: TOpenPictureDialog;
  104. FSaveDialog: TSavePictureDialog;
  105. {$ENDIF}
  106. FImageAllChannels: TImage32;
  107. FImageRGBChannels: TImage32;
  108. FImageAlphaChannel: TImage32;
  109. FLayerPixelGrid: TCustomLayer;
  110. procedure ImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  111. procedure ImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  112. procedure ImageChanged(Sender: TObject);
  113. function CurrentImage: TImage32;
  114. procedure ResetZoomAndCenter(Image: TImage32);
  115. procedure SyncZoomAndPan;
  116. public
  117. constructor Create(AOwner: TComponent); override;
  118. procedure LoadFromImage(Source: TPersistent);
  119. end;
  120. TBitmap32Editor = class(TComponent)
  121. private
  122. FBitmap32: TBitmap32;
  123. procedure SetBitmap32(Value: TBitmap32);
  124. public
  125. constructor Create(AOwner: TComponent); override;
  126. destructor Destroy; override;
  127. function Execute: Boolean;
  128. property Bitmap32: TBitmap32 read FBitmap32 write SetBitmap32;
  129. end;
  130. TBitmap32Property = class(TClassProperty
  131. {$IFDEF EXT_PROP_EDIT}
  132. , ICustomPropertyDrawing, ICustomPropertyDrawing80
  133. {$ENDIF}
  134. )
  135. public
  136. procedure Edit; override;
  137. function GetAttributes: TPropertyAttributes; override;
  138. function GetValue: string; override;
  139. procedure SetValue(const Value: string); override;
  140. {$IFDEF EXT_PROP_EDIT}
  141. { ICustomPropertyDrawing }
  142. procedure PropDrawName(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
  143. procedure PropDrawValue(Canvas: TCanvas; const ARect: TRect; ASelected: Boolean);
  144. { ICustomPropertyDrawing80 }
  145. function PropDrawNameRect(const ARect: TRect): TRect;
  146. function PropDrawValueRect(const ARect: TRect): TRect;
  147. {$ENDIF}
  148. end;
  149. TImage32Editor = class(TComponentEditor)
  150. public
  151. procedure ExecuteVerb(Index: Integer); override;
  152. function GetVerb(Index: Integer): string; override;
  153. function GetVerbCount: Integer; override;
  154. end;
  155. implementation
  156. uses
  157. Math,
  158. Types,
  159. GR32.ImageFormats,
  160. GR32_Filters,
  161. GR32_Resamplers,
  162. GR32_Backends_Generic;
  163. {$R *.dfm}
  164. {$R 'GR32.Design.BitmapEditor.res'}
  165. resourcestring
  166. sInfo = 'Width: %.0n, Height: %.0n';
  167. sInfoEmpty = '(empty)';
  168. sZoom = 'Zoom: %.0n%%';
  169. sHelp = 'Pan by clicking and dragging.'#13+
  170. 'Zoom with the mouse wheel.'#13+
  171. 'Reset zoom and center with the middle mouse button.';
  172. //------------------------------------------------------------------------------
  173. //
  174. // TPixelGridLayer
  175. //
  176. //------------------------------------------------------------------------------
  177. // Displays a pixel grid on top of the image
  178. //------------------------------------------------------------------------------
  179. type
  180. TPixelGridLayer = class(TCustomLayer)
  181. private
  182. FImage: TCustomImage32;
  183. FNeedStipple: array[0..1] of boolean;
  184. FStipple: array[0..1] of TArrayOfColor32;
  185. FPattern: array[0..1] of DWORD;
  186. FColorOn: array[0..1] of TColor32;
  187. FColorOff: array[0..1] of TColor32;
  188. procedure SetColorOff(const Index: Integer; const Value: TColor32);
  189. procedure SetColorOn(const Index: Integer; const Value: TColor32);
  190. procedure SetPattern(const Index: Integer; const Value: DWORD);
  191. procedure SetStipple(const Index: Integer; const Value: TArrayOfColor32);
  192. function GetStipple(const Index: Integer): TArrayOfColor32;
  193. protected
  194. procedure Paint(Buffer: TBitmap32); override;
  195. public
  196. constructor Create(ALayerCollection: TLayerCollection; AImage: TCustomImage32); reintroduce;
  197. class procedure CreateStipple(var Stipple: TArrayOfColor32; Pattern: DWORD; ColorOn, ColorOff: TColor32);
  198. property MajorStipple: TArrayOfColor32 index 0 read GetStipple write SetStipple;
  199. property MajorPattern: DWORD index 0 read FPattern[0] write SetPattern;
  200. property MajorColorOn: TColor32 index 0 read FColorOn[0] write SetColorOn;
  201. property MajorColorOff: TColor32 index 0 read FColorOff[0] write SetColorOff;
  202. property MinorStipple: TArrayOfColor32 index 1 read GetStipple write SetStipple;
  203. property MinorPattern: DWORD index 1 read FPattern[1] write SetPattern;
  204. property MinorColorOn: TColor32 index 1 read FColorOn[1] write SetColorOn;
  205. property MinorColorOff: TColor32 index 1 read FColorOff[1] write SetColorOff;
  206. end;
  207. //------------------------------------------------------------------------------
  208. //
  209. // TPixelGridLayer
  210. //
  211. //------------------------------------------------------------------------------
  212. constructor TPixelGridLayer.Create(ALayerCollection: TLayerCollection;
  213. AImage: TCustomImage32);
  214. begin
  215. inherited Create(ALayerCollection);
  216. FImage := AImage;
  217. FNeedStipple[0] := True;
  218. FNeedStipple[1] := True;
  219. FPattern[0] := $55555555;
  220. FColorOn[0] := $ff353535;
  221. FColorOff[0] := $ffa0a0a0;
  222. FPattern[1] := $55555555;
  223. FColorOn[1] := $ffa0a0a0;
  224. FColorOff[1] := $ffbfbfbf;
  225. end;
  226. class procedure TPixelGridLayer.CreateStipple(var Stipple: TArrayOfColor32; Pattern: DWORD; ColorOn, ColorOff: TColor32);
  227. var
  228. i: integer;
  229. Mask: DWORD;
  230. begin
  231. SetLength(Stipple, 32);
  232. Mask := $80000000;
  233. i := 0;
  234. while (Mask <> 0) do
  235. begin
  236. if (Pattern and Mask = 0) then
  237. Stipple[i] := ColorOff
  238. else
  239. Stipple[i] := ColorOn;
  240. Mask := Mask shr 1;
  241. inc(i);
  242. end;
  243. end;
  244. function TPixelGridLayer.GetStipple(const Index: Integer): TArrayOfColor32;
  245. begin
  246. if (FNeedStipple[Index]) then
  247. CreateStipple(FStipple[Index], FPattern[Index], FColorOn[Index], FColorOff[Index]);
  248. Result := FStipple[Index];
  249. end;
  250. procedure TPixelGridLayer.Paint(Buffer: TBitmap32);
  251. var
  252. i: integer;
  253. Step: integer;
  254. MinStep: integer;
  255. Lines: integer;
  256. Rect: TRect;
  257. Size: TSize;
  258. p: TPoint;
  259. begin
  260. try
  261. if (Abs(FImage.Scale) >= 4) then
  262. Step := 1
  263. else
  264. Step := 4;
  265. // Enforce minimal grid of 4 pixels
  266. MinStep := Ceil(4 / Abs(FImage.Scale));
  267. Step := Max(Step, MinStep);
  268. Rect := FImage.GetBitmapRect;
  269. Size.cx := FImage.Bitmap.Width;
  270. Size.cy := FImage.Bitmap.Height;
  271. // Minor grid
  272. Buffer.StippleStep := 1;
  273. Buffer.SetStipple(MinorStipple);
  274. i := Step;
  275. Lines := 1;
  276. while (i < Size.cx) or (i < Size.cy) do
  277. begin
  278. if (Abs(FImage.Scale) <= 4) or (Lines mod 8 <> 0) then
  279. begin
  280. p := FImage.BitmapToControl(GR32.Point(i, i));
  281. // Vertical line
  282. if (i < Size.cx) then
  283. begin
  284. Buffer.StippleCounter := 0;
  285. Buffer.VertLineTSP(p.X, Rect.Top+1, Rect.Bottom-1);
  286. end;
  287. // Horizontal line
  288. if (i < Size.cy) then
  289. begin
  290. Buffer.StippleCounter := 0;
  291. Buffer.HorzLineTSP(Rect.Left+1, p.Y, Rect.Right-1);
  292. end;
  293. end;
  294. inc(i, Step);
  295. inc(Lines);
  296. end;
  297. // Major grid
  298. Buffer.SetStipple(MajorStipple);
  299. i := Step*8;
  300. if (Abs(FImage.Scale) > 4) then
  301. while (i < Size.cx) or (i < Size.cy) do
  302. begin
  303. p := FImage.BitmapToControl(GR32.Point(i, i));
  304. // Vertical line
  305. if (i < Size.cx) then
  306. begin
  307. Buffer.StippleCounter := 0;
  308. Buffer.VertLineTSP(p.X, Rect.Top+1, Rect.Bottom-1);
  309. end;
  310. // Horizontal line
  311. if (i < Size.cy) then
  312. begin
  313. Buffer.StippleCounter := 0;
  314. Buffer.HorzLineTSP(Rect.Left+1, p.Y, Rect.Right-1);
  315. end;
  316. inc(i, Step*8);
  317. end;
  318. // Vertical border kines
  319. Buffer.StippleCounter := 1;
  320. Buffer.VertLineTSP(Rect.Left, Rect.Top, Rect.Bottom);
  321. Buffer.StippleCounter := 1;
  322. Buffer.VertLineTSP(Rect.Right, Rect.Top, Rect.Bottom);
  323. // Horizontal border lines
  324. Buffer.StippleCounter := 0;
  325. Buffer.HorzLineTSP(Rect.Left+1, Rect.Top, Rect.Right-1);
  326. Buffer.StippleCounter := 0;
  327. Buffer.HorzLineTSP(Rect.Left+1, Rect.Bottom, Rect.Right-1);
  328. except
  329. // Prevent AV flood due to repaint
  330. Visible := False;
  331. raise;
  332. end;
  333. end;
  334. procedure TPixelGridLayer.SetColorOff(const Index: Integer; const Value: TColor32);
  335. begin
  336. FColorOff[Index] := Value;
  337. FNeedStipple[Index] := True;
  338. end;
  339. procedure TPixelGridLayer.SetColorOn(const Index: Integer; const Value: TColor32);
  340. begin
  341. FColorOn[Index] := Value;
  342. FNeedStipple[Index] := True;
  343. end;
  344. procedure TPixelGridLayer.SetPattern(const Index: Integer; const Value: DWORD);
  345. begin
  346. FPattern[Index] := Value;
  347. FNeedStipple[Index] := True;
  348. end;
  349. procedure TPixelGridLayer.SetStipple(const Index: Integer;
  350. const Value: TArrayOfColor32);
  351. begin
  352. FStipple[Index] := Value;
  353. FNeedStipple[Index] := False;
  354. end;
  355. { TPictureEditorForm }
  356. function TPictureEditorForm.CurrentImage: TImage32;
  357. begin
  358. if PageControl.ActivePage = TabSheetRGB then
  359. Result := FImageRGBChannels
  360. else
  361. if PageControl.ActivePage = TabSheetAlpha then
  362. Result := FImageAlphaChannel
  363. else
  364. Result := FImageAllChannels
  365. end;
  366. procedure TPictureEditorForm.LoadFromImage(Source: TPersistent);
  367. procedure UpdateImageBackground(Image: TImage32);
  368. begin
  369. if (Image.Bitmap.Empty) then
  370. begin
  371. Image.Background.OuterBorderColor := clNone;
  372. Image.Background.InnerBorderColor := clNone;
  373. Image.Background.InnerBorderWidth := 0;
  374. Image.Background.FillStyle := bfsCheckers;
  375. end else
  376. begin
  377. Image.Background.OuterBorderColor := clGray;
  378. Image.Background.InnerBorderColor := clWhite;
  379. Image.Background.InnerBorderWidth := 8;
  380. Image.Background.FillStyle := bfsColor;
  381. end;
  382. end;
  383. begin
  384. FImageAllChannels.BeginUpdate;
  385. FImageRGBChannels.BeginUpdate;
  386. FImageAlphaChannel.BeginUpdate;
  387. try
  388. if CurrentImage = FImageAllChannels then
  389. begin
  390. // Load RGBA bitmap, separate into RGB and A
  391. // Load RGBA
  392. FImageAllChannels.Bitmap.Assign(Source);
  393. FImageAllChannels.Bitmap.DrawMode := dmBlend;
  394. // Separate RGB
  395. FImageRGBChannels.Bitmap.Assign(FImageAllChannels.Bitmap);
  396. FImageRGBChannels.Bitmap.ResetAlpha;
  397. // Separate A
  398. AlphaToGrayscale(FImageAlphaChannel.Bitmap, FImageAllChannels.Bitmap);
  399. FImageAlphaChannel.Bitmap.ResetAlpha;
  400. end else
  401. if CurrentImage = FImageRGBChannels then
  402. begin
  403. // Load RGB bitmap, keep existing A
  404. // Load RGB
  405. if (Source <> nil) then
  406. begin
  407. FImageRGBChannels.Bitmap.Assign(Source);
  408. FImageRGBChannels.Bitmap.ResetAlpha;
  409. end else
  410. FImageRGBChannels.Bitmap.Clear($FF000000);
  411. // Merge A and RGB into RGBA
  412. FImageAllChannels.Bitmap.Assign(FImageRGBChannels.Bitmap);
  413. FImageAllChannels.Bitmap.DrawMode := dmBlend;
  414. if (not FImageAlphaChannel.Bitmap.Empty) then
  415. IntensityToAlpha(FImageAllChannels.Bitmap, FImageAlphaChannel.Bitmap)
  416. else
  417. FImageAllChannels.Bitmap.ResetAlpha;
  418. end else
  419. if CurrentImage = FImageAlphaChannel then
  420. begin
  421. // Load A bitmap, keep existing RGB
  422. if (Source <> nil) then
  423. FImageAlphaChannel.Bitmap.Assign(Source)
  424. else
  425. FImageAlphaChannel.Bitmap.Clear($FFFFFFFF);
  426. ColorToGrayscale(FImageAlphaChannel.Bitmap, FImageAlphaChannel.Bitmap);
  427. // Merge A and RGB into RGBA
  428. if (not FImageRGBChannels.Bitmap.Empty) then
  429. begin
  430. FImageAllChannels.Bitmap.Assign(FImageRGBChannels.Bitmap);
  431. FImageAllChannels.Bitmap.DrawMode := dmBlend;
  432. end else
  433. begin
  434. FImageAllChannels.Bitmap.SetSizeFrom(FImageAlphaChannel.Bitmap);
  435. FImageAllChannels.Bitmap.Clear;
  436. end;
  437. IntensityToAlpha(FImageAllChannels.Bitmap, FImageAlphaChannel.Bitmap);
  438. end;
  439. ResetZoomAndCenter(FImageAllChannels);
  440. ResetZoomAndCenter(FImageRGBChannels);
  441. ResetZoomAndCenter(FImageAlphaChannel);
  442. UpdateImageBackground(FImageAllChannels);
  443. UpdateImageBackground(FImageRGBChannels);
  444. UpdateImageBackground(FImageAlphaChannel);
  445. finally
  446. FImageAllChannels.EndUpdate;
  447. FImageRGBChannels.EndUpdate;
  448. FImageAlphaChannel.EndUpdate;
  449. end;
  450. FImageAllChannels.Changed;
  451. FImageRGBChannels.Changed;
  452. FImageAlphaChannel.Changed;
  453. if (FImageAllChannels.Bitmap.Empty) then
  454. StatusBar.Panels[3].Text := sInfoEmpty
  455. else
  456. StatusBar.Panels[3].Text := Format(sInfo, [1.0*FImageAllChannels.Bitmap.Width, 1.0*FImageAllChannels.Bitmap.Height]);
  457. end;
  458. procedure TPictureEditorForm.ResetZoomAndCenter(Image: TImage32);
  459. var
  460. Size: TSize;
  461. begin
  462. Image.BeginUpdate;
  463. try
  464. // Reset Zoom...
  465. Image.Scale := 1;
  466. // ...and Center image
  467. Size := Image.GetBitmapSize;
  468. Image.OffsetHorz := (Image.Width-Size.cx) div 2;
  469. Image.OffsetVert := (Image.Height-Size.cy) div 2;
  470. finally
  471. Image.EndUpdate;
  472. end;
  473. Image.Changed;
  474. end;
  475. procedure TPictureEditorForm.SyncZoomAndPan;
  476. procedure DoSync(Image: TImage32);
  477. begin
  478. if (Image = CurrentImage) then
  479. exit;
  480. Image.BeginUpdate; // Avoid recursion
  481. try
  482. Image.Scale := CurrentImage.Scale;
  483. Image.OffsetHorz := CurrentImage.OffsetHorz;
  484. Image.OffsetVert := CurrentImage.OffsetVert;
  485. finally
  486. Image.EndUpdate;
  487. end;
  488. // Invalidate without firing OnChange
  489. Image.ForceFullInvalidate;
  490. end;
  491. begin
  492. if (CurrentImage = nil) then
  493. exit;
  494. LabelZoom.Caption := Format(sZoom, [CurrentImage.Scale * 100]);
  495. DoSync(FImageAllChannels);
  496. DoSync(FImageRGBChannels);
  497. DoSync(FImageAlphaChannel);
  498. end;
  499. constructor TPictureEditorForm.Create(AOwner: TComponent);
  500. function CreateImage32(AParent: TWinControl): TImage32;
  501. begin
  502. Result := TImage32.Create(Self);
  503. Result.Parent := AParent;
  504. Result.Align := alClient;
  505. Result.BitmapAlign := baCustom;
  506. Result.Cursor := crCross;
  507. Result.PopupMenu := PopupMenu;
  508. Result.Background.CheckersStyle := bcsMedium;
  509. Result.Background.OuterBorderColor := clGray;
  510. Result.Background.InnerBorderColor := clWhite;
  511. Result.Background.InnerBorderWidth := 8;
  512. Result.Background.FillStyle := bfsCheckers;
  513. Result.MousePan.Enabled := True;
  514. Result.MousePan.PanCursor := crSizeAll;
  515. Result.MouseZoom.Enabled := True;
  516. Result.MouseZoom.Animate := True;
  517. Result.TabStop := True; // Required for mouse wheel
  518. Result.Scale := 1;
  519. Result.ScaleMode := smScale;
  520. Result.OnMouseMove := ImageMouseMove;
  521. Result.OnMouseDown := ImageMouseDown;
  522. Result.OnChange := ImageChanged;
  523. end;
  524. procedure LoadGlyphs;
  525. var
  526. ResourceName: string;
  527. Bitmap: TBitmap;
  528. Stream: TResourceStream;
  529. const
  530. sBitmapNames: array[0..7] of string = (
  531. 'GR32_OPEN',
  532. 'GR32_SAVE',
  533. 'GR32_CLEAR',
  534. 'GR32_COPY',
  535. 'GR32_PASTE',
  536. 'GR32_INVERT',
  537. 'GR32_HELP',
  538. 'GR32_GRID'
  539. );
  540. begin
  541. // We're not storing bitmaps in the imagelist in order to support FPC.
  542. // FPC's TImageList doesn't have the ColorDepth property.
  543. ImageList.Clear;
  544. {$ifndef FPC}
  545. ImageList.ColorDepth := cd32bit;
  546. {$endif FPC}
  547. Bitmap := TBitmap.Create;
  548. try
  549. for ResourceName in sBitmapNames do
  550. begin
  551. Stream := TResourceStream.Create(hInstance, ResourceName, 'BITMAP32');
  552. try
  553. Bitmap.LoadFromStream(Stream);
  554. finally
  555. Stream.Free;
  556. end;
  557. ImageList.AddMasked(Bitmap, -1);
  558. end;
  559. finally
  560. Bitmap.Free;
  561. end;
  562. end;
  563. begin
  564. inherited;
  565. LoadGlyphs;
  566. FImageAllChannels := CreateImage32(TabSheetRGBA);
  567. FImageRGBChannels := CreateImage32(TabSheetRGB);
  568. FImageAlphaChannel := CreateImage32(TabSheetAlpha);
  569. FImageAllChannels.Bitmap.DrawMode := dmBlend;
  570. FLayerPixelGrid := TPixelGridLayer.Create(FImageAllChannels.Layers, FImageAllChannels);
  571. FLayerPixelGrid.Visible := False;
  572. {$IFDEF PLATFORM_INDEPENDENT}
  573. FOpenDialog := TOpenDialog.Create(Self);
  574. FSaveDialog := TSaveDialog.Create(Self);
  575. {$ELSE}
  576. FOpenDialog := TOpenPictureDialog.Create(Self);
  577. FSaveDialog := TSavePictureDialog.Create(Self);
  578. {$ENDIF}
  579. FOpenDialog.Filter := ImageFormatManager.BuildFileFilter(IImageFormatReader, True) +
  580. '|' + SDefaultFilter;
  581. FSaveDialog.Filter := ImageFormatManager.BuildFileFilter(IImageFormatWriter) +
  582. '|' + SDefaultFilter;
  583. end;
  584. { TBitmap32Editor }
  585. constructor TBitmap32Editor.Create(AOwner: TComponent);
  586. begin
  587. inherited;
  588. FBitmap32 := TBitmap32.Create;
  589. end;
  590. destructor TBitmap32Editor.Destroy;
  591. begin
  592. FBitmap32.Free;
  593. inherited;
  594. end;
  595. function TBitmap32Editor.Execute: Boolean;
  596. var
  597. PictureEditorForm: TPictureEditorForm;
  598. begin
  599. PictureEditorForm := TPictureEditorForm.Create(Self);
  600. try
  601. PictureEditorForm.LoadFromImage(FBitmap32);
  602. Result := (PictureEditorForm.ShowModal = mrOK);
  603. if Result then
  604. FBitmap32.Assign(PictureEditorForm.FImageAllChannels.Bitmap);
  605. finally
  606. PictureEditorForm.Free;
  607. end;
  608. end;
  609. procedure TBitmap32Editor.SetBitmap32(Value: TBitmap32);
  610. begin
  611. try
  612. FBitmap32.Assign(Value);
  613. except
  614. on E: Exception do
  615. ShowMessage(E.Message);
  616. end;
  617. end;
  618. { TBitmap32Property }
  619. procedure TBitmap32Property.Edit;
  620. var
  621. BitmapEditor: TBitmap32Editor;
  622. begin
  623. try
  624. BitmapEditor := TBitmap32Editor.Create(nil);
  625. try
  626. {$IFDEF FPC}
  627. BitmapEditor.Bitmap32 := TBitmap32(GetObjectValue);
  628. {$ELSE}
  629. BitmapEditor.Bitmap32 := TBitmap32(Pointer(GetOrdValue));
  630. {$ENDIF}
  631. if BitmapEditor.Execute then
  632. begin
  633. {$IFDEF FPC}
  634. SetPtrValue(BitmapEditor.Bitmap32);
  635. {$ELSE}
  636. SetOrdValue(Longint(BitmapEditor.Bitmap32));
  637. {$ENDIF}
  638. end;
  639. finally
  640. BitmapEditor.Free;
  641. end;
  642. except
  643. on E: Exception do
  644. ShowMessage(E.Message);
  645. end;
  646. end;
  647. function TBitmap32Property.GetAttributes: TPropertyAttributes;
  648. begin
  649. Result := [paDialog, paSubProperties];
  650. end;
  651. function TBitmap32Property.GetValue: string;
  652. var
  653. Bitmap: TBitmap32;
  654. begin
  655. try
  656. {$IFDEF FPC}
  657. Bitmap := TBitmap32(GetObjectValue);
  658. {$ELSE}
  659. Bitmap := TBitmap32(GetOrdValue);
  660. {$ENDIF}
  661. if (Bitmap = nil) or Bitmap.Empty then
  662. Result := srNone
  663. else
  664. Result := Format('%s [%d,%d]', [Bitmap.ClassName, Bitmap.Width, Bitmap.Height]);
  665. except
  666. on E: Exception do
  667. ShowMessage(E.Message);
  668. end;
  669. end;
  670. {$IFDEF EXT_PROP_EDIT}
  671. procedure TBitmap32Property.PropDrawValue(Canvas: TCanvas;
  672. const ARect: TRect; ASelected: Boolean);
  673. var
  674. Bitmap32: TBitmap32;
  675. TmpBitmap: TBitmap32;
  676. R: TRect;
  677. begin
  678. Bitmap32 := TBitmap32(GetOrdValue);
  679. if Bitmap32.Empty then
  680. DefaultPropertyDrawValue(Self, Canvas, ARect)
  681. else
  682. begin
  683. R := ARect;
  684. R.Right := R.Left + R.Bottom - R.Top;
  685. TmpBitmap := TBitmap32.Create;
  686. TmpBitmap.Width := R.Right - R.Left;
  687. TmpBitmap.Height := R.Bottom - R.Top;
  688. TDraftResampler.Create(TmpBitmap);
  689. TmpBitmap.Draw(TmpBitmap.BoundsRect, Bitmap32.BoundsRect, Bitmap32);
  690. TmpBitmap.DrawTo(Canvas.Handle, R, TmpBitmap.BoundsRect);
  691. TmpBitmap.Free;
  692. R.Left := R.Right;
  693. R.Right := ARect.Right;
  694. DefaultPropertyDrawValue(Self, Canvas, R);
  695. end;
  696. end;
  697. procedure TBitmap32Property.PropDrawName(ACanvas: TCanvas; const ARect: TRect; ASelected: Boolean);
  698. begin
  699. DefaultPropertyDrawName(Self, ACanvas, ARect);
  700. end;
  701. function TBitmap32Property.PropDrawNameRect(const ARect: TRect): TRect;
  702. begin
  703. Result := ARect;
  704. end;
  705. function TBitmap32Property.PropDrawValueRect(const ARect: TRect): TRect;
  706. begin
  707. if TBitmap32(GetOrdValue).Empty then
  708. Result := ARect
  709. else
  710. Result := Rect(ARect.Left, ARect.Top, (ARect.Bottom - ARect.Top) + ARect.Left, ARect.Bottom);
  711. end;
  712. {$ENDIF}
  713. procedure TBitmap32Property.SetValue(const Value: string);
  714. begin
  715. if Value = '' then
  716. SetOrdValue(0);
  717. end;
  718. { TImage32Editor }
  719. procedure TImage32Editor.ExecuteVerb(Index: Integer);
  720. var
  721. Img: TCustomImage32;
  722. BitmapEditor: TBitmap32Editor;
  723. begin
  724. Img := Component as TCustomImage32;
  725. if Index = 0 then
  726. begin
  727. BitmapEditor := TBitmap32Editor.Create(nil);
  728. try
  729. BitmapEditor.Bitmap32 := Img.Bitmap;
  730. if BitmapEditor.Execute then
  731. begin
  732. Img.Bitmap := BitmapEditor.Bitmap32;
  733. Designer.Modified;
  734. end;
  735. finally
  736. BitmapEditor.Free;
  737. end;
  738. end;
  739. end;
  740. function TImage32Editor.GetVerb(Index: Integer): string;
  741. begin
  742. if Index = 0 then
  743. Result := 'Bitmap32 Editor...';
  744. end;
  745. function TImage32Editor.GetVerbCount: Integer;
  746. begin
  747. Result := 1;
  748. end;
  749. procedure TPictureEditorForm.ActionClearExecute(Sender: TObject);
  750. begin
  751. LoadFromImage(nil);
  752. end;
  753. procedure TPictureEditorForm.ActionLoadExecute(Sender: TObject);
  754. var
  755. Bitmap: TBitmap32;
  756. begin
  757. if not FOpenDialog.Execute then
  758. exit;
  759. Bitmap := TBitmap32.Create(TMemoryBackend);
  760. try
  761. Bitmap.LoadFromFile(FOpenDialog.Filename);
  762. LoadFromImage(Bitmap);
  763. finally
  764. Bitmap.Free;
  765. end;
  766. end;
  767. procedure TPictureEditorForm.ActionPasteExecute(Sender: TObject);
  768. var
  769. Bitmap: TBitmap32;
  770. begin
  771. Bitmap := TBitmap32.Create;
  772. try
  773. Bitmap.Assign(Clipboard);
  774. LoadFromImage(Bitmap);
  775. finally
  776. Bitmap.Free;
  777. end;
  778. end;
  779. procedure TPictureEditorForm.ActionPasteUpdate(Sender: TObject);
  780. begin
  781. try
  782. TAction(Sender).Enabled := ImageFormatManager.ClipboardFormats.CanPasteFromClipboard;
  783. except
  784. {$IFDEF FPC}
  785. TAction(Sender).Enabled := False;
  786. {$ELSE FPC}
  787. on E: EClipboardException do
  788. TAction(Sender).Enabled := False; // Something else has the clipboard open
  789. {$ENDIF FPC}
  790. end;
  791. end;
  792. procedure TPictureEditorForm.ActionSaveExecute(Sender: TObject);
  793. var
  794. Bitmap: TBitmap;
  795. begin
  796. if (CurrentImage.Bitmap.Empty) then
  797. exit;
  798. FSaveDialog.DefaultExt := GraphicExtension(TBitmap);
  799. if not FSaveDialog.Execute then
  800. exit;
  801. if (CurrentImage = FImageAllChannels) or
  802. (not SameText(ExtractFileExt(FSaveDialog.Filename), GraphicExtension(TBitmap))) then
  803. // Save in 32-bit RGBA bitmap (or whatever format we have chosen)
  804. FImageAllChannels.Bitmap.SaveToFile(FSaveDialog.Filename)
  805. else
  806. begin
  807. // Save 24-bit RGB bitmap
  808. Bitmap := TBitmap.Create;
  809. try
  810. Bitmap.Assign(CurrentImage.Bitmap);
  811. Bitmap.PixelFormat := pf24Bit;
  812. Bitmap.SaveToFile(FSaveDialog.Filename)
  813. finally
  814. Bitmap.Free;
  815. end;
  816. end;
  817. end;
  818. procedure TPictureEditorForm.ActionCopyExecute(Sender: TObject);
  819. begin
  820. Clipboard.Assign(CurrentImage.Bitmap);
  821. end;
  822. procedure TPictureEditorForm.ActionGridExecute(Sender: TObject);
  823. begin
  824. FLayerPixelGrid.Visible := TAction(Sender).Checked;
  825. end;
  826. procedure TPictureEditorForm.ActionGridUpdate(Sender: TObject);
  827. begin
  828. TAction(Sender).Checked := FLayerPixelGrid.Visible;
  829. end;
  830. procedure TPictureEditorForm.ActionHasBitmapUpdate(Sender: TObject);
  831. begin
  832. TAction(Sender).Enabled := (CurrentImage <> nil) and (not CurrentImage.Bitmap.Empty);
  833. end;
  834. procedure TPictureEditorForm.ActionHelpExecute(Sender: TObject);
  835. begin
  836. ShowMessage(sHelp);
  837. end;
  838. procedure TPictureEditorForm.ActionInvertExecute(Sender: TObject);
  839. begin
  840. if (CurrentImage = FImageAllChannels) then
  841. begin
  842. Invert(FImageAllChannels.Bitmap, FImageAllChannels.Bitmap);
  843. InvertRGB(FImageRGBChannels.Bitmap, FImageRGBChannels.Bitmap);
  844. InvertRGB(FImageAlphaChannel.Bitmap, FImageAlphaChannel.Bitmap);
  845. end else
  846. if (CurrentImage = FImageRGBChannels) then
  847. begin
  848. InvertRGB(FImageAllChannels.Bitmap, FImageAllChannels.Bitmap);
  849. InvertRGB(FImageRGBChannels.Bitmap, FImageRGBChannels.Bitmap);
  850. end else
  851. begin
  852. Invert(FImageAllChannels.Bitmap, FImageAllChannels.Bitmap, [ccAlpha]);
  853. InvertRGB(FImageAlphaChannel.Bitmap, FImageAlphaChannel.Bitmap);
  854. end;
  855. end;
  856. procedure TPictureEditorForm.ImageChanged(Sender: TObject);
  857. begin
  858. SyncZoomAndPan;
  859. end;
  860. procedure TPictureEditorForm.ImageMouseDown(Sender: TObject;
  861. Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  862. begin
  863. if (Button = mbMiddle) then
  864. ResetZoomAndCenter(TImage32(Sender));
  865. end;
  866. procedure TPictureEditorForm.ImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  867. var
  868. Image: TImage32;
  869. P: TPoint;
  870. Color: TColor32Entry;
  871. ColorHex: string;
  872. ColorChannels: string;
  873. begin
  874. Image := TImage32(Sender);
  875. if (Image.IsMousePanning) then
  876. exit;
  877. if (Image.Bitmap = nil) or (Image.Bitmap.Empty) then
  878. begin
  879. StatusBar.Panels[0].Text := '';
  880. StatusBar.Panels[1].Text := '';
  881. StatusBar.Panels[2].Text := '';
  882. exit;
  883. end;
  884. P := Image.ControlToBitmap(GR32.Point(X, Y));
  885. if (P.X >= 0) and (P.Y >= 0) and
  886. (P.X < Image.Bitmap.Width) and (P.Y < Image.Bitmap.Height) then
  887. begin
  888. Color := TColor32Entry(Image.Bitmap[P.X, P.Y]);
  889. if (Image = FImageAllChannels) then
  890. begin
  891. ColorHex := Format('ARGB: $%.8X', [Color.ARGB]);
  892. ColorChannels := Format('A:%-3d R:%-3d G:%-3d B:%-3d', [Color.A, Color.R, Color.G, Color.B]);
  893. end else
  894. if (Image = FImageRGBChannels) then
  895. begin
  896. ColorHex := Format('RGB: $%.6X', [Color.ARGB and $00FFFFFF]);
  897. ColorChannels := Format('R:%-3d G:%-3d B:%-3d', [Color.R, Color.G, Color.B]);
  898. end else
  899. begin
  900. ColorHex := Format('Alpha: $%.2X', [Color.R]);
  901. ColorChannels := Format('A:%-3d', [Color.R]);
  902. end;
  903. StatusBar.Panels[0].Text := ColorHex;
  904. StatusBar.Panels[1].Text := ColorChannels;
  905. StatusBar.Panels[2].Text := Format('X:%-2d Y:%-2d', [P.X, P.Y])
  906. end else
  907. begin
  908. StatusBar.Panels[0].Text := '';
  909. StatusBar.Panels[1].Text := '';
  910. StatusBar.Panels[2].Text := '';
  911. end;
  912. end;
  913. end.