MainUnit.pas 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  1. unit MainUnit;
  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 Image View Layers Example
  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-2005
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. * Andre Beckedorf <[email protected]>
  32. * Christian-W. Budde <[email protected]>
  33. *
  34. * ***** END LICENSE BLOCK ***** *)
  35. interface
  36. {$include GR32.inc}
  37. uses
  38. {$IFDEF FPC}LCLIntf, LResources, LCLType, {$ELSE} Windows, Actions, {$ENDIF}
  39. SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, ExtCtrls,
  40. StdCtrls, Buttons, Types, ActnList,
  41. GR32, GR32_Image, GR32_Layers, GR32_RangeBars,
  42. GR32_Filters, GR32_Transforms, GR32_Resamplers;
  43. type
  44. { TMainForm }
  45. TMainForm = class(TForm)
  46. BtnLayerRescale: TButton;
  47. BtnLayerResetScale: TButton;
  48. CbxCropped: TCheckBox;
  49. CbxImageInterpolate: TCheckBox;
  50. CbxLayerInterpolate: TCheckBox;
  51. CbxMagnInterpolate: TCheckBox;
  52. CbxOptRedraw: TCheckBox;
  53. GbrBorderRadius: TGaugeBar;
  54. GbrBorderWidth: TGaugeBar;
  55. GbrLayerOpacity: TGaugeBar;
  56. GbrMagnMagnification: TGaugeBar;
  57. GbrMagnOpacity: TGaugeBar;
  58. GbrMagnRotation: TGaugeBar;
  59. ImgView: TImgView32;
  60. LblBorderRadius: TLabel;
  61. LblBorderWidth: TLabel;
  62. LblMagifierOpacity: TLabel;
  63. LblMagnification: TLabel;
  64. LblOpacity: TLabel;
  65. LblRotation: TLabel;
  66. MainMenu: TMainMenu;
  67. MimArrange: TMenuItem;
  68. MnuBringFront: TMenuItem;
  69. MnuButtonMockup: TMenuItem;
  70. MnuDelete: TMenuItem;
  71. MnuFile: TMenuItem;
  72. MnuFileNew: TMenuItem;
  73. MnuFileOpen: TMenuItem;
  74. MnuFlatten: TMenuItem;
  75. MnuFlipHorz: TMenuItem;
  76. MnuFlipVert: TMenuItem;
  77. MnuLayers: TMenuItem;
  78. MnuLevelDown: TMenuItem;
  79. MnuLevelUp: TMenuItem;
  80. MnuMagnifier: TMenuItem;
  81. MnuNewBitmapLayer: TMenuItem;
  82. MnuNewBitmapRGBA: TMenuItem;
  83. MnuNewCustomLayer: TMenuItem;
  84. MnuPrint: TMenuItem;
  85. MnuRotate180: TMenuItem;
  86. MnuRotate270: TMenuItem;
  87. MnuRotate90: TMenuItem;
  88. MnuScaled: TMenuItem;
  89. MnuSendBack: TMenuItem;
  90. MnuSimpleDrawing: TMenuItem;
  91. N1: TMenuItem;
  92. N2: TMenuItem;
  93. N3: TMenuItem;
  94. N4: TMenuItem;
  95. N5: TMenuItem;
  96. N6: TMenuItem;
  97. OpenDialog: TOpenDialog;
  98. PnlBitmapLayer: TPanel;
  99. PnlBitmapLayerHeader: TPanel;
  100. PnlButtonMockup: TPanel;
  101. PnlButtonMockupHeader: TPanel;
  102. PnlControl: TPanel;
  103. PnlImage: TPanel;
  104. PnlImageHeader: TPanel;
  105. PnlMagnification: TPanel;
  106. PnlMagnificationHeader: TPanel;
  107. SaveDialog: TSaveDialog;
  108. N7: TMenuItem;
  109. MenuItemEdit: TMenuItem;
  110. MenuItemCopy: TMenuItem;
  111. ActionList: TActionList;
  112. ActionCopy: TAction;
  113. ActionPasteNew: TAction;
  114. MenuItemPasteNew: TMenuItem;
  115. ActionPasteInto: TAction;
  116. MenuItemPasteInto: TMenuItem;
  117. ActionSave: TAction;
  118. Saveas1: TMenuItem;
  119. TimerMarchingAnts: TTimer;
  120. procedure FormCreate(Sender: TObject);
  121. procedure FormDestroy(Sender: TObject);
  122. procedure BtnLayerRescaleClick(Sender: TObject);
  123. procedure BtnLayerResetScaleClick(Sender: TObject);
  124. procedure CbxCroppedClick(Sender: TObject);
  125. procedure CbxImageInterpolateClick(Sender: TObject);
  126. procedure CbxLayerInterpolateClick(Sender: TObject);
  127. procedure CbxOptRedrawClick(Sender: TObject);
  128. procedure ImgViewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  129. procedure ImgViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  130. procedure LayerOpacityChanged(Sender: TObject);
  131. procedure MimArrangeClick(Sender: TObject);
  132. procedure MnuButtonMockupClick(Sender: TObject);
  133. procedure MnuDeleteClick(Sender: TObject);
  134. procedure MnuFileClick(Sender: TObject);
  135. procedure MnuFileNewClick(Sender: TObject);
  136. procedure MnuFileOpenClick(Sender: TObject);
  137. procedure MnuFlattenClick(Sender: TObject);
  138. procedure MnuFlipHorzClick(Sender: TObject);
  139. procedure MnuFlipVertClick(Sender: TObject);
  140. procedure MnuLayersClick(Sender: TObject);
  141. procedure MnuMagnifierClick(Sender: TObject);
  142. procedure MnuNewBitmapLayerClick(Sender: TObject);
  143. procedure MnuNewBitmapRGBAClick(Sender: TObject);
  144. procedure MnuPrintClick(Sender: TObject);
  145. procedure MnuReorderClick(Sender: TObject);
  146. procedure MnuRotate180Click(Sender: TObject);
  147. procedure MnuRotate270Click(Sender: TObject);
  148. procedure MnuRotate90Click(Sender: TObject);
  149. procedure MnuScaledClick(Sender: TObject);
  150. procedure MnuSimpleDrawingClick(Sender: TObject);
  151. procedure PropertyChange(Sender: TObject);
  152. procedure ActionCopyUpdate(Sender: TObject);
  153. procedure ActionPasteIntoUpdate(Sender: TObject);
  154. procedure ActionPasteNewUpdate(Sender: TObject);
  155. procedure ActionCopyExecute(Sender: TObject);
  156. procedure ActionPasteIntoExecute(Sender: TObject);
  157. procedure ActionPasteNewExecute(Sender: TObject);
  158. procedure ActionSaveExecute(Sender: TObject);
  159. procedure TimerMarchingAntsTimer(Sender: TObject);
  160. private
  161. FSelection: TPositionedLayer;
  162. procedure SetSelection(Value: TPositionedLayer);
  163. private
  164. RBLayer: TRubberbandLayer;
  165. function CreatePositionedLayer: TPositionedLayer;
  166. procedure LayerDblClick(Sender: TObject);
  167. procedure LayerMouseDown(Sender: TObject; Buttons: TMouseButton;
  168. Shift: TShiftState; X, Y: Integer);
  169. procedure RBResizing(Sender: TObject; const OldLocation: TFloatRect;
  170. var NewLocation: TFloatRect; DragState: TRBDragState; Shift: TShiftState);
  171. procedure PaintMagnifierHandler(Sender: TObject; Buffer: TBitmap32);
  172. procedure PaintSimpleDrawingHandler(Sender: TObject; Buffer: TBitmap32);
  173. procedure PaintSimpleDrawingGetUpdateRectHandler(Sender: TObject; var UpdateRect: TRect);
  174. procedure PaintButtonMockupHandler(Sender: TObject; Buffer: TBitmap32);
  175. public
  176. procedure CreateNewImage(AWidth, AHeight: Integer; FillColor: TColor32);
  177. procedure OpenImage(const FileName: string);
  178. property Selection: TPositionedLayer read FSelection write SetSelection;
  179. end;
  180. var
  181. MainForm: TMainForm;
  182. implementation
  183. {$R *.dfm}
  184. uses
  185. {$IFDEF Darwin}
  186. MacOSAll,
  187. {$ENDIF}
  188. Math, Printers, ClipBrd,
  189. GR32_LowLevel, GR32_Paths, GR32_VectorUtils, GR32_Backends,
  190. GR32_ColorGradients, GR32_Polygons, GR32_Geometry, GR32_Clipboard,
  191. GR32.ImageFormats,
  192. GR32.ImageFormats.JPG,
  193. GR32.ImageFormats.GIF,
  194. GR32.ImageFormats.PSD,
  195. NewImageUnit,
  196. RGBALoaderUnit;
  197. const
  198. RESAMPLER: array [Boolean] of TCustomResamplerClass = (TNearestResampler, TDraftResampler);
  199. { TMainForm }
  200. procedure TMainForm.FormCreate(Sender: TObject);
  201. begin
  202. ImgView.Background.CheckersStyle := bcsMedium;
  203. ImgView.Background.FillStyle := bfsCheckers;
  204. ImgView.MousePan.Enabled := True;
  205. ImgView.MousePan.PanCursor := crSizeAll;
  206. ImgView.MouseZoom.Enabled := True;
  207. ImgView.MouseZoom.Animate := True;
  208. ImgView.MouseZoom.MaintainPivot := True;
  209. ImgView.RepaintMode := rmOptimizer;;
  210. ImgView.Options := ImgView.Options + [pboWantArrowKeys];
  211. OpenDialog.Filter := ImageFormatManager.BuildFileFilter(IImageFormatReader, True);
  212. SaveDialog.Filter := ImageFormatManager.BuildFileFilter(IImageFormatWriter, True);
  213. end;
  214. procedure TMainForm.FormDestroy(Sender: TObject);
  215. begin
  216. Selection := nil;
  217. RBLayer := nil;
  218. end;
  219. procedure TMainForm.CreateNewImage(AWidth, AHeight: Integer; FillColor: TColor32);
  220. begin
  221. Selection := nil;
  222. RBLayer := nil;
  223. ImgView.Layers.Clear;
  224. ImgView.Scale := 1;
  225. ImgView.Bitmap.SetSize(AWidth, AHeight);
  226. ImgView.Bitmap.Clear(FillColor);
  227. pnlImage.Visible := not ImgView.Bitmap.Empty;
  228. end;
  229. function TMainForm.CreatePositionedLayer: TPositionedLayer;
  230. var
  231. R: TRect;
  232. P: TPoint;
  233. begin
  234. // get coordinates of the center of viewport
  235. R := ImgView.GetViewportRect;
  236. P := ImgView.ControlToBitmap(R.CenterPoint);
  237. // Here's one way to add a layer:
  238. Result := ImgView.Layers.Add<TPositionedLayer>;
  239. (* and here's another way:
  240. Result := TPositionedLayer.Create(ImgView.Layers);
  241. *)
  242. Result.Location := FloatRect(P.X - 32, P.Y - 32, P.X + 32, P.Y + 32);
  243. Result.Scaled := True;
  244. Result.MouseEvents := True;
  245. Result.OnMouseDown := LayerMouseDown;
  246. Result.OnDblClick := LayerDblClick;
  247. end;
  248. procedure TMainForm.CbxCroppedClick(Sender: TObject);
  249. begin
  250. if Selection is TBitmapLayer then
  251. TBitmapLayer(Selection).Cropped := CbxCropped.Checked;
  252. end;
  253. procedure TMainForm.CbxImageInterpolateClick(Sender: TObject);
  254. begin
  255. RESAMPLER[CbxImageInterpolate.Checked].Create(ImgView.Bitmap);
  256. end;
  257. procedure TMainForm.CbxLayerInterpolateClick(Sender: TObject);
  258. begin
  259. if Selection is TBitmapLayer then
  260. RESAMPLER[CbxLayerInterpolate.Checked].Create(TBitmapLayer(Selection).Bitmap);
  261. end;
  262. procedure TMainForm.LayerDblClick(Sender: TObject);
  263. begin
  264. if Sender is TRubberbandLayer then
  265. TRubberbandLayer(Sender).Quantize;
  266. end;
  267. procedure TMainForm.LayerMouseDown(Sender: TObject; Buttons: TMouseButton;
  268. Shift: TShiftState; X, Y: Integer);
  269. begin
  270. if Sender <> nil then
  271. Selection := TPositionedLayer(Sender);
  272. end;
  273. procedure TMainForm.LayerOpacityChanged(Sender: TObject);
  274. begin
  275. if Selection is TBitmapLayer then
  276. TBitmapLayer(Selection).Bitmap.MasterAlpha := GbrLayerOpacity.Position;
  277. end;
  278. procedure TMainForm.ActionCopyExecute(Sender: TObject);
  279. begin
  280. if (Selection is TBitmapLayer) then
  281. Clipboard.Assign(TBitmapLayer(Selection).Bitmap)
  282. else
  283. Clipboard.Assign(ImgView.Bitmap);
  284. end;
  285. procedure TMainForm.ActionCopyUpdate(Sender: TObject);
  286. begin
  287. TAction(Sender).Enabled := (Selection is TBitmapLayer) or (not ImgView.Bitmap.Empty);
  288. end;
  289. procedure TMainForm.ActionPasteIntoExecute(Sender: TObject);
  290. begin
  291. if (Selection is TBitmapLayer) then
  292. TBitmapLayer(Selection).Bitmap.Assign(Clipboard)
  293. else
  294. ImgView.Bitmap.Assign(Clipboard);
  295. end;
  296. procedure TMainForm.ActionPasteIntoUpdate(Sender: TObject);
  297. begin
  298. TAction(Sender).Enabled := CanPasteBitmap32;
  299. end;
  300. procedure TMainForm.ActionPasteNewExecute(Sender: TObject);
  301. var
  302. BitmapLayer: TBitmapLayer;
  303. R: TRect;
  304. P: TPoint;
  305. W, H: Single;
  306. begin
  307. BitmapLayer := TBitmapLayer.Create(ImgView.Layers);
  308. try
  309. BitmapLayer.Bitmap.Assign(Clipboard);
  310. BitmapLayer.Bitmap.DrawMode := dmBlend;
  311. R := ImgView.GetViewportRect;
  312. P := ImgView.ControlToBitmap(R.CenterPoint);
  313. W := BitmapLayer.Bitmap.Width * 0.5;
  314. H := BitmapLayer.Bitmap.Height * 0.5;
  315. with ImgView.Bitmap do
  316. BitmapLayer.Location := GR32.FloatRect(P.X - W, P.Y - H, P.X + W, P.Y + H);
  317. BitmapLayer.Scaled := True;
  318. BitmapLayer.OnMouseDown := LayerMouseDown;
  319. except
  320. BitmapLayer.Free;
  321. raise;
  322. end;
  323. Selection := BitmapLayer;
  324. end;
  325. procedure TMainForm.ActionPasteNewUpdate(Sender: TObject);
  326. begin
  327. TAction(Sender).Enabled := CanPasteBitmap32;
  328. end;
  329. procedure TMainForm.ActionSaveExecute(Sender: TObject);
  330. var
  331. TempBitmap: TBitmap32;
  332. SaveBitmap: TBitmap32;
  333. begin
  334. if not SaveDialog.Execute then
  335. exit;
  336. // Hide selection
  337. Selection := nil;
  338. Screen.Cursor := crHourGlass;
  339. try
  340. TempBitmap := nil;
  341. try
  342. if (ImgView.Layers.Count > 0) then
  343. begin
  344. // Save a flattened copy of the main bitmap
  345. TempBitmap := TBitmap32.Create;
  346. TempBitmap.SetSizeFrom(ImgView.Bitmap);
  347. ImgView.PaintTo(TempBitmap, ImgView.Bitmap.BoundsRect);
  348. SaveBitmap := TempBitmap;
  349. end else
  350. // Save the main bitmap directly
  351. SaveBitmap := ImgView.Bitmap;
  352. SaveBitmap.SaveToFile(SaveDialog.FileName);
  353. finally
  354. TempBitmap.Free;
  355. end;
  356. finally
  357. Screen.Cursor := crDefault;
  358. end;
  359. end;
  360. procedure TMainForm.BtnLayerRescaleClick(Sender: TObject);
  361. var
  362. T: TBitmap32;
  363. BitmapLayer: TBitmapLayer;
  364. R: TRect;
  365. begin
  366. // resize the layer's bitmap to the size of the layer
  367. if Selection is TBitmapLayer then
  368. begin
  369. BitmapLayer := TBitmapLayer(Selection);
  370. T := TBitmap32.Create;
  371. try
  372. T.Assign(BitmapLayer.Bitmap);
  373. R := MakeRect(BitmapLayer.Location);
  374. BitmapLayer.Bitmap.SetSize(R.Width, R.Height);
  375. T.Resampler := TNearestResampler.Create(T);
  376. T.DrawMode := dmOpaque;
  377. T.DrawTo(BitmapLayer.Bitmap, Classes.Rect(0, 0, BitmapLayer.Bitmap.Width, BitmapLayer.Bitmap.Height));
  378. finally
  379. T.Free;
  380. end;
  381. BtnLayerResetScaleClick(Self);
  382. end;
  383. ImgView.GetBitmapRect
  384. end;
  385. procedure TMainForm.BtnLayerResetScaleClick(Sender: TObject);
  386. var
  387. L: TFloatRect;
  388. begin
  389. // resize the layer to the size of its bitmap
  390. if Selection is TBitmapLayer then
  391. begin
  392. L := RBLayer.Location;
  393. L.Right := L.Left + TBitmapLayer(Selection).Bitmap.Width;
  394. L.Bottom := L.Top + TBitmapLayer(Selection).Bitmap.Height;
  395. RBLayer.Location := L;
  396. RBLayer.Changed;
  397. end;
  398. end;
  399. procedure TMainForm.PropertyChange(Sender: TObject);
  400. begin
  401. ImgView.Invalidate;
  402. end;
  403. procedure TMainForm.MimArrangeClick(Sender: TObject);
  404. var
  405. HasSelection: Boolean;
  406. begin
  407. HasSelection := (Selection <> nil);
  408. MnuBringFront.Enabled := HasSelection and (Selection.Index < ImgView.Layers.Count - 2);
  409. MnuSendBack.Enabled := HasSelection and (Selection.Index > 0);
  410. MnuLevelUp.Enabled := HasSelection and (Selection.Index < ImgView.Layers.Count - 2);
  411. MnuLevelDown.Enabled := HasSelection and (Selection.Index > 0);
  412. MnuScaled.Enabled := HasSelection;
  413. MnuScaled.Checked := HasSelection and Selection.Scaled;
  414. MnuDelete.Enabled := HasSelection;
  415. HasSelection := HasSelection and (Selection is TBitmapLayer);
  416. MnuFlipHorz.Enabled := HasSelection;
  417. MnuFlipVert.Enabled := HasSelection;
  418. MnuRotate90.Enabled := HasSelection;
  419. MnuRotate180.Enabled := HasSelection;
  420. MnuRotate270.Enabled := HasSelection;
  421. end;
  422. procedure TMainForm.MnuButtonMockupClick(Sender: TObject);
  423. var
  424. L: TPositionedLayer;
  425. begin
  426. L := CreatePositionedLayer;
  427. L.OnPaint := PaintButtonMockupHandler;
  428. L.Tag := 2;
  429. Selection := L;
  430. end;
  431. procedure TMainForm.MnuDeleteClick(Sender: TObject);
  432. var
  433. ALayer: TPositionedLayer;
  434. begin
  435. if Selection <> nil then
  436. begin
  437. ALayer := Selection;
  438. Selection := nil;
  439. ALayer.Free;
  440. end;
  441. end;
  442. procedure TMainForm.MnuFileNewClick(Sender: TObject);
  443. begin
  444. FrmNewImage.ShowModal;
  445. if FrmNewImage.ModalResult <> mrOK then
  446. exit;
  447. CreateNewImage(FrmNewImage.BtnUpDownWidth.Position, FrmNewImage.BtnUpDownHeight.Position,
  448. Color32(FrmNewImage.PnlColor.Color));
  449. end;
  450. procedure TMainForm.MnuFileOpenClick(Sender: TObject);
  451. begin
  452. if OpenDialog.Execute then
  453. OpenImage(OpenDialog.FileName);
  454. end;
  455. procedure TMainForm.MnuLayersClick(Sender: TObject);
  456. var
  457. HasBitmap: Boolean;
  458. begin
  459. HasBitmap := not ImgView.Bitmap.Empty;
  460. MnuNewBitmapLayer.Enabled := HasBitmap;
  461. MnuNewBitmapRGBA.Enabled := HasBitmap;
  462. MnuNewCustomLayer.Enabled := HasBitmap;
  463. MnuFlatten.Enabled := HasBitmap and (ImgView.Layers.Count > 0);
  464. end;
  465. procedure TMainForm.MnuMagnifierClick(Sender: TObject);
  466. var
  467. L: TPositionedLayer;
  468. begin
  469. L := CreatePositionedLayer;
  470. L.OnPaint := PaintMagnifierHandler;
  471. L.Tag := 3;
  472. Selection := L;
  473. end;
  474. procedure TMainForm.MnuNewBitmapLayerClick(Sender: TObject);
  475. var
  476. BitmapLayer: TBitmapLayer;
  477. R: TRect;
  478. P: TPoint;
  479. W, H: Single;
  480. begin
  481. if not OpenDialog.Execute then
  482. exit;
  483. BitmapLayer := TBitmapLayer.Create(ImgView.Layers);
  484. try
  485. BitmapLayer.Bitmap.LoadFromFile(OpenDialog.FileName);
  486. BitmapLayer.Bitmap.DrawMode := dmBlend;
  487. R := ImgView.GetViewportRect;
  488. P := ImgView.ControlToBitmap(R.CenterPoint);
  489. W := BitmapLayer.Bitmap.Width * 0.5;
  490. H := BitmapLayer.Bitmap.Height * 0.5;
  491. with ImgView.Bitmap do
  492. BitmapLayer.Location := GR32.FloatRect(P.X - W, P.Y - H, P.X + W, P.Y + H);
  493. BitmapLayer.Scaled := True;
  494. BitmapLayer.OnMouseDown := LayerMouseDown;
  495. except
  496. BitmapLayer.Free;
  497. raise;
  498. end;
  499. Selection := BitmapLayer;
  500. end;
  501. procedure TMainForm.MnuNewBitmapRGBAClick(Sender: TObject);
  502. var
  503. BitmapLayer: TBitmapLayer;
  504. R: TRect;
  505. P: TPoint;
  506. Tmp: TBitmap32;
  507. W, H: Single;
  508. begin
  509. RGBALoaderForm.ImgRGB.Bitmap.Delete;
  510. RGBALoaderForm.ImgRGB.Scale := 1;
  511. RGBALoaderForm.ImgAlpha.Bitmap.Delete;
  512. RGBALoaderForm.ImgAlpha.Scale := 1;
  513. RGBALoaderForm.ShowModal;
  514. if (RGBALoaderForm.ModalResult <> mrOK) then
  515. exit;
  516. if (RGBALoaderForm.ImgRGB.Bitmap.Empty) then
  517. exit;
  518. BitmapLayer := TBitmapLayer.Create(ImgView.Layers);
  519. BitmapLayer.Bitmap := RGBALoaderForm.ImgRGB.Bitmap;
  520. BitmapLayer.Bitmap.DrawMode := dmBlend;
  521. if not RGBALoaderForm.ImgAlpha.Bitmap.Empty then
  522. begin
  523. Tmp := TBitmap32.Create;
  524. try
  525. Tmp.SetSize(BitmapLayer.Bitmap.Width, BitmapLayer.Bitmap.Height);
  526. RGBALoaderForm.ImgAlpha.Bitmap.DrawTo(Tmp, Classes.Rect(0, 0, Tmp.Width, Tmp.Height));
  527. // combine Alpha into already loaded RGB colors
  528. IntensityToAlpha(BitmapLayer.Bitmap, Tmp);
  529. finally
  530. Tmp.Free;
  531. end;
  532. end;
  533. R := ImgView.GetViewportRect;
  534. P := ImgView.ControlToBitmap(R.CenterPoint);
  535. W := BitmapLayer.Bitmap.Width * 0.5;
  536. H := BitmapLayer.Bitmap.Height * 0.5;
  537. BitmapLayer.Location := GR32.FloatRect(P.X - W, P.Y - H, P.X + W, P.Y + H);
  538. BitmapLayer.Scaled := True;
  539. BitmapLayer.OnMouseDown := LayerMouseDown;
  540. Selection := BitmapLayer;
  541. end;
  542. procedure TMainForm.MnuReorderClick(Sender: TObject);
  543. begin
  544. // note that the top-most layer is occupied with the rubber-banding layer
  545. if Selection = nil then
  546. exit;
  547. case TMenuItem(Sender).Tag of
  548. 1: // Bring to front, do not use BringToFront here, see note above
  549. Selection.Index := ImgView.Layers.Count - 2;
  550. 2: Selection.SendToBack;
  551. 3: Selection.Index := Selection.Index + 1; // up one level
  552. 4: Selection.Index := Selection.Index - 1; // down one level
  553. end;
  554. end;
  555. procedure TMainForm.MnuSimpleDrawingClick(Sender: TObject);
  556. var
  557. L: TPositionedLayer;
  558. begin
  559. L := CreatePositionedLayer;
  560. L.OnPaint := PaintSimpleDrawingHandler;
  561. L.OnGetUpdateRect := PaintSimpleDrawingGetUpdateRectHandler;
  562. L.Tag := 1;
  563. Selection := L;
  564. end;
  565. procedure TMainForm.OpenImage(const FileName: string);
  566. begin
  567. try
  568. Selection := nil;
  569. RBLayer := nil;
  570. ImgView.Layers.Clear;
  571. ImgView.Scale := 1;
  572. ImgView.Bitmap.LoadFromFile(FileName);
  573. finally
  574. pnlImage.Visible := not ImgView.Bitmap.Empty;
  575. end;
  576. end;
  577. procedure TMainForm.PaintButtonMockupHandler(Sender: TObject;
  578. Buffer: TBitmap32);
  579. var
  580. Layer: TPositionedLayer;
  581. RoundPoly: TArrayOfFloatPoint;
  582. TextPoly: TArrayOfArrayOfFloatPoint;
  583. Bounds, Dst: TFloatRect;
  584. Path: TFlattenedPath;
  585. Intf: ITextToPathSupport;
  586. ColorGradient: TLinearGradientPolygonFiller;
  587. BorderWidth: Double;
  588. begin
  589. if not(Sender is TPositionedLayer) then
  590. exit;
  591. Layer := TPositionedLayer(Sender);
  592. Bounds := Layer.GetAdjustedLocation;
  593. BorderWidth := 0.1 * GbrBorderWidth.Position;
  594. // Make sure that we stay within bounds.
  595. // The fill is done inside the bounds rect while the border is centered on the
  596. // bounds rect. I.e. half of the border is "outside" the bounds rect.
  597. GR32.InflateRect(Bounds, -(BorderWidth / 2), -(BorderWidth / 2));
  598. RoundPoly := RoundRect(Bounds, GbrBorderRadius.Position);
  599. // Button fill
  600. ColorGradient := TLinearGradientPolygonFiller.Create;
  601. try
  602. ColorGradient.SetPoints(FloatPoint(0, Bounds.Top), FloatPoint(0, Bounds.Bottom));
  603. ColorGradient.Gradient.StartColor := $FFE2E2E2;
  604. ColorGradient.Gradient.AddColorStop(0.499, $FFD3D3D3);
  605. ColorGradient.Gradient.AddColorStop(0.501, $FFDBDBDB);
  606. ColorGradient.Gradient.EndColor := $FFFDFDFD;
  607. PolygonFS(Buffer, RoundPoly, ColorGradient, pfAlternate);
  608. finally
  609. ColorGradient.Free;
  610. end;
  611. // Button border
  612. PolyPolygonFS(Buffer,
  613. BuildPolyPolyLine(PolyPolygon(RoundPoly), True, BorderWidth),
  614. clGray32, pfAlternate);
  615. // Button text
  616. Path := TFlattenedPath.Create;
  617. try
  618. Buffer.Font.Size := 12;
  619. if Supports(Buffer.Backend, ITextToPathSupport, Intf) then
  620. begin
  621. Intf.TextToPath(Path, 0, 0, 'Button');
  622. TextPoly := Path.Path;
  623. if Length(TextPoly) > 0 then
  624. begin
  625. Dst := PolypolygonBounds(TextPoly);
  626. TextPoly := TranslatePolyPolygon(TextPoly,
  627. 0.5 * (Bounds.Left + Bounds.Right - (Dst.Right - Dst.Left)),
  628. 0.5 * (Bounds.Bottom + Bounds.Top - Dst.Bottom));
  629. PolyPolygonFS_LCD2(Buffer, TextPoly, clBlack32, pfAlternate);
  630. end;
  631. end;
  632. finally
  633. Path.Free;
  634. end;
  635. end;
  636. procedure TMainForm.PaintMagnifierHandler(Sender: TObject; Buffer: TBitmap32);
  637. var
  638. Layer: TPositionedLayer;
  639. Magnification, Rotation: Single;
  640. SrcRect, DstRect: TFloatRect;
  641. R: TRect;
  642. T: TAffineTransformation;
  643. B: TBitmap32;
  644. W2, H2: Single;
  645. I: Integer;
  646. begin
  647. if not(Sender is TPositionedLayer) then
  648. exit;
  649. Layer := TPositionedLayer(Sender);
  650. DstRect := Layer.GetAdjustedLocation;
  651. R := MakeRect(DstRect);
  652. if not Buffer.MeasuringMode then
  653. begin
  654. Magnification := Power(10, (GbrMagnMagnification.Position * 0.02));
  655. Rotation := -GbrMagnRotation.Position;
  656. B := TBitmap32.Create;
  657. try
  658. B.SetSize(R.Width, R.Height);
  659. W2 := R.Width * 0.5;
  660. H2 := R.Height * 0.5;
  661. SrcRect := DstRect;
  662. SrcRect.Left := SrcRect.Left - H2;
  663. SrcRect.Right := SrcRect.Right + H2;
  664. SrcRect.Top := SrcRect.Top - W2;
  665. SrcRect.Bottom := SrcRect.Bottom + W2;
  666. T := TAffineTransformation.Create;
  667. try
  668. T.SrcRect := SrcRect;
  669. T.Translate(-R.Left, -R.Top);
  670. T.Translate(-W2, -H2);
  671. T.Scale(Magnification, Magnification);
  672. T.Rotate(0, 0, Rotation);
  673. T.Translate(W2, H2);
  674. if CbxMagnInterpolate.Checked then
  675. begin
  676. TLinearResampler.Create(Buffer);
  677. Transform(B, Buffer, T);
  678. end
  679. else
  680. begin
  681. TNearestResampler.Create(Buffer);
  682. Transform(B, Buffer, T);
  683. end;
  684. B.ResetAlpha;
  685. B.DrawMode := dmBlend;
  686. B.MasterAlpha := GbrMagnOpacity.Position;
  687. B.DrawTo(Buffer, R);
  688. // draw frame
  689. for I := 0 to 4 do
  690. begin
  691. Buffer.RaiseRectTS(R, 35 - I * 8);
  692. GR32.InflateRect(R, -1, -1);
  693. end;
  694. finally
  695. T.Free;
  696. end;
  697. finally
  698. B.Free;
  699. end;
  700. end;
  701. Buffer.Changed;
  702. end;
  703. procedure TMainForm.PaintSimpleDrawingHandler(Sender: TObject; Buffer: TBitmap32);
  704. var
  705. Layer: TPositionedLayer;
  706. R: TFloatRect;
  707. Cx, Cy: Single;
  708. W2, H2: Single;
  709. I: Integer;
  710. const
  711. CScale = 1 / 200;
  712. begin
  713. if not(Sender is TPositionedLayer) then
  714. exit;
  715. Layer := TPositionedLayer(Sender);
  716. R := Layer.GetAdjustedLocation;
  717. W2 := (R.Right - R.Left) * 0.5;
  718. H2 := (R.Bottom - R.Top) * 0.5;
  719. Cx := R.Left + W2;
  720. Cy := R.Top + H2;
  721. W2 := W2 * CScale;
  722. H2 := H2 * CScale;
  723. Buffer.PenColor := clRed32;
  724. Buffer.MoveToF(Cx, Cy);
  725. for I := 0 to 240 do
  726. Buffer.LineToFS(
  727. Cx + W2 * I * Cos(I * 0.125),
  728. Cy + H2 * I * Sin(I * 0.125));
  729. end;
  730. procedure TMainForm.PaintSimpleDrawingGetUpdateRectHandler(Sender: TObject;
  731. var UpdateRect: TRect);
  732. begin
  733. // Since we're drawing outside the layer bounds rect, we need to expand the
  734. // update rect or else the layer repaint mechanism will not be able to update
  735. // the correct area when the layer is moved or resized.
  736. // Instead of trying to calculate the precise extent of the figure drawn, we
  737. // just increase the update rect about 15%.
  738. GR32.InflateRect(UpdateRect, UpdateRect.Width div 6, UpdateRect.Height div 6);
  739. end;
  740. procedure TMainForm.SetSelection(Value: TPositionedLayer);
  741. begin
  742. if Value <> FSelection then
  743. begin
  744. if RBLayer <> nil then
  745. begin
  746. RBLayer.ChildLayer := nil;
  747. RBLayer.LayerOptions := LOB_NO_UPDATE;
  748. pnlBitmapLayer.Visible := False;
  749. pnlButtonMockup.Visible := False;
  750. pnlMagnification.Visible := False;
  751. ImgView.Invalidate;
  752. end;
  753. FSelection := Value;
  754. if Value <> nil then
  755. begin
  756. if RBLayer = nil then
  757. begin
  758. RBLayer := TRubberBandLayer.Create(ImgView.Layers);
  759. // Set a minimum size just to test it
  760. RBLayer.MinHeight := 5;
  761. RBLayer.MinWidth := 5;
  762. // Quantization is applied unless [Alt] is pressed (and when layer is double-clicked)
  763. RBLayer.Options := RBLayer.Options + [roQuantized];
  764. RBLayer.QuantizeShiftToggle := [ssAlt];
  765. RBLayer.Quantized := 8;
  766. RBLayer.FrameStipple := [clWhite32, clWhite32, clWhite32, clWhite32, clBlack32, clBlack32, clBlack32, clBlack32];
  767. end
  768. else
  769. RBLayer.BringToFront;
  770. RBLayer.ChildLayer := Value;
  771. RBLayer.LayerOptions := LOB_VISIBLE or LOB_MOUSE_EVENTS or LOB_NO_UPDATE;
  772. RBLayer.OnResizing := RBResizing;
  773. RBLayer.OnDblClick := LayerDblClick;
  774. if Value is TBitmapLayer then
  775. begin
  776. pnlBitmapLayer.Visible := True;
  777. GbrLayerOpacity.Position := TBitmapLayer(Value).Bitmap.MasterAlpha;
  778. CbxLayerInterpolate.Checked := (TBitmapLayer(Value).Bitmap.Resampler.ClassType = TDraftResampler);
  779. end else
  780. if Value.Tag = 2 then
  781. begin
  782. // tag = 2 for button mockup
  783. pnlButtonMockup.Visible := True;
  784. end else
  785. if Value.Tag = 3 then
  786. begin
  787. // tag = 3 for magnifiers
  788. pnlMagnification.Visible := True;
  789. end;
  790. end;
  791. end;
  792. end;
  793. procedure TMainForm.TimerMarchingAntsTimer(Sender: TObject);
  794. begin
  795. if (RBLayer = nil) or (not RBLayer.Visible) then
  796. exit;
  797. // Only animate when our application is active
  798. if (not Application.Active) then
  799. exit;
  800. RBLayer.FrameStippleCounter := RBLayer.FrameStippleCounter+1.5;
  801. end;
  802. procedure TMainForm.MnuScaledClick(Sender: TObject);
  803. begin
  804. if Selection <> nil then
  805. Selection.Scaled := not Selection.Scaled;
  806. RBLayer.Scaled := Selection.Scaled;
  807. end;
  808. procedure TMainForm.ImgViewKeyDown(Sender: TObject; var Key: Word;
  809. Shift: TShiftState);
  810. var
  811. Location: TFloatRect;
  812. begin
  813. if (FSelection = nil) then
  814. exit;
  815. case Key of
  816. VK_LEFT:
  817. begin
  818. Location := OffsetRect(FSelection.Location, -1, 0);
  819. FSelection.Location := Location;
  820. RBLayer.Location := Location;
  821. end;
  822. VK_RIGHT:
  823. begin
  824. Location := OffsetRect(FSelection.Location, 1, 0);
  825. FSelection.Location := Location;
  826. RBLayer.Location := Location;
  827. end;
  828. VK_UP:
  829. begin
  830. Location := OffsetRect(FSelection.Location, 0, -1);
  831. FSelection.Location := Location;
  832. RBLayer.Location := Location;
  833. end;
  834. VK_DOWN:
  835. begin
  836. Location := OffsetRect(FSelection.Location, 0, 1);
  837. FSelection.Location := Location;
  838. RBLayer.Location := Location;
  839. end;
  840. VK_DELETE:
  841. begin
  842. FreeAndNil(FSelection);
  843. RBLayer.ChildLayer := nil;
  844. RBLayer.LayerOptions := LOB_NO_UPDATE;
  845. end;
  846. end;
  847. end;
  848. procedure TMainForm.ImgViewMouseDown(Sender: TObject; Button: TMouseButton;
  849. Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
  850. begin
  851. if Layer = nil then
  852. Selection := nil;
  853. if (Button = mbMiddle) then
  854. begin
  855. ImgView.BeginUpdate;
  856. try
  857. // Reset Zoom...
  858. ImgView.Scale := 1;
  859. // ...and Center ImgView
  860. ImgView.ScrollToCenter;
  861. finally
  862. ImgView.EndUpdate;
  863. end;
  864. end;
  865. end;
  866. procedure TMainForm.RBResizing(Sender: TObject;
  867. const OldLocation: TFloatRect; var NewLocation: TFloatRect;
  868. DragState: TRBDragState; Shift: TShiftState);
  869. var
  870. w, h, cx, cy: Single;
  871. nw, nh: Single;
  872. begin
  873. if DragState = dsMove then
  874. Exit; // we are interested only in scale operations
  875. if Shift = [] then
  876. Exit; // special processing is not required
  877. if ssCtrl in Shift then
  878. begin
  879. { make changes symmetrical }
  880. with OldLocation do
  881. begin
  882. cx := (Left + Right) / 2;
  883. cy := (Top + Bottom) / 2;
  884. w := Right - Left;
  885. h := Bottom - Top;
  886. end;
  887. with NewLocation do
  888. begin
  889. nw := w / 2;
  890. nh := h / 2;
  891. case DragState of
  892. dsSizeL: nw := cx - Left;
  893. dsSizeT: nh := cy - Top;
  894. dsSizeR: nw := Right - cx;
  895. dsSizeB: nh := Bottom - cy;
  896. dsSizeTL: begin nw := cx - Left; nh := cy - Top; end;
  897. dsSizeTR: begin nw := Right - cx; nh := cy - Top; end;
  898. dsSizeBL: begin nw := cx - Left; nh := Bottom - cy; end;
  899. dsSizeBR: begin nw := Right - cx; nh := Bottom - cy; end;
  900. end;
  901. if nw < 2 then nw := 2;
  902. if nh < 2 then nh := 2;
  903. Left := cx - nw;
  904. Right := cx + nw;
  905. Top := cy - nh;
  906. Bottom := cy + nh;
  907. end;
  908. end;
  909. end;
  910. procedure TMainForm.MnuFlattenClick(Sender: TObject);
  911. var
  912. B: TBitmap32;
  913. W, H: Integer;
  914. begin
  915. { deselect everything }
  916. Selection := nil;
  917. W := ImgView.Bitmap.Width;
  918. H := ImgView.Bitmap.Height;
  919. { Create a new bitmap to store a flattened image }
  920. B := TBitmap32.Create;
  921. try
  922. B.SetSize(W, H);
  923. ImgView.PaintTo(B, Classes.Rect(0, 0, W, H));
  924. { destroy all the layers of the original image... }
  925. ImgView.Layers.Clear;
  926. RBLayer := nil; // note that RBLayer reference is destroyed here as well.
  927. // The rubber band will be recreated during the next
  928. // SetSelection call. Alternatively, you can delete
  929. // all the layers except the rubber band.
  930. { ...and overwrite it with the flattened one }
  931. ImgView.Bitmap := B;
  932. finally
  933. B.Free;
  934. end;
  935. end;
  936. procedure TMainForm.MnuPrintClick(Sender: TObject);
  937. var
  938. B: TBitmap32;
  939. W, H: Integer;
  940. R: TRect;
  941. function GetCenteredRectToFit(const src, dst: TRect): TRect;
  942. var
  943. srcWidth, srcHeight, dstWidth, dstHeight, ScaledSide: Integer;
  944. begin
  945. with src do begin
  946. srcWidth := Right - Left;
  947. srcHeight := Bottom - Top;
  948. end;
  949. with dst do begin
  950. dstWidth := Right - Left;
  951. dstHeight := Bottom - Top;
  952. end;
  953. if (srcWidth = 0) or (srcHeight = 0) then exit;
  954. if srcWidth / srcHeight > dstWidth / dstHeight then begin
  955. ScaledSide := Round(dstWidth * srcHeight / srcWidth);
  956. with Result do begin
  957. Left := dst.Left;
  958. Top := dst.Top + (dstHeight - ScaledSide) div 2;
  959. Right := dst.Right;
  960. Bottom := Top + ScaledSide;
  961. end;
  962. end else begin
  963. ScaledSide := Round(dstHeight * srcWidth / srcHeight);
  964. with Result do begin
  965. Left := dst.Left + (dstWidth - ScaledSide) div 2;
  966. Top := dst.Top;
  967. Right := Left + ScaledSide;
  968. Bottom := dst.Bottom;
  969. end;
  970. end;
  971. end;
  972. begin
  973. { deselect everything }
  974. Selection := nil;
  975. W := ImgView.Bitmap.Width;
  976. H := ImgView.Bitmap.Height;
  977. { Create a new bitmap to store a flattened image }
  978. B := TBitmap32.Create;
  979. Screen.Cursor := crHourGlass;
  980. try
  981. B.SetSize(W, H);
  982. ImgView.PaintTo(B, Classes.Rect(0, 0, W, H));
  983. Printer.BeginDoc;
  984. Printer.Title := 'Image View Layers Example';
  985. B.Resampler := TLinearResampler.Create(B);
  986. R := GetCenteredRectToFit(Classes.Rect(0, 0, W, H), Classes.Rect(0, 0, Printer.PageWidth, Printer.PageHeight));
  987. B.TileTo(Printer.Canvas.Handle, R, Classes.Rect(0, 0, W, H));
  988. Printer.EndDoc;
  989. finally
  990. B.Free;
  991. Screen.Cursor := crDefault;
  992. end;
  993. end;
  994. procedure TMainForm.MnuFlipHorzClick(Sender: TObject);
  995. begin
  996. if Selection is TBitmapLayer then
  997. TBitmapLayer(Selection).Bitmap.FlipHorz;
  998. end;
  999. procedure TMainForm.MnuFlipVertClick(Sender: TObject);
  1000. begin
  1001. if Selection is TBitmapLayer then
  1002. TBitmapLayer(Selection).Bitmap.FlipVert;
  1003. end;
  1004. procedure TMainForm.MnuRotate90Click(Sender: TObject);
  1005. var
  1006. R: TFloatRect;
  1007. Cx, Cy, W2, H2: Single;
  1008. begin
  1009. if Selection is TBitmapLayer then
  1010. begin
  1011. R := Selection.Location;
  1012. TBitmapLayer(Selection).Bitmap.Rotate90;
  1013. Cx := (R.Left + R.Right) * 0.5;
  1014. Cy := (R.Top + R.Bottom) * 0.5;
  1015. W2 := (R.Right - R.Left) * 0.5;
  1016. H2 := (R.Bottom - R.Top) * 0.5;
  1017. RBLayer.Location := FloatRect(Cx - H2, Cy - W2, Cx + H2, Cy + W2);
  1018. end;
  1019. end;
  1020. procedure TMainForm.MnuRotate180Click(Sender: TObject);
  1021. begin
  1022. if Selection is TBitmapLayer then
  1023. TBitmapLayer(Selection).Bitmap.Rotate180;
  1024. end;
  1025. procedure TMainForm.MnuRotate270Click(Sender: TObject);
  1026. var
  1027. R: TFloatRect;
  1028. Cx, Cy, W2, H2: Single;
  1029. begin
  1030. if Selection is TBitmapLayer then
  1031. begin
  1032. R := Selection.Location;
  1033. TBitmapLayer(Selection).Bitmap.Rotate270;
  1034. Cx := (R.Left + R.Right) * 0.5;
  1035. Cy := (R.Top + R.Bottom) * 0.5;
  1036. W2 := (R.Right - R.Left) * 0.5;
  1037. H2 := (R.Bottom - R.Top) * 0.5;
  1038. RBLayer.Location := FloatRect(Cx - H2, Cy - W2, Cx + H2, Cy + W2);
  1039. end;
  1040. end;
  1041. procedure TMainForm.MnuFileClick(Sender: TObject);
  1042. begin
  1043. MnuPrint.Enabled := not ImgView.Bitmap.Empty;
  1044. end;
  1045. procedure TMainForm.CbxOptRedrawClick(Sender: TObject);
  1046. const
  1047. RepaintMode: array[Boolean] of TRepaintMode = (rmFull, rmOptimizer);
  1048. begin
  1049. ImgView.RepaintMode := RepaintMode[CbxOptRedraw.Checked];
  1050. end;
  1051. end.