uimageview.pas 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UImageView;
  3. {$mode objfpc}{$H+}
  4. {$IF defined(LINUX) and not defined(LCLqt5)}{$DEFINE IMAGEVIEW_DIRECTUPDATE}{$ENDIF}
  5. {$DEFINE DRAW_TOOL_OUTSIDE_IMAGE}
  6. {$IF not defined(DARWIN) and not defined(LCLqt5)}{$DEFINE IMAGEVIEW_QUICKUPDATE}{$ENDIF}
  7. interface
  8. uses
  9. Classes, SysUtils, USelectionHighlight, Types, BGRABitmap, BGRABitmapTypes,
  10. LazPaintType, UImage, UZoom, Graphics, Controls, LCLType, UImageObservation,
  11. laztablet, LMessages;
  12. type
  13. TPictureMouseMoveEvent = procedure(ASender: TObject; APosition: TPointF) of object;
  14. TPictureMouseBeforeEvent = procedure(ASender: TObject; AShift: TShiftState) of object;
  15. { TOpaquePaintBox }
  16. TOpaquePaintBox = class(TCustomControl)
  17. protected
  18. procedure WMEraseBkgnd(var {%H-}Message: TLMEraseBkgnd); message LM_ERASEBKGND;
  19. procedure WMPaint(var Message: TLMPaint); message LM_PAINT;
  20. public
  21. PaintRect: TRect;
  22. procedure InvalidateRect(ARect: TRect);
  23. end;
  24. { TImageView }
  25. TImageView = class
  26. private
  27. function GetMouseButtonState: TShiftState;
  28. procedure SetCanvasScale(AValue: integer);
  29. protected
  30. FVirtualScreen: TBGRABitmap;
  31. FUpdatingPopup: boolean;
  32. FPenCursorVisible: boolean;
  33. FPenCursorPos,FPenCursorPosBefore: TVSCursorPosition;
  34. FQueryPaintVirtualScreen: boolean;
  35. FSelectionHighlight: TSelectionHighlight;
  36. FShowSelection: boolean;
  37. FInstance: TLazPaintCustomInstance;
  38. FLastPictureParameters: record
  39. defined: boolean;
  40. canvasScale: integer;
  41. workArea, scaledWorkArea: TRect;
  42. zoomedArea, scaledZoomedArea: TRect;
  43. virtualScreenArea, scaledVirtualScreenArea: TRect;
  44. originInVS: TPoint;
  45. zoomFactorX, zoomFactorY,
  46. scaledZoomFactorX, scaledZoomFactorY: double;
  47. imageOffset: TPoint;
  48. imageWidth,imageHeight: integer;
  49. end;
  50. FZoom: TZoom;
  51. FPaintBox: TOpaquePaintBox;
  52. FormMouseMovePos: TPoint;
  53. InFormMouseMove: boolean;
  54. InFormPaint: boolean;
  55. FOnPaint: TNotifyEvent;
  56. FOnToolbarUpdate: TNotifyEvent;
  57. FOnMouseMove: TPictureMouseMoveEvent;
  58. FOnMouseBefore: TPictureMouseBeforeEvent;
  59. btnLeftDown, btnRightDown, btnMiddleDown: boolean;
  60. FLastPaintDate: TDateTime;
  61. FUpdateStackWhenIdle: boolean;
  62. FCatchPaintPicture, FPaintPictureCatched: boolean;
  63. InShowNoPicture: boolean;
  64. FCanCompressOrUpdateStack: boolean;
  65. FTablet: TLazTablet;
  66. FImagePos: TPointF;
  67. FCanvasScale: integer;
  68. FAltKeyUsedForPenSize: boolean;
  69. function GetImage: TLazPaintImage;
  70. function GetPictureCanvas: TCanvas;
  71. function GetWorkArea: TRect;
  72. procedure PaintBoxMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  73. procedure PaintBoxMouseEnter(Sender: TObject);
  74. procedure PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
  75. procedure PaintBoxMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  76. procedure PaintBoxMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  77. procedure PaintBoxPaint(Sender: TObject);
  78. procedure ImageChanged(AEvent: TLazPaintImageObservationEvent);
  79. function GetRenderUpdateRectVS(AIncludeCurrentToolEditor: boolean): TRect;
  80. function GetFillSelectionHighlight: boolean;
  81. function GetPenCursorPosition: TVSCursorPosition;
  82. function GetWorkspaceColor: TColor;
  83. procedure PaintPictureImplementation(AWorkArea: TRect; AInvalidatedPart: TRect);
  84. procedure PaintVirtualScreenImplementation(AWorkArea: TRect; AInvalidatedPart: TRect);
  85. procedure PaintBlueAreaImplementation(AWorkArea: TRect);
  86. procedure PaintBlueAreaOnly(AWorkArea: TRect);
  87. procedure ComputePictureParams(AWorkArea: TRect);
  88. procedure SetFillSelectionHighlight(AValue: boolean);
  89. procedure SetShowSelection(AValue: boolean);
  90. procedure PictureSelectionChanged({%H-}sender: TLazPaintImage; const ARect: TRect);
  91. procedure ToolManagerRenderChanged(Sender: TObject);
  92. procedure PaintVirtualScreenCursor({%H-}AWorkArea: TRect);
  93. function GetRectToInvalidate(AInvalidateAll: boolean; AWorkArea: TRect): TRect;
  94. function GetPictureCoordsDefined: boolean;
  95. procedure DoInvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect);
  96. procedure DoPaint(AWorkArea: TRect; AShowNoPicture: boolean);
  97. procedure DoUpdatePicture(AWorkArea: TRect);
  98. procedure ReleaseMouseButtons(Shift: TShiftState);
  99. function GetCurrentPressure: single;
  100. public
  101. constructor Create(AInstance: TLazPaintCustomInstance; AZoom: TZoom; APaintBox: TOpaquePaintBox);
  102. destructor Destroy; override;
  103. function CatchToolKeyDown(var AKey: Word): boolean;
  104. function CatchToolKeyPress(var AKey: TUTF8Char): boolean;
  105. function CatchToolKeyUp(var AKey: Word): boolean;
  106. procedure SetBounds(ALeft, ATop, AWidth, AHeight: integer);
  107. procedure UpdatePicture;
  108. procedure ShowNoPicture;
  109. procedure InvalidatePicture(AInvalidateAll: boolean);
  110. procedure OnZoomChanged({%H-}sender: TZoom; {%H-}ANewZoom: single);
  111. procedure UpdateCursor(X,Y: integer);
  112. function BitmapToForm(pt: TPointF): TPointF;
  113. function BitmapToForm(X, Y: Single): TPointF;
  114. function BitmapToVirtualScreen(ptF: TPointF): TPointF;
  115. function BitmapToVirtualScreen(X, Y: Single): TPointF;
  116. function FormToBitmap(pt: TPoint): TPointF;
  117. function FormToBitmap(X, Y: Integer): TPointF;
  118. property Image: TLazPaintImage read GetImage;
  119. property Zoom: TZoom read FZoom;
  120. property CanvasScale: integer read FCanvasScale write SetCanvasScale;
  121. property LazPaintInstance: TLazPaintCustomInstance read FInstance;
  122. property PictureCanvas: TCanvas read GetPictureCanvas;
  123. property FillSelectionHighlight: boolean read GetFillSelectionHighlight write SetFillSelectionHighlight;
  124. property ShowSelection: boolean read FShowSelection write SetShowSelection;
  125. property WorkspaceColor: TColor read GetWorkspaceColor;
  126. property WorkArea: TRect read GetWorkArea;
  127. property PictureCoordsDefined: boolean read GetPictureCoordsDefined;
  128. property UpdatingPopup: boolean read FUpdatingPopup write FUpdatingPopup;
  129. property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
  130. property OnMouseMove: TPictureMouseMoveEvent read FOnMouseMove write FOnMouseMove;
  131. property OnMouseBefore: TPictureMouseBeforeEvent read FOnMouseBefore write FOnMouseBefore;
  132. property OnToolbarUpdate: TNotifyEvent read FOnToolbarUpdate write FOnToolbarUpdate;
  133. property LastPaintDate: TDateTime read FLastPaintDate;
  134. property MouseButtonState: TShiftState read GetMouseButtonState;
  135. property CanCompressOrUpdateStack: boolean read FCanCompressOrUpdateStack;
  136. end;
  137. implementation
  138. uses BGRATransform, LCLIntf, ugraph, math, UTool, BGRAThumbnail, LCScaleDPI, Forms,
  139. UToolVectorial, ExtCtrls;
  140. procedure InvalidateControlRect(AControl: TControl; AArea: TRect);
  141. var
  142. h: HWND;
  143. begin
  144. if AControl is TWinControl then
  145. h := TWinControl(AControl).Handle
  146. else
  147. begin
  148. AArea.Offset(AControl.Left, AControl.Top);
  149. h := AControl.Parent.Handle;
  150. end;
  151. InvalidateRect(h, @AArea, False);
  152. end;
  153. { TOpaquePaintBox }
  154. procedure TOpaquePaintBox.WMEraseBkgnd(var Message: TLMEraseBkgnd);
  155. begin
  156. //nothing
  157. end;
  158. procedure TOpaquePaintBox.WMPaint(var Message: TLMPaint);
  159. begin
  160. if Assigned(Message.PaintStruct) then
  161. PaintRect := Message.PaintStruct^.rcPaint
  162. else PaintRect := rect(0,0,ClientWidth,ClientHeight);
  163. inherited WMPaint(Message);
  164. end;
  165. procedure TOpaquePaintBox.InvalidateRect(ARect: TRect);
  166. begin
  167. InvalidateControlRect(self, ARect);
  168. end;
  169. function TImageView.GetFillSelectionHighlight: boolean;
  170. begin
  171. result := FSelectionHighlight.FillSelection;
  172. end;
  173. procedure TImageView.SetFillSelectionHighlight(AValue: boolean);
  174. begin
  175. if AValue = FSelectionHighlight.FillSelection then exit;
  176. FSelectionHighlight.FillSelection := AValue;
  177. Image.ImageMayChangeCompletely;
  178. end;
  179. function TImageView.GetWorkspaceColor: TColor;
  180. begin
  181. result := LazPaintInstance.Config.GetWorkspaceColor;
  182. end;
  183. procedure TImageView.SetShowSelection(AValue: boolean);
  184. begin
  185. if FShowSelection=AValue then Exit;
  186. FShowSelection:=AValue;
  187. Image.RenderMayChange(rect(0,0,Image.Width,Image.Height), true);
  188. end;
  189. function TImageView.GetPictureCoordsDefined: boolean;
  190. begin
  191. result := FLastPictureParameters.defined;
  192. end;
  193. procedure TImageView.ToolManagerRenderChanged(Sender: TObject);
  194. begin
  195. if Assigned(FVirtualScreen) then
  196. Image.RenderMayChange(LazPaintInstance.ToolManager.GetRenderBounds(
  197. FVirtualScreen.Width, FVirtualScreen.Height));
  198. end;
  199. procedure TImageView.PaintBoxMouseEnter(Sender: TObject);
  200. begin
  201. Image.PrepareForRendering;
  202. end;
  203. procedure TImageView.PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
  204. Y: Integer);
  205. var
  206. updateForVSCursor: boolean;
  207. begin
  208. if Assigned(FOnMouseBefore) then
  209. FOnMouseBefore(self, Shift);
  210. ReleaseMouseButtons(Shift);
  211. Image.CurrentState.LayeredBitmap.EditorFocused := true;
  212. FormMouseMovePos := Point(X,Y);
  213. if InFormMouseMove then exit;
  214. InFormMouseMove := True;
  215. if not PictureCoordsDefined then
  216. Application.ProcessMessages; //empty message stack
  217. if not PictureCoordsDefined then
  218. begin
  219. InFormMouseMove:= false;
  220. exit;
  221. end;
  222. FImagePos := FormToBitmap(FormMouseMovePos);
  223. if Assigned(FOnMouseMove) then
  224. FOnMouseMove(self, FImagePos);
  225. updateForVSCursor:= false;
  226. if LazPaintInstance.ToolManager.ToolMove(FImagePos, GetCurrentPressure) then
  227. UpdatePicture
  228. else
  229. updateForVSCursor := true;
  230. if Assigned(FOnToolbarUpdate) then
  231. FOnToolbarUpdate(self);
  232. if updateForVSCursor then
  233. UpdateCursor(X,Y);
  234. InFormMouseMove := False;
  235. end;
  236. procedure TImageView.PaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
  237. Shift: TShiftState; X, Y: Integer);
  238. var redraw: boolean;
  239. begin
  240. redraw := false;
  241. if (btnLeftDown and (Button = mbLeft)) or (btnRightDown and (Button=mbRight))
  242. or (btnMiddleDown and (Button = mbMiddle)) then
  243. begin
  244. if PictureCoordsDefined then
  245. redraw := LazPaintInstance.ToolManager.ToolMove(FormToBitmap(X,Y), GetCurrentPressure)
  246. else redraw := false;
  247. if LazPaintInstance.ToolManager.ToolUp then redraw := true;
  248. btnLeftDown := false;
  249. btnRightDown := false;
  250. btnMiddleDown:= false;
  251. end;
  252. if redraw then UpdatePicture;
  253. if FUpdateStackWhenIdle then
  254. begin
  255. LazPaintInstance.UpdateLayerStackOnTimer;
  256. FUpdateStackWhenIdle:= false;
  257. end;
  258. if Assigned(FOnToolbarUpdate) then
  259. FOnToolbarUpdate(self);
  260. ReleaseMouseButtons(Shift);
  261. end;
  262. procedure TImageView.PaintBoxMouseWheel(Sender: TObject; Shift: TShiftState;
  263. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  264. begin
  265. if not PictureCoordsDefined then exit;
  266. if LazPaintInstance.ToolManager.PenWidthVisible and (ssAlt in Shift) then
  267. begin
  268. if WheelDelta > 0 then LazPaintInstance.ToolManager.StepPenSize(false)
  269. else if WheelDelta < 0 then LazPaintInstance.ToolManager.StepPenSize(true);
  270. FAltKeyUsedForPenSize := true;
  271. end else
  272. begin
  273. Zoom.SetPosition(FormToBitmap(MousePos.X,MousePos.Y), MousePos);
  274. if WheelDelta > 0 then Zoom.ZoomIn(ssSnap in Shift) else
  275. if WheelDelta < 0 then Zoom.ZoomOut(ssSnap in Shift);
  276. Zoom.ClearPosition;
  277. end;
  278. Handled := True;
  279. end;
  280. procedure TImageView.PaintBoxMouseDown(Sender: TObject; Button: TMouseButton;
  281. Shift: TShiftState; X, Y: Integer);
  282. begin
  283. ReleaseMouseButtons(Shift);
  284. if not (Button in[mbLeft,mbRight,mbMiddle]) or not PictureCoordsDefined then exit;
  285. FCanCompressOrUpdateStack := false;
  286. if Assigned(LazPaintInstance) then LazPaintInstance.ExitColorEditor;
  287. Image.OnImageChanged.DelayedStackUpdate := True;
  288. if btnLeftDown or btnRightDown or btnMiddleDown then exit;
  289. if Button = mbMiddle then
  290. begin
  291. btnMiddleDown:= true;
  292. if not LazPaintInstance.ToolManager.ToolSleeping and not (ssAlt in Shift) then LazPaintInstance.ToolManager.ToolSleep;
  293. end;
  294. if PictureCoordsDefined then
  295. begin
  296. if Button = mbLeft then
  297. btnLeftDown := true else
  298. if Button = mbRight then
  299. btnRightDown := true;
  300. with LazPaintInstance.ToolManager do
  301. begin
  302. if (
  303. (GetCurrentToolType = ptHand) or
  304. ((GetCurrentToolType = ptEditShape) and
  305. Assigned(CurrentTool) and
  306. (CurrentTool as TEditShapeTool).NothingSelected)
  307. ) and
  308. (ssShift in Shift) then
  309. Image.SelectLayerContainingPixelAt(FormToBitmap(X,Y).Round);
  310. if ToolDown(FormToBitmap(X,Y),
  311. btnRightDown{$IFDEF DARWIN} or (ssCtrl in Shift){$ENDIF},
  312. GetCurrentPressure) then
  313. UpdatePicture;
  314. end;
  315. if Assigned(FOnToolbarUpdate) then
  316. FOnToolbarUpdate(self);
  317. end;
  318. end;
  319. function TImageView.GetPictureCanvas: TCanvas;
  320. begin
  321. result := FPaintBox.Canvas;
  322. end;
  323. procedure TImageView.PaintBoxPaint(Sender: TObject);
  324. begin
  325. if InFormPaint then exit;
  326. InFormPaint := true;
  327. DoPaint(WorkArea, InShowNoPicture);
  328. LazPaintInstance.NotifyImagePaint;
  329. InFormPaint := false;
  330. FLastPaintDate := Now;
  331. end;
  332. function TImageView.GetWorkArea: TRect;
  333. begin
  334. result := rect(0, 0, FPaintBox.Width, FPaintBox.Height);
  335. end;
  336. procedure TImageView.ImageChanged(AEvent: TLazPaintImageObservationEvent);
  337. begin
  338. if AEvent.DelayedStackUpdate then FUpdateStackWhenIdle := true;
  339. if FCatchPaintPicture then
  340. FPaintPictureCatched := true
  341. else InvalidatePicture(false);
  342. end;
  343. function TImageView.GetMouseButtonState: TShiftState;
  344. begin
  345. result := [];
  346. if btnLeftDown then include(result, ssLeft);
  347. if btnMiddleDown then include(result, ssMiddle);
  348. if btnRightDown then include(result, ssRight);
  349. end;
  350. procedure TImageView.SetCanvasScale(AValue: integer);
  351. begin
  352. if FCanvasScale=AValue then Exit;
  353. FCanvasScale:=AValue;
  354. ugraph.CanvasScale:= AValue;
  355. LazPaintInstance.ToolManager.CanvasScale := AValue;
  356. end;
  357. function TImageView.GetImage: TLazPaintImage;
  358. begin
  359. result := FInstance.Image;
  360. end;
  361. procedure TImageView.PaintPictureImplementation(AWorkArea: TRect; AInvalidatedPart: TRect);
  362. var
  363. renderRect: TRect;
  364. picParamWereDefined: boolean;
  365. procedure DrawSelectionHighlight(ARenderRect: TRect);
  366. var renderVisibleBounds: TRect;
  367. transform, invTransform: TAffineMatrix;
  368. renderWidth, renderHeight: integer;
  369. begin
  370. if Assigned(FSelectionHighlight) and ShowSelection then
  371. begin
  372. renderVisibleBounds := rect(0,0,FVirtualScreen.Width,FVirtualScreen.Height);
  373. renderWidth := ARenderRect.Right-ARenderRect.Left;
  374. renderHeight := ARenderRect.Bottom-ARenderRect.Top;
  375. transform := AffineMatrixTranslation(ARenderRect.Left,ARenderRect.Top) *
  376. AffineMatrixScale(renderWidth/Image.Width, renderHeight/Image.Height) *
  377. AffineMatrixTranslation(0.5,0.5) * Image.SelectionTransform * AffineMatrixTranslation(-0.5,-0.5) *
  378. AffineMatrixScale(Image.Width/renderWidth, Image.Height/renderHeight);
  379. try
  380. invTransform := AffineMatrixInverse(transform);
  381. renderVisibleBounds := FVirtualScreen.GetImageAffineBounds(invTransform, renderVisibleBounds,False);
  382. FSelectionHighlight.Update(ARenderRect.Right-ARenderRect.Left,ARenderRect.Bottom-ARenderRect.Top, renderVisibleBounds);
  383. except
  384. end;
  385. FSelectionHighlight.DrawAffine(FVirtualScreen, transform, rfBox, dmLinearBlend);
  386. end;
  387. end;
  388. begin
  389. picParamWereDefined := FLastPictureParameters.defined;
  390. ComputePictureParams(AWorkArea);
  391. if not FLastPictureParameters.defined then
  392. begin
  393. FreeAndNil(FVirtualScreen);
  394. exit;
  395. end;
  396. if not picParamWereDefined then
  397. FPenCursorPos := GetPenCursorPosition;
  398. if Assigned(FVirtualScreen) and ((FVirtualScreen.Width <> FLastPictureParameters.scaledVirtualScreenArea.Width) or
  399. (FVirtualScreen.Height <> FLastPictureParameters.scaledVirtualScreenArea.Height)) then
  400. FreeAndNil(FVirtualScreen);
  401. if not Assigned(FVirtualScreen) then
  402. begin
  403. FVirtualScreen := TBGRABitmap.Create(FLastPictureParameters.scaledVirtualScreenArea.Width,
  404. FLastPictureParameters.scaledVirtualScreenArea.Height, WorkspaceColor);
  405. Image.ResetRenderUpdateRect;
  406. Image.RenderMayChange(rect(0,0,FVirtualScreen.Width,FVirtualScreen.Height), false, false);
  407. end;
  408. if not FUpdatingPopup then
  409. begin
  410. if picParamWereDefined then FVirtualScreen.ClipRect := GetRenderUpdateRectVS(False);
  411. Image.ResetRenderUpdateRect;
  412. if not FVirtualScreen.ClipRect.IsEmpty then
  413. begin
  414. renderRect := FLastPictureParameters.scaledZoomedArea;
  415. Types.OffsetRect(renderRect, -FLastPictureParameters.scaledVirtualScreenArea.Left,
  416. -FLastPictureParameters.scaledVirtualScreenArea.Top);
  417. DrawThumbnailCheckers(FVirtualScreen, renderRect, Image.IsIconCursor, DoScaleX(60*CanvasScale, OriginalDPI)/60);
  418. //draw image (with merged selection)
  419. FVirtualScreen.StretchPutImage(renderRect,Image.RenderedImage,dmDrawWithTransparency);
  420. if (Zoom.Factor > DoScaleX(MinZoomForGrid, OriginalDPI)) and LazPaintInstance.GridVisible then
  421. DrawGrid(FVirtualScreen, FLastPictureParameters.scaledZoomFactorX,
  422. FLastPictureParameters.scaledZoomFactorY, FLastPictureParameters.originInVS.X,
  423. FLastPictureParameters.originInVS.Y);
  424. DrawSelectionHighlight(renderRect);
  425. {$IFDEF DRAW_TOOL_OUTSIDE_IMAGE}
  426. //paint blue area in virtual screen
  427. if FLastPictureParameters.scaledZoomedArea.Top > 0 then
  428. FVirtualScreen.FillRect(0, 0,
  429. FVirtualScreen.Width, FLastPictureParameters.scaledZoomedArea.Top, WorkspaceColor, dmSet);
  430. if FLastPictureParameters.scaledZoomedArea.Left > 0 then
  431. FVirtualScreen.FillRect(0, FLastPictureParameters.scaledZoomedArea.Top,
  432. FLastPictureParameters.scaledZoomedArea.Left, FLastPictureParameters.scaledZoomedArea.Bottom,
  433. WorkspaceColor, dmSet);
  434. if FLastPictureParameters.scaledZoomedArea.Right < FVirtualScreen.Width then
  435. FVirtualScreen.FillRect(FLastPictureParameters.scaledZoomedArea.Right, FLastPictureParameters.scaledZoomedArea.Top,
  436. FVirtualScreen.Width, FLastPictureParameters.scaledZoomedArea.Bottom,
  437. WorkspaceColor, dmSet);
  438. if FLastPictureParameters.scaledZoomedArea.Bottom < FVirtualScreen.Height then
  439. FVirtualScreen.FillRect(0, FLastPictureParameters.scaledZoomedArea.Bottom,
  440. FVirtualScreen.Width, FVirtualScreen.Height, WorkspaceColor, dmSet);
  441. {$ENDIF}
  442. end;
  443. FVirtualScreen.NoClip;
  444. //show tools info
  445. Image.RenderMayChange(LazPaintInstance.ToolManager.RenderTool(FVirtualScreen), false, false);
  446. end;
  447. PaintVirtualScreenImplementation(AWorkArea, AInvalidatedPart);
  448. Image.VisibleArea := TRectF.Intersect(rectF(FormToBitmap(AWorkArea.Left, AWorkArea.Top),
  449. FormToBitmap(AWorkArea.Right, AWorkArea.Bottom)),
  450. rectF(-0.5,-0.5,Image.Width-0.5,Image.Height-0.5));
  451. end;
  452. procedure TImageView.PaintVirtualScreenImplementation(AWorkArea: TRect; AInvalidatedPart: TRect);
  453. var cursorBack: TBGRABitmap;
  454. DestCanvas: TCanvas;
  455. cursorContourF: array of TPointF;
  456. rectBack, oldClip: TRect;
  457. cursorPos: TVSCursorPosition;
  458. procedure DrawPart;
  459. var
  460. vsPart: TRect;
  461. begin
  462. with AInvalidatedPart do
  463. vsPart := rect(Left*CanvasScale, Top*CanvasScale, Right*CanvasScale, Bottom*CanvasScale);
  464. with FLastPictureParameters.scaledVirtualScreenArea do
  465. vsPart.Offset(-Left, -Top);
  466. FVirtualScreen.DrawPart(vsPart, DestCanvas, AInvalidatedPart, True);
  467. end;
  468. begin
  469. if (FVirtualScreen = nil) or not FLastPictureParameters.defined then exit;
  470. AInvalidatedPart.Intersect(FLastPictureParameters.virtualScreenArea);
  471. if AInvalidatedPart.IsEmpty then exit;
  472. DestCanvas := PictureCanvas;
  473. cursorPos := FPenCursorPos;
  474. if FPenCursorVisible and not IsRectEmpty(cursorPos.bounds) then
  475. begin
  476. rectBack := cursorPos.bounds;
  477. IntersectRect(rectBack,rectBack,rect(0,0,FVirtualScreen.Width,FVirtualScreen.Height));
  478. if not IsRectEmpty(rectBack) then
  479. begin
  480. cursorBack := FVirtualScreen.GetPart(rectBack) as TBGRABitmap;
  481. cursorContourF := FVirtualScreen.ComputeEllipseContour(cursorPos.c.x,cursorPos.c.y,cursorPos.rx,cursorPos.ry);
  482. oldClip := FVirtualScreen.ClipRect;
  483. FVirtualScreen.ClipRect := rectBack;
  484. FVirtualScreen.PenStyle := psSolid;
  485. FVirtualScreen.DrawPolygonAntialias(cursorcontourF,BGRA(0,0,0,192),3*cursorPos.penWidth);
  486. FVirtualScreen.DrawPolygonAntialias(cursorcontourF,BGRA(255,255,255,255),cursorPos.penWidth);
  487. FVirtualScreen.ClipRect := oldClip;
  488. DrawPart;
  489. FVirtualScreen.PutImage(rectBack.left,rectBack.Top,cursorBack,dmSet);
  490. cursorBack.Free;
  491. end else
  492. DrawPart;
  493. end else
  494. DrawPart;
  495. if (image.Width = 0) or (image.Height = 0) then
  496. Zoom.MinFactor := 1
  497. else
  498. Zoom.MinFactor := max(8/image.Width, 8/image.Height);
  499. with AWorkArea do
  500. Zoom.MaxFactor := min(32,max(1,min((right-left)/8,(bottom-top)/8)));
  501. end;
  502. procedure TImageView.PaintBlueAreaImplementation(AWorkArea: TRect);
  503. var
  504. lastWorkArea, zoomedArea: TRect;
  505. begin
  506. if FLastPictureParameters.defined then
  507. begin
  508. lastWorkArea := FLastPictureParameters.WorkArea;
  509. if (lastWorkArea.Right <= lastWorkArea.Left) or (lastWorkArea.Bottom <= lastWorkArea.Top) then exit;
  510. zoomedArea := FLastPictureParameters.virtualScreenArea;
  511. IntersectRect(zoomedArea, zoomedArea,lastWorkArea);
  512. with PictureCanvas do
  513. begin
  514. Brush.Color := WorkspaceColor;
  515. if zoomedArea.Left > lastWorkArea.Left then
  516. FillRect(lastWorkArea.Left, zoomedArea.Top, zoomedArea.Left, zoomedArea.Bottom);
  517. if zoomedArea.Top > lastWorkArea.Top then
  518. FillRect(lastWorkArea.Left, lastWorkArea.Top, lastWorkArea.Right, zoomedArea.Top);
  519. if zoomedArea.Right < lastWorkArea.Right then
  520. FillRect(zoomedArea.Right, zoomedArea.Top, lastWorkArea.Right, zoomedArea.Bottom);
  521. if zoomedArea.Bottom < lastWorkArea.Bottom then
  522. FillRect(lastWorkArea.Left, zoomedArea.Bottom, lastWorkArea.Right, lastWorkArea.Bottom);
  523. end;
  524. end else
  525. PaintBlueAreaOnly(AWorkArea);
  526. end;
  527. procedure TImageView.PaintBlueAreaOnly(AWorkArea: TRect);
  528. begin
  529. if (AWorkArea.Right <= AWorkArea.Left) or (AWorkArea.Bottom <= AWorkArea.Top) then exit;
  530. with PictureCanvas do
  531. begin
  532. Brush.Color := WorkspaceColor;
  533. FillRect(AWorkArea);
  534. end;
  535. FLastPictureParameters.defined := false;
  536. end;
  537. constructor TImageView.Create(AInstance: TLazPaintCustomInstance; AZoom: TZoom;
  538. APaintBox: TOpaquePaintBox);
  539. begin
  540. FInstance := AInstance;
  541. FZoom := AZoom;
  542. FCanvasScale:= round(APaintBox.GetCanvasScaleFactor);
  543. FAltKeyUsedForPenSize:= false;
  544. AInstance.ToolManager.CanvasScale := FCanvasScale;
  545. ugraph.CanvasScale:= FCanvasScale;
  546. FPaintBox := APaintBox;
  547. FPaintBox.OnMouseEnter:=@PaintBoxMouseEnter;
  548. FPaintBox.OnMouseDown:= @PaintBoxMouseDown;
  549. FPaintBox.OnMouseMove:= @PaintBoxMouseMove;
  550. FPaintBox.OnMouseUp:= @PaintBoxMouseUp;
  551. FPaintBox.OnMouseWheel:=@PaintBoxMouseWheel;
  552. FPaintBox.OnPaint:=@PaintBoxPaint;
  553. //recursive calls
  554. InFormMouseMove:= false;
  555. InFormPaint := false;
  556. FLastPictureParameters.defined:= false;
  557. FSelectionHighlight := TSelectionHighlight.Create(Image);
  558. FShowSelection:= true;
  559. Image.OnSelectionChanged := @PictureSelectionChanged;
  560. Image.OnImageChanged.AddObserver(@ImageChanged);
  561. if Assigned(LazPaintInstance.ToolManager) then
  562. begin
  563. LazPaintInstance.ToolManager.OnToolRenderChanged:=@ToolManagerRenderChanged;
  564. LazPaintInstance.ToolManager.BitmapToVirtualScreen := @BitmapToVirtualScreen;
  565. end;
  566. //mouse status
  567. btnLeftDown := false;
  568. btnRightDown := false;
  569. btnMiddleDown:= false;
  570. FImagePos := EmptyPointF;
  571. try
  572. FTablet := TLazTablet.Create(nil);
  573. except
  574. on ex: exception do
  575. FTablet := nil;
  576. end;
  577. end;
  578. destructor TImageView.Destroy;
  579. begin
  580. FreeAndNil(FTablet);
  581. if Assigned(LazPaintInstance.ToolManager) then
  582. begin
  583. LazPaintInstance.ToolManager.OnToolRenderChanged := nil;
  584. LazPaintInstance.ToolManager.BitmapToVirtualScreen := nil;
  585. end;
  586. Image.OnImageChanged.RemoveObserver(@ImageChanged);
  587. Image.OnSelectionChanged := nil;
  588. FreeAndNil(FSelectionHighlight);
  589. FreeAndNil(FVirtualScreen);
  590. inherited Destroy;
  591. end;
  592. procedure TImageView.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
  593. var picBoundsChanged: boolean;
  594. begin
  595. picBoundsChanged := (FPaintBox.Left <> ALeft) or (FPaintBox.Top <> ATop) or
  596. (FPaintBox.Width <> AWidth) or (FPaintBox.Height <> AHeight);
  597. FPaintBox.SetBounds(ALeft, ATop, AWidth, AHeight);
  598. CanvasScale:= round(FPaintBox.GetCanvasScaleFactor);
  599. if picBoundsChanged then
  600. InvalidatePicture(True);
  601. end;
  602. function TImageView.CatchToolKeyDown(var AKey: Word): boolean;
  603. begin
  604. FCatchPaintPicture:= true;
  605. FPaintPictureCatched := false;
  606. if AKey = VK_MENU then FAltKeyUsedForPenSize := false;
  607. try
  608. result := LazPaintInstance.ToolManager.ToolKeyDown(AKey) or FPaintPictureCatched;
  609. finally
  610. FCatchPaintPicture:= false;
  611. end;
  612. end;
  613. function TImageView.CatchToolKeyUp(var AKey: Word): boolean;
  614. begin
  615. FCatchPaintPicture:= true;
  616. FPaintPictureCatched := false;
  617. try
  618. result := LazPaintInstance.ToolManager.ToolKeyUp(AKey) or FPaintPictureCatched;
  619. if (AKey = VK_MENU) and FAltKeyUsedForPenSize then AKey := 0;
  620. finally
  621. FCatchPaintPicture:= false;
  622. end;
  623. end;
  624. function TImageView.CatchToolKeyPress(var AKey: TUTF8Char): boolean;
  625. begin
  626. FCatchPaintPicture:= true;
  627. FPaintPictureCatched := false;
  628. try
  629. result := LazPaintInstance.ToolManager.ToolKeyPress(AKey) or FPaintPictureCatched;
  630. finally
  631. FCatchPaintPicture:= false;
  632. end;
  633. end;
  634. procedure TImageView.UpdatePicture;
  635. begin
  636. DoUpdatePicture(WorkArea);
  637. if not Image.OnImageChanged.DelayedStackUpdate then LazPaintInstance.InvalidateLayerStack;
  638. end;
  639. procedure TImageView.ShowNoPicture;
  640. begin
  641. InShowNoPicture := true;
  642. try
  643. DoUpdatePicture(WorkArea);
  644. finally
  645. InShowNoPicture := false;
  646. end;
  647. end;
  648. procedure TImageView.DoPaint(AWorkArea: TRect; AShowNoPicture: boolean);
  649. begin
  650. if AShowNoPicture then
  651. PaintBlueAreaOnly(AWorkArea)
  652. else
  653. begin
  654. if FQueryPaintVirtualScreen and
  655. (FLastPictureParameters.defined and
  656. IsRectEmpty(GetRenderUpdateRectVS(False))) then
  657. PaintVirtualScreenImplementation(AWorkArea, FPaintBox.PaintRect)
  658. else
  659. PaintPictureImplementation(AWorkArea, FPaintBox.PaintRect);
  660. PaintBlueAreaImplementation(AWorkArea);
  661. end;
  662. if Assigned(FOnPaint) then FOnPaint(self);
  663. end;
  664. procedure TImageView.InvalidatePicture(AInvalidateAll: boolean);
  665. begin
  666. DoInvalidatePicture(AInvalidateAll, WorkArea);
  667. end;
  668. procedure TImageView.ComputePictureParams(AWorkArea: TRect);
  669. var
  670. croppedArea: TRect;
  671. begin
  672. FLastPictureParameters.canvasScale:= CanvasScale;
  673. FLastPictureParameters.imageWidth:= Image.Width;
  674. FLastPictureParameters.imageHeight:= Image.Height;
  675. FLastPictureParameters.zoomFactorX := Zoom.Factor;
  676. FLastPictureParameters.zoomFactorY := Zoom.Factor;
  677. FLastPictureParameters.zoomFactorX := Zoom.Factor*CanvasScale;
  678. FLastPictureParameters.zoomFactorY := Zoom.Factor*CanvasScale;
  679. FLastPictureParameters.zoomedArea := EmptyRect;
  680. FLastPictureParameters.scaledZoomedArea := EmptyRect;
  681. FLastPictureParameters.imageOffset := Image.ImageOffset;
  682. FLastPictureParameters.originInVS := Point(0,0);
  683. FLastPictureParameters.virtualScreenArea := EmptyRect;
  684. FLastPictureParameters.scaledVirtualScreenArea := EmptyRect;
  685. FLastPictureParameters.workArea := AWorkArea;
  686. if (AWorkArea.Right <= AWorkArea.Left) or (AWorkArea.Bottom <= AWorkArea.Top) or not Assigned(Zoom) then
  687. begin
  688. FLastPictureParameters.defined := false;
  689. exit;
  690. end;
  691. FLastPictureParameters.scaledWorkArea := rect(AWorkArea.Left*CanvasScale,
  692. AWorkArea.Top*CanvasScale, AWorkArea.Right*CanvasScale, AWorkArea.Bottom*CanvasScale);
  693. FLastPictureParameters.zoomedArea := Zoom.GetScaledArea(AWorkArea, image.Width, image.Height, image.ImageOffset);
  694. with FLastPictureParameters.zoomedArea do
  695. FLastPictureParameters.scaledZoomedArea := rect(Left*CanvasScale, Top*CanvasScale,
  696. Right*CanvasScale, Bottom*CanvasScale);
  697. {$IFDEF DRAW_TOOL_OUTSIDE_IMAGE}
  698. croppedArea := FLastPictureParameters.workArea;
  699. {$ELSE}
  700. croppedArea := RectInter(FLastPictureParameters.zoomedArea, FLastPictureParameters.workArea);
  701. {$ENDIF}
  702. if IsRectEmpty(croppedArea) then
  703. begin
  704. FLastPictureParameters.defined := false;
  705. exit;
  706. end;
  707. FLastPictureParameters.zoomFactorX := FLastPictureParameters.zoomedArea.Width/Image.Width;
  708. FLastPictureParameters.zoomFactorY := FLastPictureParameters.zoomedArea.Height/Image.Height;
  709. FLastPictureParameters.scaledZoomFactorX := FLastPictureParameters.scaledZoomedArea.Width/Image.Width;
  710. FLastPictureParameters.scaledZoomFactorY := FLastPictureParameters.scaledZoomedArea.Height/Image.Height;
  711. FLastPictureParameters.virtualScreenArea := croppedArea;
  712. with FLastPictureParameters.virtualScreenArea do
  713. FLastPictureParameters.scaledVirtualScreenArea := rect(Left*CanvasScale, Top*CanvasScale,
  714. Right*CanvasScale, Bottom*CanvasScale);
  715. FLastPictureParameters.originInVS.X := FLastPictureParameters.scaledZoomedArea.Left - FLastPictureParameters.scaledVirtualScreenArea.Left;
  716. FLastPictureParameters.originInVS.Y := FLastPictureParameters.scaledZoomedArea.Top - FLastPictureParameters.scaledVirtualScreenArea.Top;
  717. FLastPictureParameters.defined := true;
  718. end;
  719. procedure TImageView.OnZoomChanged(sender: TZoom; ANewZoom: single);
  720. Var
  721. NewBitmapPos: TPointF;
  722. begin
  723. if sender.PositionDefined then
  724. begin
  725. ComputePictureParams(WorkArea);
  726. NewBitmapPos := FormToBitmap(sender.MousePosition.X,sender.MousePosition.Y);
  727. image.ImageOffset:= point(image.ImageOffset.X + round(NewBitmapPos.X-sender.BitmapPosition.X),
  728. image.ImageOffset.Y + round(NewBitmapPos.Y-sender.BitmapPosition.Y));
  729. FPenCursorPos.bounds := EmptyRect;
  730. end;
  731. FLastPictureParameters.defined := false;
  732. end;
  733. function TImageView.GetRenderUpdateRectVS(AIncludeCurrentToolEditor: boolean): TRect;
  734. const displayMargin = 1;
  735. begin
  736. result := Image.RenderUpdateRectInPicCoord;
  737. if not IsRectEmpty(result) then
  738. begin
  739. with BitmapToVirtualScreen(result.Left-0.5,result.Top-0.5) do
  740. begin
  741. result.Left := Math.floor(X) - displayMargin;
  742. result.Top := Math.floor(Y) - displayMargin;
  743. end;
  744. with BitmapToVirtualScreen(result.Right-0.5,result.Bottom-0.5) do
  745. begin
  746. result.Right := ceil(X) + displayMargin;
  747. result.Bottom := ceil(Y) + displayMargin;
  748. end;
  749. end;
  750. result := RectUnion(result, Image.RenderUpdateRectInVSCoord);
  751. if AIncludeCurrentToolEditor and Assigned(FVirtualScreen) then
  752. result := RectUnion(result, LazPaintInstance.ToolManager.GetRenderBounds(FVirtualScreen.Width,FVirtualScreen.Height));
  753. end;
  754. function TImageView.FormToBitmap(X, Y: Integer): TPointF;
  755. begin
  756. if not FLastPictureParameters.defined then
  757. result.X := 0 else
  758. result.x := ((x-FLastPictureParameters.zoomedArea.Left)*CanvasScale+0.5)/FLastPictureParameters.scaledZoomFactorX - 0.5;
  759. if not FLastPictureParameters.defined then
  760. result.Y := 0 else
  761. result.y := ((y-FLastPictureParameters.zoomedArea.Top)*CanvasScale+0.5)/FLastPictureParameters.scaledZoomFactorY - 0.5;
  762. end;
  763. function TImageView.FormToBitmap(pt: TPoint): TPointF;
  764. begin
  765. result := FormToBitmap(pt.X,pt.Y);
  766. end;
  767. function TImageView.BitmapToForm(X, Y: Single): TPointF;
  768. begin
  769. if not FLastPictureParameters.defined then
  770. result.X := 0 else
  771. result.X := ((X+0.5)*FLastPictureParameters.scaledZoomFactorX - 0.5)/CanvasScale + FLastPictureParameters.zoomedArea.Left;
  772. if not FLastPictureParameters.defined then
  773. result.Y := 0 else
  774. result.Y := ((Y+0.5)*FLastPictureParameters.scaledZoomFactorY - 0.5)/CanvasScale + FLastPictureParameters.zoomedArea.Top;
  775. end;
  776. function TImageView.BitmapToForm(pt: TPointF): TPointF;
  777. begin
  778. result := BitmapToForm(pt.x,pt.y);
  779. end;
  780. function TImageView.BitmapToVirtualScreen(X, Y: Single): TPointF;
  781. begin
  782. if not FLastPictureParameters.defined then
  783. result.X := 0 else
  784. result.X := ((X+0.5)*FLastPictureParameters.scaledZoomFactorX - 0.5)
  785. + FLastPictureParameters.scaledZoomedArea.Left - FLastPictureParameters.scaledVirtualScreenArea.Left;
  786. if not FLastPictureParameters.defined then
  787. result.Y := 0 else
  788. result.Y := ((Y+0.5)*FLastPictureParameters.scaledZoomFactorY - 0.5)
  789. + FLastPictureParameters.scaledZoomedArea.Top - FLastPictureParameters.scaledVirtualScreenArea.Top;
  790. end;
  791. function TImageView.BitmapToVirtualScreen(ptF: TPointF): TPointF;
  792. begin
  793. result := BitmapToVirtualScreen(ptF.X,ptF.Y);
  794. end;
  795. function TImageView.GetPenCursorPosition: TVSCursorPosition;
  796. var
  797. tl,br: TPointF;
  798. margin: single;
  799. begin
  800. with LazPaintInstance.ToolManager do
  801. begin
  802. result.c := self.BitmapToVirtualScreen(ToolCurrentCursorPos);
  803. tl := self.BitmapToVirtualScreen(ToolCurrentCursorPos.X-PenWidth/2,ToolCurrentCursorPos.Y-PenWidth/2);
  804. br := self.BitmapToVirtualScreen(ToolCurrentCursorPos.X+PenWidth/2,ToolCurrentCursorPos.Y+PenWidth/2);
  805. end;
  806. result.penWidth:= max(1, 0.75 * ScreenInfo.PixelsPerInchX / OriginalDPI * CanvasScale);
  807. result.rx := (br.x-tl.x)/2-0.5;
  808. result.ry := (br.y-tl.y)/2-0.5;
  809. margin := result.penWidth/2*3 + 1;
  810. if FPenCursorVisible then
  811. begin
  812. result.bounds.left := floor(tl.x-margin)-1;
  813. result.bounds.top := floor(tl.y-margin)-1;
  814. result.bounds.right := ceil(br.x+margin)+2;
  815. result.bounds.bottom := ceil(br.y+margin)+2;
  816. end else
  817. result.bounds := EmptyRect;
  818. end;
  819. procedure TImageView.DoInvalidatePicture(AInvalidateAll: boolean; AWorkArea: TRect);
  820. var
  821. area: TRect;
  822. begin
  823. area := GetRectToInvalidate(AInvalidateAll, AWorkArea);
  824. IntersectRect(area, area, AWorkArea);
  825. FPaintBox.InvalidateRect(area);
  826. end;
  827. procedure TImageView.PictureSelectionChanged(sender: TLazPaintImage; const ARect: TRect);
  828. begin
  829. if Assigned(FSelectionHighlight) then FSelectionHighlight.NotifyChange(ARect);
  830. end;
  831. procedure TImageView.PaintVirtualScreenCursor(AWorkArea: TRect);
  832. var area: TRect;
  833. begin
  834. area := FPenCursorPos.bounds;
  835. FPenCursorPos := GetPenCursorPosition;
  836. area := RectUnion(area, FPenCursorPos.bounds);
  837. if CanvasScale > 1 then
  838. begin
  839. area.Left := area.Left div CanvasScale;
  840. area.Top := area.Top div CanvasScale;
  841. area.Right := (area.Right+CanvasScale-1) div CanvasScale;
  842. area.Bottom := (area.Bottom+CanvasScale-1) div CanvasScale;
  843. end;
  844. Types.OffsetRect(area, FLastPictureParameters.virtualScreenArea.Left,
  845. FLastPictureParameters.virtualScreenArea.Top);
  846. {$IFDEF IMAGEVIEW_DIRECTUPDATE}
  847. PaintVirtualScreenImplementation(AWorkArea, area);
  848. {$ELSE}
  849. FQueryPaintVirtualScreen := True;
  850. FPaintBox.InvalidateRect(area);
  851. {$IFDEF IMAGEVIEW_QUICKUPDATE}FPaintBox.Update;{$ENDIF}
  852. FQueryPaintVirtualScreen := False;
  853. {$ENDIF}
  854. end;
  855. function TImageView.GetRectToInvalidate(AInvalidateAll: boolean;
  856. AWorkArea: TRect): TRect;
  857. begin
  858. if not AInvalidateAll and FLastPictureParameters.defined and
  859. (FLastPictureParameters.canvasScale = CanvasScale) and
  860. (FLastPictureParameters.imageWidth = image.Width) and (FLastPictureParameters.imageHeight = image.Height) and
  861. (FLastPictureParameters.imageOffset.x = Image.ImageOffset.x) and (FLastPictureParameters.imageOffset.y = Image.ImageOffset.y) and
  862. (FLastPictureParameters.workArea.Left = AWorkArea.Left) and (FLastPictureParameters.workArea.Top = AWorkArea.Top) and
  863. (FLastPictureParameters.workArea.Right = AWorkArea.Right) and (FLastPictureParameters.workArea.Bottom = AWorkArea.Bottom) then
  864. begin
  865. result := GetRenderUpdateRectVS(True);
  866. result := RectUnion(result,FPenCursorPosBefore.bounds);
  867. result := RectUnion(result,FPenCursorPos.bounds);
  868. Types.OffsetRect(result, FLastPictureParameters.scaledVirtualScreenArea.Left,
  869. FLastPictureParameters.scaledVirtualScreenArea.Top);
  870. if CanvasScale > 1 then
  871. begin
  872. result.Left := result.Left div CanvasScale;
  873. result.Top := result.Top div CanvasScale;
  874. result.Right := (result.Right+CanvasScale-1) div CanvasScale;
  875. result.Bottom := (result.Bottom+CanvasScale-1) div CanvasScale;
  876. end;
  877. end
  878. else
  879. begin
  880. FLastPictureParameters.defined:=false;
  881. result:= rect(-maxlongint div 2,-maxlongint div 2,maxlongint div 2,maxlongint div 2);
  882. end;
  883. end;
  884. procedure TImageView.UpdateCursor(X,Y: integer);
  885. var virtualScreenPenCursorBefore: boolean;
  886. wantedCursor: TCursor;
  887. function UseVSPenCursor: boolean;
  888. begin
  889. if FLastPictureParameters.Defined and
  890. (LazPaintInstance.ToolManager.PenWidth * FLastPictureParameters.zoomFactorX > 6) and
  891. PtInRect(FLastPictureParameters.zoomedArea, Point(X,Y)) then
  892. begin
  893. FPenCursorVisible := True;
  894. {$IFNDEF DARWIN}wantedCursor := crNone;{$ENDIF}
  895. result := true;
  896. end else
  897. result := false;
  898. end;
  899. begin
  900. virtualScreenPenCursorBefore := FPenCursorVisible;
  901. FPenCursorVisible := false;
  902. wantedCursor := LazPaintInstance.ToolManager.Cursor;
  903. if LazPaintInstance.ToolManager.GetCurrentToolType in[ptPen,ptEraser,ptBrush,ptClone,ptSelectPen] then UseVSPenCursor;
  904. if not PtInRect(WorkArea, Point(X,Y)) then wantedCursor:= crDefault;
  905. if FPaintBox.Cursor <> wantedCursor then FPaintBox.Cursor := wantedCursor;
  906. if virtualScreenPenCursorBefore or FPenCursorVisible then
  907. PaintVirtualScreenCursor(WorkArea);
  908. end;
  909. procedure TImageView.DoUpdatePicture(AWorkArea: TRect);
  910. var
  911. updateArea: TRect;
  912. {$IFDEF IMAGEVIEW_DIRECTUPDATE}prevVSArea: TRect;{$ENDIF}
  913. begin
  914. {$IFDEF IMAGEVIEW_DIRECTUPDATE}
  915. prevVSArea := FLastPictureParameters.virtualScreenArea;
  916. {$ENDIF}
  917. FPenCursorPosBefore := FPenCursorPos;
  918. FPenCursorPos := GetPenCursorPosition;
  919. updateArea := GetRectToInvalidate(false, AWorkArea);
  920. FPenCursorPosBefore.bounds := EmptyRect;
  921. {$IFDEF IMAGEVIEW_DIRECTUPDATE}
  922. PaintPictureImplementation(AWorkArea, updateArea);
  923. if prevVSArea <> FLastPictureParameters.virtualScreenArea then
  924. PaintBlueAreaImplementation(AWorkArea);
  925. {$ELSE}
  926. if IntersectRect(updateArea, updateArea, AWorkArea) then
  927. begin
  928. FPaintBox.InvalidateRect(updateArea);
  929. {$IFDEF IMAGEVIEW_QUICKUPDATE}FPaintBox.Update;{$ENDIF}
  930. end;
  931. {$ENDIF}
  932. end;
  933. procedure TImageView.ReleaseMouseButtons(Shift: TShiftState);
  934. begin
  935. if not (ssLeft in Shift) and btnLeftDown then
  936. begin
  937. btnLeftDown := false;
  938. if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
  939. end;
  940. if not (ssRight in Shift) and btnRightDown then
  941. begin
  942. btnRightDown := false;
  943. if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
  944. end;
  945. if not (ssMiddle in Shift) and btnMiddleDown then
  946. begin
  947. btnMiddleDown := false;
  948. if LazPaintInstance.ToolManager.ToolUp then UpdatePicture;
  949. end;
  950. if not btnLeftDown and not btnRightDown then
  951. begin
  952. FCanCompressOrUpdateStack := true;
  953. Image.OnImageChanged.DelayedStackUpdate := False;
  954. end;
  955. end;
  956. function TImageView.GetCurrentPressure: single;
  957. begin
  958. if Assigned(FTablet) and FTablet.Present and FTablet.Entering and (FTablet.Max > 0) then
  959. result := FTablet.Pressure/FTablet.Max
  960. else
  961. result := 1;
  962. end;
  963. end.