utoollayer.pas 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UToolLayer;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, UTool, BGRABitmap, BGRABitmapTypes,
  7. BGRATransform, BGRALayers, ULayerAction, UImageDiff,
  8. UImageType, UStateType;
  9. type
  10. { TToolMoveLayer }
  11. TToolMoveLayer = class(TGenericTool)
  12. protected
  13. handMoving: boolean;
  14. handOriginF: TPointF;
  15. originalTransformBefore: TAffineMatrix;
  16. layerOffsetBefore: TPoint;
  17. FStartLayerOffset: TPoint;
  18. FStartLayerMatrix: TAffineMatrix;
  19. FStartLayerOffsetDefined: boolean;
  20. FLayerBounds: TRect;
  21. FLayerBoundsDefined: boolean;
  22. function GetIsSelectingTool: boolean; override;
  23. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  24. {%H-}rightBtn: boolean): TRect; override;
  25. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
  26. function DoToolKeyDown(var key: Word): TRect; override;
  27. function UseOriginal: boolean;
  28. procedure NeedLayerBounds;
  29. function GetAction: TLayerAction; override;
  30. function DoGetToolDrawingLayer: TBGRABitmap; override;
  31. procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
  32. function FixLayerOffset: boolean; override;
  33. function DoTranslate(dx,dy: single): TRect;
  34. procedure SaveOffsetBefore;
  35. public
  36. constructor Create(AManager: TToolManager); override;
  37. function ToolUp: TRect; override;
  38. function GetContextualToolbars: TContextualToolbars; override;
  39. function ToolCommand(ACommand: TToolCommand): boolean; override;
  40. function ToolProvideCommand(ACommand: TToolCommand): boolean; override;
  41. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth,
  42. {%H-}VirtualScreenHeight: integer;
  43. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
  44. end;
  45. { TToolTransformLayer }
  46. TToolTransformLayer = class(TGenericTool)
  47. private
  48. function GetInitialLayerBounds: TRect;
  49. function GetTransformCenter: TPointF;
  50. procedure SetTransformCenter(AValue: TPointF);
  51. procedure NeedOriginal;
  52. protected
  53. FOriginalInit: boolean;
  54. FBackupLayer: TReplaceLayerByImageOriginalDifference;
  55. FInitialOriginalMatrix: TAffineMatrix;
  56. FInitialLayerBounds: TRect;
  57. FInitialLayerBoundsDefined: boolean;
  58. FTransformCenter: TPointF;
  59. FTransformCenterDefined: boolean;
  60. FPreviousTransformCenter: TPointF;
  61. FPreviousFilter: TResampleFilter;
  62. FTransforming: boolean;
  63. FPreviousMousePos: TPointF;
  64. FSnapDown: boolean;
  65. FLastUpdateRect: TRect;
  66. FLastUpdateRectDefined: boolean;
  67. FOriginalBounds: TRect;
  68. FOriginalBoundsDefined: boolean;
  69. function GetIsSelectingTool: boolean; override;
  70. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  71. rightBtn: boolean): TRect; override;
  72. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
  73. function DoToolKeyDown(var key: Word): TRect; override;
  74. function DoToolKeyUp(var key: Word): TRect; override;
  75. procedure CancelTransform;
  76. procedure ValidateTransform;
  77. function TransformOk: boolean; virtual; abstract;
  78. function UpdateTransform: TRect; virtual; abstract;
  79. procedure TransformCenterChanged; virtual; abstract;
  80. function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; virtual; abstract;
  81. function CtrlChangesTransform: boolean; virtual; abstract;
  82. property TransformCenter: TPointF read GetTransformCenter write SetTransformCenter;
  83. function GetAction: TLayerAction; override;
  84. function DoGetToolDrawingLayer: TBGRABitmap; override;
  85. procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
  86. public
  87. constructor Create(AManager: TToolManager); override;
  88. destructor Destroy; override;
  89. function GetContextualToolbars: TContextualToolbars; override;
  90. function ToolCommand(ACommand: TToolCommand): boolean; override;
  91. function ToolProvideCommand(ACommand: TToolCommand): boolean; override;
  92. function ToolUp: TRect; override;
  93. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth,
  94. {%H-}VirtualScreenHeight: integer;
  95. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
  96. end;
  97. { TToolZoomLayer }
  98. TToolZoomLayer = class(TToolTransformLayer)
  99. private
  100. FZoom,FActualZoom,FPreviousActualZoom: single;
  101. function GetActualZoom: single;
  102. protected
  103. function TransformOk: boolean; override;
  104. function UpdateTransform: TRect; override;
  105. procedure TransformCenterChanged; override;
  106. function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; override;
  107. function CtrlChangesTransform: boolean; override;
  108. public
  109. constructor Create(AManager: TToolManager); override;
  110. end;
  111. { TToolRotateLayer }
  112. TToolRotateLayer = class(TToolTransformLayer)
  113. private
  114. FAngle,FActualAngle,FPreviousActualAngle: single;
  115. function GetActualAngle: single;
  116. protected
  117. function TransformOk: boolean; override;
  118. function UpdateTransform: TRect; override;
  119. procedure TransformCenterChanged; override;
  120. function MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean; override;
  121. function CtrlChangesTransform: boolean; override;
  122. public
  123. constructor Create(AManager: TToolManager); override;
  124. end;
  125. implementation
  126. uses LazPaintType, ugraph, LCLType, Types, BGRALayerOriginal, math, LCVectorOriginal;
  127. const
  128. VeryBigValue = maxLongInt div 2;
  129. { TToolMoveLayer }
  130. function TToolMoveLayer.GetIsSelectingTool: boolean;
  131. begin
  132. result := false;
  133. end;
  134. function TToolMoveLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  135. ptF: TPointF; rightBtn: boolean): TRect;
  136. begin
  137. result := EmptyRect;
  138. if not handMoving then
  139. begin
  140. GetAction;
  141. handMoving := true;
  142. handOriginF := ptF;
  143. if UseOriginal then Manager.Image.DraftOriginal := true;
  144. SaveOffsetBefore;
  145. end;
  146. end;
  147. function TToolMoveLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  148. ptF: TPointF): TRect;
  149. var
  150. dx, dy: Single;
  151. begin
  152. result := EmptyRect;
  153. if handMoving then
  154. begin
  155. dx := ptF.X-HandOriginF.X;
  156. dy := ptF.Y-HandOriginF.Y;
  157. if ssSnap in ShiftState then
  158. begin
  159. dx := round(dx);
  160. dy := round(dy);
  161. end;
  162. result := DoTranslate(dx,dy);
  163. end;
  164. end;
  165. function TToolMoveLayer.UseOriginal: boolean;
  166. begin
  167. with Manager.Image do
  168. result := LayerOriginalDefined[CurrentLayerIndex] and
  169. LayerOriginalKnown[CurrentLayerIndex];
  170. end;
  171. procedure TToolMoveLayer.NeedLayerBounds;
  172. var
  173. idx: Integer;
  174. begin
  175. idx := Manager.Image.CurrentLayerIndex;
  176. if not FLayerBoundsDefined then
  177. begin
  178. if UseOriginal then
  179. begin
  180. if Manager.Image.LayerOriginal[idx] is TVectorOriginal then
  181. FLayerBounds := TVectorOriginal(Manager.Image.LayerOriginal[idx]).GetAlignBounds(
  182. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  183. AffineMatrixIdentity)
  184. else
  185. FLayerBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
  186. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  187. AffineMatrixIdentity);
  188. if FLayerBounds.Left = -VeryBigValue then FLayerBounds.Left := 0;
  189. if FLayerBounds.Top = -VeryBigValue then FLayerBounds.Top := 0;
  190. if FLayerBounds.Right = VeryBigValue then FLayerBounds.Right := Manager.Image.Width;
  191. if FLayerBounds.Bottom = VeryBigValue then FLayerBounds.Bottom := Manager.Image.Height;
  192. end
  193. else
  194. FLayerBounds := Manager.Image.LayerBitmap[idx].GetImageBounds;
  195. FLayerBoundsDefined := true;
  196. end;
  197. end;
  198. function TToolMoveLayer.GetAction: TLayerAction;
  199. begin
  200. result := GetIdleAction;
  201. end;
  202. function TToolMoveLayer.DoGetToolDrawingLayer: TBGRABitmap;
  203. begin
  204. Result:= Manager.Image.CurrentLayerReadOnly; //do not modify layer data directly and ignore selection
  205. end;
  206. procedure TToolMoveLayer.OnTryStop(sender: TCustomLayerAction);
  207. begin
  208. //nothing
  209. end;
  210. function TToolMoveLayer.FixLayerOffset: boolean;
  211. begin
  212. Result:= false;
  213. end;
  214. function TToolMoveLayer.DoTranslate(dx, dy: single): TRect;
  215. var
  216. idx: integer;
  217. newTransform: TAffineMatrix;
  218. newOfs: TPoint;
  219. begin
  220. idx := Manager.Image.CurrentLayerIndex;
  221. if not FStartLayerOffsetDefined then
  222. begin
  223. FStartLayerOffsetDefined := true;
  224. NeedLayerBounds;
  225. FStartLayerOffset := Manager.Image.LayerOffset[idx];
  226. FStartLayerMatrix := Manager.Image.LayerOriginalMatrix[idx];
  227. end;
  228. if UseOriginal then
  229. begin
  230. newTransform := AffineMatrixTranslation(dx,dy)*originalTransformBefore;
  231. if Manager.Image.LayerOriginalMatrix[idx] <> newTransform then
  232. begin
  233. Manager.Image.LayerOriginalMatrix[idx] := newTransform;
  234. result := OnlyRenderChange;
  235. end;
  236. end else
  237. begin
  238. newOfs := Point(layerOffsetBefore.X+round(dx),
  239. layerOffsetBefore.Y+round(dy));
  240. if Manager.Image.LayerOffset[idx]<>newOfs then
  241. begin
  242. Manager.Image.SetLayerOffset(idx, newOfs, FLayerBounds);
  243. result := OnlyRenderChange;
  244. end;
  245. end;
  246. end;
  247. procedure TToolMoveLayer.SaveOffsetBefore;
  248. var
  249. idx: Integer;
  250. begin
  251. idx := Manager.Image.CurrentLayerIndex;
  252. if UseOriginal then
  253. originalTransformBefore := Manager.Image.LayerOriginalMatrix[idx]
  254. else
  255. originalTransformBefore := AffineMatrixIdentity;
  256. layerOffsetBefore := Manager.Image.LayerOffset[idx];
  257. end;
  258. constructor TToolMoveLayer.Create(AManager: TToolManager);
  259. begin
  260. inherited Create(AManager);
  261. handMoving := false;
  262. FStartLayerOffsetDefined:= false;
  263. end;
  264. function TToolMoveLayer.ToolUp: TRect;
  265. begin
  266. handMoving := false;
  267. result := EmptyRect;
  268. if UseOriginal then Manager.Image.DraftOriginal := false;
  269. end;
  270. function TToolMoveLayer.DoToolKeyDown(var key: Word): TRect;
  271. function Translate(dx,dy: integer): TRect;
  272. begin
  273. if handMoving or (ssAlt in ShiftState) then exit(EmptyRect);
  274. key := 0;
  275. GetAction;
  276. SaveOffsetBefore;
  277. if ssSnap in ShiftState then
  278. begin
  279. dx := dx*max(Manager.Image.Width div 20, 2);
  280. dy := dy*max(Manager.Image.Height div 20, 2);
  281. end;
  282. result := DoTranslate(dx,dy);
  283. end;
  284. var idx: integer;
  285. begin
  286. if key = VK_RETURN then
  287. begin
  288. Manager.QueryExitTool;
  289. result := EmptyRect;
  290. Key := 0;
  291. end
  292. else if key = VK_ESCAPE then
  293. begin
  294. if FStartLayerOffsetDefined then
  295. begin
  296. idx := Manager.Image.CurrentLayerIndex;
  297. if UseOriginal then
  298. Manager.Image.LayerOriginalMatrix[idx] := FStartLayerMatrix
  299. else
  300. Manager.Image.SetLayerOffset(idx, FStartLayerOffset, FLayerBounds);
  301. result := OnlyRenderChange;
  302. end else
  303. result := EmptyRect;
  304. Manager.QueryExitTool;
  305. Key := 0;
  306. end
  307. else if key = VK_LEFT then result := Translate(-1, 0)
  308. else if key = VK_RIGHT then result := Translate(1, 0)
  309. else if key = VK_UP then result := Translate(0,-1)
  310. else if key = VK_DOWN then result := Translate(0,1)
  311. else
  312. Result:=inherited DoToolKeyDown(key);
  313. end;
  314. function TToolMoveLayer.GetContextualToolbars: TContextualToolbars;
  315. begin
  316. Result:= [];
  317. end;
  318. function TToolMoveLayer.ToolCommand(ACommand: TToolCommand): boolean;
  319. var
  320. actualBounds: TRect;
  321. idx: Integer;
  322. orig: TBGRALayerCustomOriginal;
  323. begin
  324. if not ToolProvideCommand(ACommand) then exit(false);
  325. idx := Manager.Image.CurrentLayerIndex;
  326. case ACommand of
  327. tcAlignLeft,tcAlignRight,tcAlignTop,tcAlignBottom,tcCenterHorizontally,tcCenterVertically:
  328. if handMoving then exit(false) else
  329. begin
  330. NeedLayerBounds;
  331. if UseOriginal then
  332. begin
  333. orig := Manager.Image.LayerOriginal[idx];
  334. if orig is TVectorOriginal then
  335. actualBounds := TVectorOriginal(orig).GetAlignBounds(
  336. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  337. Manager.Image.LayerOriginalMatrix[idx])
  338. else
  339. actualBounds := orig.GetRenderBounds(
  340. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  341. Manager.Image.LayerOriginalMatrix[idx]);
  342. end
  343. else
  344. begin
  345. actualBounds := FLayerBounds;
  346. actualBounds.Offset(Manager.Image.LayerOffset[idx]);
  347. end;
  348. GetAction;
  349. SaveOffsetBefore;
  350. case ACommand of
  351. tcAlignLeft: DoTranslate(-actualBounds.Left, 0);
  352. tcAlignRight: DoTranslate(Manager.Image.Width-actualBounds.Right, 0);
  353. tcAlignTop: DoTranslate(0, -actualBounds.Top);
  354. tcAlignBottom: DoTranslate(0, Manager.Image.Height-actualBounds.Bottom);
  355. tcCenterHorizontally: DoTranslate((Manager.Image.Width-(actualBounds.Left+actualBounds.Right)) div 2, 0);
  356. tcCenterVertically: DoTranslate(0, (Manager.Image.Height-(actualBounds.Top+actualBounds.Bottom)) div 2);
  357. end;
  358. end;
  359. tcMoveDown: Manager.Image.MoveLayer(idx, idx-1);
  360. tcMoveToBack: Manager.Image.MoveLayer(idx, 0);
  361. tcMoveUp: Manager.Image.MoveLayer(idx, idx+1);
  362. tcMoveToFront: Manager.Image.MoveLayer(idx, Manager.Image.NbLayers-1);
  363. end;
  364. result := true;
  365. end;
  366. function TToolMoveLayer.ToolProvideCommand(ACommand: TToolCommand): boolean;
  367. begin
  368. case ACommand of
  369. tcAlignLeft,tcAlignRight,tcAlignTop,tcAlignBottom,tcCenterHorizontally,tcCenterVertically:
  370. result := not handMoving;
  371. tcMoveDown,tcMoveToBack: result := Manager.Image.CurrentLayerIndex > 0;
  372. tcMoveUp,tcMoveToFront: result := Manager.Image.CurrentLayerIndex < Manager.Image.NbLayers-1;
  373. else result := false;
  374. end;
  375. end;
  376. function TToolMoveLayer.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
  377. VirtualScreenHeight: integer;
  378. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  379. var
  380. idx, i: integer;
  381. m: TAffineMatrix;
  382. ab: TAffineBox;
  383. ptsF: ArrayOfTPointF;
  384. pts: array of TPoint;
  385. begin
  386. NeedLayerBounds;
  387. if UseOriginal then
  388. begin
  389. idx := Manager.Image.CurrentLayerIndex;
  390. m := Manager.Image.LayerOriginalMatrix[idx];
  391. with Manager.Image.LayerOffset[idx] do
  392. m := AffineMatrixTranslation(-x,-y)*m;
  393. end else m := AffineMatrixIdentity;
  394. m := AffineMatrixTranslation(-0.5,-0.5)*m;
  395. ab := TAffineBox.AffineBox(BitmapToVirtualScreen(m*PointF(FLayerBounds.Left+0.001,FLayerBounds.Top+0.001)),
  396. BitmapToVirtualScreen(m*PointF(FLayerBounds.Right-0.001,FLayerBounds.Top+0.001)),
  397. BitmapToVirtualScreen(m*PointF(FLayerBounds.Left+0.001,FLayerBounds.Bottom-0.001)));
  398. ptsF := ab.AsPolygon;
  399. setlength(pts, length(ptsF));
  400. for i := 0 to high(pts) do
  401. pts[i] := ptsF[i].Round;
  402. result := TRect.Union(pts);
  403. result.Inflate(1,1);
  404. if Assigned(VirtualScreen) then
  405. virtualScreen.DrawpolygonAntialias(pts,BGRA(230,255,230,255),BGRA(0,0,0,255),FrameDashLength);
  406. end;
  407. { TToolTransformLayer }
  408. function TToolTransformLayer.GetInitialLayerBounds: TRect;
  409. begin
  410. if not FInitialLayerBoundsDefined then
  411. begin
  412. FInitialLayerBounds := GetToolDrawingLayer.GetImageBounds;
  413. with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
  414. FInitialLayerBounds.Offset(X,Y);
  415. FInitialLayerBoundsDefined := true;
  416. end;
  417. result := FInitialLayerBounds;
  418. end;
  419. function TToolTransformLayer.GetTransformCenter: TPointF;
  420. var bounds: TRect;
  421. begin
  422. if not FTransformCenterDefined then
  423. begin
  424. bounds := GetInitialLayerBounds;
  425. if IsRectEmpty(bounds) then
  426. FTransformCenter := PointF(Manager.Image.Width/2 - 0.5,Manager.Image.Height/2 - 0.5)
  427. else
  428. begin
  429. with bounds do
  430. FTransformCenter := PointF((Left+Right)/2 - 0.5, (Top+Bottom)/2 - 0.5);
  431. end;
  432. FTransformCenterDefined := true;
  433. end;
  434. result := FTransformCenter;
  435. end;
  436. procedure TToolTransformLayer.SetTransformCenter(AValue: TPointF);
  437. begin
  438. FTransformCenter := AValue;
  439. end;
  440. procedure TToolTransformLayer.NeedOriginal;
  441. var
  442. layered: TBGRALayeredBitmap;
  443. layerIdx: Integer;
  444. begin
  445. if FOriginalInit then exit;
  446. GetAction;
  447. layerIdx := Manager.Image.CurrentLayerIndex;
  448. layered := Manager.Image.CurrentState.LayeredBitmap;
  449. if not (Manager.Image.LayerOriginalDefined[layerIdx] and
  450. Manager.Image.LayerOriginalKnown[layerIdx]) then
  451. begin
  452. if Assigned(FBackupLayer) then raise exception.Create('Backup layer already assigned');
  453. FBackupLayer:= TReplaceLayerByImageOriginalDifference.Create(Manager.Image.CurrentState, layerIdx, true);
  454. end;
  455. FInitialOriginalMatrix := layered.LayerOriginalMatrix[layerIdx];
  456. FOriginalInit := true;
  457. end;
  458. function TToolTransformLayer.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  459. ptF: TPointF; rightBtn: boolean): TRect;
  460. begin
  461. with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
  462. ptF += PointF(X,Y);
  463. if not FTransforming and not rightBtn then
  464. begin
  465. FTransforming := true;
  466. FPreviousMousePos := ptF;
  467. if FSnapDown then
  468. begin
  469. result := UpdateTransform;
  470. if IsRectEmpty(result) then result := OnlyRenderChange;
  471. end else result := EmptyRect;
  472. Manager.Image.DraftOriginal := true;
  473. end else
  474. if rightBtn then
  475. begin
  476. if FSnapDown then
  477. begin
  478. if Manager.Image.ZoomFactor > 4 then
  479. begin
  480. ptF.X := round(ptF.X*2)/2;
  481. ptF.Y := round(ptF.Y*2)/2;
  482. end else
  483. begin
  484. ptF.X := round(ptF.X);
  485. ptF.Y := round(ptF.Y);
  486. end;
  487. end;
  488. FTransformCenter := ptF;
  489. TransformCenterChanged;
  490. result := UpdateTransform;
  491. if IsRectEmpty(result) then result := OnlyRenderChange;
  492. end else
  493. result := EmptyRect;
  494. end;
  495. function TToolTransformLayer.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  496. ptF: TPointF): TRect;
  497. begin
  498. with Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex] do
  499. ptF += PointF(X,Y);
  500. if FTransforming then
  501. begin
  502. If MouseChangesTransform(FPreviousMousePos, ptF) then
  503. begin
  504. result := UpdateTransform;
  505. if result.IsEmpty then result := OnlyRenderChange;
  506. end
  507. else result := EmptyRect;
  508. FPreviousMousePos := ptF;
  509. end else
  510. result := EmptyRect;
  511. end;
  512. procedure TToolTransformLayer.CancelTransform;
  513. begin
  514. if FOriginalInit then
  515. begin
  516. Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] := FInitialOriginalMatrix;
  517. if Assigned(FBackupLayer) then
  518. begin
  519. FBackupLayer.UnapplyTo(Manager.Image.CurrentState);
  520. FreeAndNil(FBackupLayer);
  521. end;
  522. FOriginalInit := false;
  523. end;
  524. Manager.QueryExitTool;
  525. end;
  526. procedure TToolTransformLayer.ValidateTransform;
  527. var
  528. transform: TAffineMatrix;
  529. layerIdx: Integer;
  530. invTransformDiff: TCustomImageDifference;
  531. r: TRect;
  532. begin
  533. if FOriginalInit then
  534. begin
  535. if Assigned(FBackupLayer) then
  536. begin
  537. layerIdx := Manager.Image.CurrentLayerIndex;
  538. transform := Manager.Image.LayerOriginalMatrix[layerIdx];
  539. invTransformDiff := Manager.Image.CurrentState.ComputeLayerMatrixDifference(layerIdx,
  540. transform, FInitialOriginalMatrix);
  541. FBackupLayer.nextMatrix := transform;
  542. Manager.Image.AddUndo(invTransformDiff);
  543. Manager.Image.AddUndo(FBackupLayer);
  544. r := EmptyRect;
  545. Manager.Image.CurrentState.LayeredBitmap.RenderLayerFromOriginalIfNecessary(layerIdx, false, r);
  546. FBackupLayer := nil;
  547. end;
  548. FOriginalInit := false;
  549. end;
  550. Manager.QueryExitTool;
  551. end;
  552. function TToolTransformLayer.GetAction: TLayerAction;
  553. begin
  554. result := GetIdleAction;
  555. end;
  556. function TToolTransformLayer.DoGetToolDrawingLayer: TBGRABitmap;
  557. begin
  558. Result:= Manager.Image.CurrentLayerReadOnly //do not modify layer data directly and ignore selection
  559. end;
  560. procedure TToolTransformLayer.OnTryStop(sender: TCustomLayerAction);
  561. begin
  562. //nothing
  563. end;
  564. constructor TToolTransformLayer.Create(AManager: TToolManager);
  565. begin
  566. inherited Create(AManager);
  567. FSnapDown:= false;
  568. FTransformCenterDefined := false;
  569. FLastUpdateRectDefined:= false;
  570. end;
  571. destructor TToolTransformLayer.Destroy;
  572. begin
  573. if TransformOk then ValidateTransform
  574. else CancelTransform;
  575. inherited Destroy;
  576. end;
  577. function TToolTransformLayer.GetContextualToolbars: TContextualToolbars;
  578. begin
  579. Result:= [];
  580. end;
  581. function TToolTransformLayer.ToolCommand(ACommand: TToolCommand): boolean;
  582. begin
  583. if not ToolProvideCommand(ACommand) then exit(false);
  584. case ACommand of
  585. tcMoveDown: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, Manager.Image.CurrentLayerIndex-1);
  586. tcMoveToBack: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, 0);
  587. tcMoveUp: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, Manager.Image.CurrentLayerIndex+1);
  588. tcMoveToFront: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, Manager.Image.NbLayers-1);
  589. end;
  590. result := true;
  591. end;
  592. function TToolTransformLayer.ToolProvideCommand(ACommand: TToolCommand): boolean;
  593. begin
  594. case ACommand of
  595. tcMoveDown,tcMoveToBack: result := Manager.Image.CurrentLayerIndex > 0;
  596. tcMoveUp,tcMoveToFront: result := Manager.Image.CurrentLayerIndex < Manager.Image.NbLayers-1;
  597. else result := false;
  598. end;
  599. end;
  600. function TToolTransformLayer.DoToolKeyDown(var key: Word): TRect;
  601. begin
  602. if key = VK_CONTROL then
  603. begin
  604. FSnapDown:= true;
  605. if FTransforming and CtrlChangesTransform then
  606. begin
  607. result := UpdateTransform;
  608. if result.IsEmpty then result := OnlyRenderChange;
  609. end
  610. else result := EmptyRect;
  611. Key := 0;
  612. end else
  613. if Key = VK_RETURN then
  614. begin
  615. if TransformOk then ValidateTransform
  616. else CancelTransform;
  617. result := OnlyRenderChange;
  618. key := 0;
  619. end else
  620. if Key = VK_ESCAPE then
  621. begin
  622. CancelTransform;
  623. result := OnlyRenderChange;
  624. key := 0;
  625. end else
  626. result := EmptyRect;
  627. end;
  628. function TToolTransformLayer.DoToolKeyUp(var key: Word): TRect;
  629. begin
  630. if key = VK_CONTROL then
  631. begin
  632. FSnapDown := false;
  633. if FTransforming and CtrlChangesTransform then
  634. begin
  635. result := UpdateTransform;
  636. if result.IsEmpty then result := OnlyRenderChange;
  637. end
  638. else result := EmptyRect;
  639. Key := 0;
  640. end else
  641. result := EmptyRect;
  642. end;
  643. function TToolTransformLayer.ToolUp: TRect;
  644. begin
  645. if FTransforming then
  646. begin
  647. FTransforming := false;
  648. result := UpdateTransform;
  649. if result.IsEmpty then result := OnlyRenderChange;
  650. Manager.Image.DraftOriginal := false;
  651. end else
  652. Result:=EmptyRect;
  653. end;
  654. function TToolTransformLayer.Render(VirtualScreen: TBGRABitmap;
  655. VirtualScreenWidth, VirtualScreenHeight: integer;
  656. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  657. var
  658. idx, i: integer;
  659. m: TAffineMatrix;
  660. ab: TAffineBox;
  661. ptsF: ArrayOfTPointF;
  662. pts: array of TPoint;
  663. ptsRect: TRect;
  664. begin
  665. idx := Manager.Image.CurrentLayerIndex;
  666. if not FOriginalBoundsDefined then
  667. begin
  668. if Manager.Image.LayerOriginalDefined[idx] then
  669. begin
  670. if Manager.Image.LayerOriginal[idx] is TVectorOriginal then
  671. FOriginalBounds := TVectorOriginal(Manager.Image.LayerOriginal[idx]).GetAlignBounds(
  672. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  673. AffineMatrixIdentity)
  674. else
  675. FOriginalBounds := Manager.Image.LayerOriginal[idx].GetRenderBounds(
  676. Rect(-VeryBigValue,-VeryBigValue,VeryBigValue,VeryBigValue),
  677. AffineMatrixIdentity);
  678. if FOriginalBounds.Left = -VeryBigValue then FOriginalBounds.Left := 0;
  679. if FOriginalBounds.Top = -VeryBigValue then FOriginalBounds.Top := 0;
  680. if FOriginalBounds.Right = VeryBigValue then FOriginalBounds.Right := Manager.Image.Width;
  681. if FOriginalBounds.Bottom = VeryBigValue then FOriginalBounds.Bottom := Manager.Image.Height;
  682. end
  683. else
  684. FOriginalBounds := GetInitialLayerBounds;
  685. end;
  686. m := Manager.Image.LayerOriginalMatrix[idx];
  687. with Manager.Image.LayerOffset[idx] do
  688. m := AffineMatrixTranslation(-x,-y)*m;
  689. m := AffineMatrixTranslation(-0.5,-0.5)*m;
  690. with Manager.Image.LayerOffset[idx] do
  691. Result:= NicePoint(VirtualScreen,BitmapToVirtualScreen(TransformCenter-PointF(X,Y)));
  692. ab := TAffineBox.AffineBox(BitmapToVirtualScreen(m*PointF(FOriginalBounds.Left+0.001,FOriginalBounds.Top+0.001)),
  693. BitmapToVirtualScreen(m*PointF(FOriginalBounds.Right-0.001,FOriginalBounds.Top+0.001)),
  694. BitmapToVirtualScreen(m*PointF(FOriginalBounds.Left+0.001,FOriginalBounds.Bottom-0.001)));
  695. ptsF := ab.AsPolygon;
  696. setlength(pts, length(ptsF));
  697. for i := 0 to high(pts) do
  698. pts[i] := ptsF[i].Round;
  699. ptsRect := TRect.Union(pts);
  700. ptsRect.Inflate(1,1);
  701. Result.Union(ptsRect);
  702. if Assigned(VirtualScreen) then
  703. virtualScreen.DrawpolygonAntialias(pts,BGRA(230,255,230,255),BGRA(0,0,0,255),FrameDashLength);
  704. end;
  705. function TToolTransformLayer.GetIsSelectingTool: boolean;
  706. begin
  707. result := false;
  708. end;
  709. { TToolZoomLayer }
  710. function TToolZoomLayer.GetActualZoom: single;
  711. const log125 = 0.321928095;
  712. log15 = 0.584962501;
  713. var
  714. logZoom, fracZoom: single;
  715. baseZoom: single;
  716. invZoom: boolean;
  717. begin
  718. if FSnapDown then
  719. begin
  720. logZoom := ln(FZoom)/ln(2);
  721. if logZoom < 0 then
  722. begin
  723. invZoom := true;
  724. logZoom := -logZoom;
  725. end else invZoom := false;
  726. fracZoom := frac(logZoom);
  727. baseZoom := 1 shl trunc(logZoom);
  728. if fracZoom < log125/2 then result := baseZoom else
  729. if fracZoom < (log125+log15)/2 then result := baseZoom*1.25 else
  730. if fracZoom < (log15+1)/2 then result := baseZoom*1.5 else
  731. result := baseZoom*2;
  732. if invZoom then result := 1/result;
  733. end
  734. else
  735. result := FZoom;
  736. end;
  737. function TToolZoomLayer.TransformOk: boolean;
  738. begin
  739. result := FActualZoom <> 0;
  740. end;
  741. function TToolZoomLayer.UpdateTransform: TRect;
  742. begin
  743. if (FActualZoom = FPreviousActualZoom) and ((FActualZoom = 1) or (TransformCenter = FPreviousTransformCenter)) then
  744. begin
  745. result := EmptyRect;
  746. exit;
  747. end;
  748. FPreviousActualZoom := FActualZoom;
  749. FPreviousTransformCenter := TransformCenter;
  750. result := EmptyRect;
  751. NeedOriginal;
  752. Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
  753. AffineMatrixTranslation(TransformCenter.X+0.5,TransformCenter.Y+0.5)*
  754. AffineMatrixScale(FActualZoom,FActualZoom)*
  755. AffineMatrixTranslation(-TransformCenter.X-0.5,-TransformCenter.Y-0.5)*
  756. FInitialOriginalMatrix;
  757. end;
  758. procedure TToolZoomLayer.TransformCenterChanged;
  759. begin
  760. FZoom := 1;
  761. FActualZoom:= GetActualZoom;
  762. end;
  763. function TToolZoomLayer.MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean;
  764. var
  765. dist, prevDist: Single;
  766. begin
  767. dist := VectLen(ANewPos-TransformCenter);
  768. prevDist := VectLen(APrevPos-TransformCenter);
  769. if (prevDist <> 0) and (dist <> 0) then
  770. begin
  771. FZoom *= dist/prevDist;
  772. FActualZoom:= GetActualZoom;
  773. result := true;
  774. end
  775. else result := false;
  776. end;
  777. function TToolZoomLayer.CtrlChangesTransform: boolean;
  778. var
  779. newActualZoom: Single;
  780. begin
  781. newActualZoom := GetActualZoom;
  782. if FActualZoom<>newActualZoom then
  783. begin
  784. FActualZoom := newActualZoom;
  785. result := true;
  786. end else
  787. result := false;
  788. end;
  789. constructor TToolZoomLayer.Create(AManager: TToolManager);
  790. begin
  791. inherited Create(AManager);
  792. FZoom:= 1;
  793. FPreviousActualZoom := 1;
  794. end;
  795. { TToolRotateLayer }
  796. function TToolRotateLayer.GetActualAngle: single;
  797. begin
  798. if FSnapDown then
  799. result := round(FAngle/15)*15
  800. else
  801. result := FAngle;
  802. end;
  803. function TToolRotateLayer.TransformOk: boolean;
  804. begin
  805. result := true;
  806. end;
  807. procedure TToolRotateLayer.TransformCenterChanged;
  808. begin
  809. FAngle := 0;
  810. FActualAngle:= GetActualAngle;
  811. end;
  812. function TToolRotateLayer.MouseChangesTransform(APrevPos, ANewPos: TPointF): boolean;
  813. var
  814. angleDiff, newActualAngle: Single;
  815. begin
  816. angleDiff := ComputeAngle(ANewPos.X-TransformCenter.X,ANewPos.Y-TransformCenter.Y)-
  817. ComputeAngle(APrevPos.X-TransformCenter.X,APrevPos.Y-TransformCenter.Y);
  818. FAngle += angleDiff;
  819. newActualAngle := GetActualAngle;
  820. if newActualAngle <> FActualAngle then
  821. begin
  822. FActualAngle:= newActualAngle;
  823. result := true;
  824. end
  825. else result := false;
  826. end;
  827. function TToolRotateLayer.CtrlChangesTransform: boolean;
  828. var
  829. newActualAngle: Single;
  830. begin
  831. newActualAngle := GetActualAngle;
  832. if newActualAngle<>FActualAngle then
  833. begin
  834. FActualAngle := newActualAngle;
  835. result := true;
  836. end else
  837. result := false;
  838. end;
  839. function TToolRotateLayer.UpdateTransform: TRect;
  840. begin
  841. if (FActualAngle = FPreviousActualAngle) and ((FActualAngle = 0) or (TransformCenter = FPreviousTransformCenter)) then
  842. begin
  843. result := EmptyRect;
  844. exit;
  845. end;
  846. FPreviousActualAngle := FActualAngle;
  847. FPreviousTransformCenter := TransformCenter;
  848. result := EmptyRect;
  849. NeedOriginal;
  850. Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
  851. AffineMatrixTranslation(TransformCenter.X+0.5,TransformCenter.Y+0.5)*
  852. AffineMatrixRotationDeg(FActualAngle)*
  853. AffineMatrixTranslation(-TransformCenter.X-0.5,-TransformCenter.Y-0.5)*
  854. FInitialOriginalMatrix;
  855. end;
  856. constructor TToolRotateLayer.Create(AManager: TToolManager);
  857. begin
  858. inherited Create(AManager);
  859. FAngle:= 0;
  860. FPreviousActualAngle := 0;
  861. end;
  862. initialization
  863. RegisterTool(ptMoveLayer,TToolMoveLayer);
  864. RegisterTool(ptRotateLayer,TToolRotateLayer);
  865. RegisterTool(ptZoomLayer,TToolZoomLayer);
  866. end.