utoolselect.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UToolSelect;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, Graphics, BGRABitmapTypes, BGRABitmap,
  7. UTool, UToolBasic, UToolVectorial, UToolPolygon,
  8. ULayerAction, LCVectorOriginal;
  9. type
  10. { TVectorialSelectTool }
  11. TVectorialSelectTool = class(TVectorialTool)
  12. protected
  13. function GetIsSelectingTool: boolean; override;
  14. procedure AssignShapeStyle({%H-}AMatrix: TAffineMatrix; {%H-}AAlwaysFit: boolean); override;
  15. function RoundCoordinate(constref ptF: TPointF): TPointF; override;
  16. function UpdateShape(toolDest: TBGRABitmap): TRect; override;
  17. procedure ShapeChange({%H-}ASender: TObject; ABounds: TRectF; ADiff: TVectorShapeDiff); override;
  18. procedure QuickDefineEnd; override;
  19. function BigImage: boolean;
  20. public
  21. function GetContextualToolbars: TContextualToolbars; override;
  22. end;
  23. { TToolSelectRect }
  24. TToolSelectRect = class(TVectorialSelectTool)
  25. protected
  26. function ShapeClass: TVectorShapeAny; override;
  27. public
  28. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
  29. function GetContextualToolbars: TContextualToolbars; override;
  30. end;
  31. { TToolSelectEllipse }
  32. TToolSelectEllipse = class(TVectorialSelectTool)
  33. protected
  34. function ShapeClass: TVectorShapeAny; override;
  35. function GetGridMatrix: TAffineMatrix; override;
  36. public
  37. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
  38. function GetContextualToolbars: TContextualToolbars; override;
  39. end;
  40. { TToolSelectPoly }
  41. TToolSelectPoly = class(TToolPolygon)
  42. protected
  43. procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
  44. function GetIsSelectingTool: boolean; override;
  45. public
  46. function GetContextualToolbars: TContextualToolbars; override;
  47. end;
  48. { TToolSelectSpline }
  49. TToolSelectSpline = class(TToolSpline)
  50. protected
  51. procedure AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean); override;
  52. function GetIsSelectingTool: boolean; override;
  53. public
  54. function GetContextualToolbars: TContextualToolbars; override;
  55. end;
  56. { TToolMagicWand }
  57. TToolMagicWand = class(TGenericTool)
  58. protected
  59. function GetIsSelectingTool: boolean; override;
  60. function DoToolDown(toolDest: TBGRABitmap; pt: TPoint; {%H-}ptF: TPointF;
  61. rightBtn: boolean): TRect; override;
  62. public
  63. function GetContextualToolbars: TContextualToolbars; override;
  64. end;
  65. { TToolSelectionPen }
  66. TToolSelectionPen = class(TToolPen)
  67. protected
  68. function GetIsSelectingTool: boolean; override;
  69. function GetUniversalBrush(ARightButton: boolean): TUniversalBrush; override;
  70. public
  71. function GetContextualToolbars: TContextualToolbars; override;
  72. end;
  73. { TTransformSelectionTool }
  74. TTransformSelectionTool = class(TGenericTool)
  75. protected
  76. function GetIsSelectingTool: boolean; override;
  77. function GetAction: TLayerAction; override;
  78. function FixSelectionTransform: boolean; override;
  79. function DoGetToolDrawingLayer: TBGRABitmap; override;
  80. end;
  81. { TToolMoveSelection }
  82. TToolMoveSelection = class(TTransformSelectionTool)
  83. protected
  84. handMoving, notMovedAtAll: boolean;
  85. handOriginF: TPointF;
  86. selectionTransformBefore: TAffineMatrix;
  87. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  88. {%H-}rightBtn: boolean): TRect; override;
  89. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
  90. public
  91. constructor Create(AManager: TToolManager); override;
  92. function ToolUp: TRect; override;
  93. destructor Destroy; override;
  94. end;
  95. { TToolRotateSelection }
  96. TToolRotateSelection = class(TTransformSelectionTool)
  97. protected
  98. class var HintShown: boolean;
  99. FHandRotating, FHandTranslating: boolean;
  100. FHandOrigin: TPointF;
  101. FSnapMode: boolean;
  102. FUnsnappedAngle: single;
  103. FOriginalTransform: TAffineMatrix;
  104. FCurrentAngle: single;
  105. FCurrentCenter: TPointF;
  106. FOffsetBeforeMove, FFinalOffset: TPointF;
  107. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  108. rightBtn: boolean): TRect; override;
  109. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect; override;
  110. function DoToolKeyDown(var key: Word): TRect; override;
  111. function DoToolKeyUp(var key: Word): TRect; override;
  112. function GetStatusText: string; override;
  113. procedure UpdateTransform;
  114. public
  115. class procedure ForgetHintShown;
  116. constructor Create(AManager: TToolManager); override;
  117. function ToolUp: TRect; override;
  118. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
  119. destructor Destroy; override;
  120. end;
  121. implementation
  122. uses types, ugraph, LCLType, LazPaintType, Math, BGRATransform, BGRAPath,
  123. BGRAPen, LCVectorRectShapes, Controls, BGRAGrayscaleMask;
  124. procedure AssignSelectShapeStyle(AShape: TVectorShape; ASwapColor: boolean);
  125. var
  126. f: TVectorShapeFields;
  127. begin
  128. f:= AShape.MultiFields;
  129. if vsfPenFill in f then AShape.PenFill.Clear;
  130. if vsfPenStyle in f Then AShape.PenStyle := ClearPenStyle;
  131. if vsfBackFill in f then
  132. begin
  133. if ASwapColor then
  134. AShape.BackFill.SetSolid(BGRABlack)
  135. else
  136. AShape.BackFill.SetSolid(BGRAWhite);
  137. end;
  138. end;
  139. { TToolSelectSpline }
  140. procedure TToolSelectSpline.AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean);
  141. begin
  142. FShape.BeginUpdate;
  143. inherited AssignShapeStyle(AMatrix, AAlwaysFit);
  144. AssignSelectShapeStyle(FShape, FSwapColor);
  145. FShape.EndUpdate;
  146. end;
  147. function TToolSelectSpline.GetIsSelectingTool: boolean;
  148. begin
  149. Result:= true;
  150. end;
  151. function TToolSelectSpline.GetContextualToolbars: TContextualToolbars;
  152. begin
  153. Result:= [ctSplineStyle, ctCloseShape];
  154. end;
  155. { TToolSelectPoly }
  156. procedure TToolSelectPoly.AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean);
  157. begin
  158. FShape.BeginUpdate;
  159. inherited AssignShapeStyle(AMatrix, AAlwaysFit);
  160. AssignSelectShapeStyle(FShape, FSwapColor);
  161. FShape.EndUpdate;
  162. end;
  163. function TToolSelectPoly.GetIsSelectingTool: boolean;
  164. begin
  165. Result:= true;
  166. end;
  167. function TToolSelectPoly.GetContextualToolbars: TContextualToolbars;
  168. begin
  169. Result:= [];
  170. end;
  171. { TVectorialSelectTool }
  172. function TVectorialSelectTool.GetIsSelectingTool: boolean;
  173. begin
  174. Result:= true;
  175. end;
  176. procedure TVectorialSelectTool.AssignShapeStyle(AMatrix: TAffineMatrix; AAlwaysFit: boolean);
  177. begin
  178. AssignSelectShapeStyle(FShape, FSwapColor);
  179. if FShape is TCustomRectShape then
  180. begin
  181. if Manager.ShapeRatio = 0 then
  182. TCustomRectShape(FShape).FixedRatio:= EmptySingle
  183. else
  184. TCustomRectShape(FShape).FixedRatio:= Manager.ShapeRatio;
  185. end;
  186. end;
  187. function TVectorialSelectTool.RoundCoordinate(constref ptF: TPointF): TPointF;
  188. begin
  189. Result:= PointF(floor(ptF.x)+0.5,floor(ptF.y)+0.5);
  190. end;
  191. function TVectorialSelectTool.UpdateShape(toolDest: TBGRABitmap): TRect;
  192. begin
  193. if BigImage and FQuickDefine then
  194. result := OnlyRenderChange
  195. else
  196. Result:= inherited UpdateShape(toolDest);
  197. end;
  198. procedure TVectorialSelectTool.ShapeChange(ASender: TObject; ABounds: TRectF;
  199. ADiff: TVectorShapeDiff);
  200. begin
  201. if BigImage and FQuickDefine then
  202. begin
  203. ADiff.Free;
  204. exit;
  205. end;
  206. inherited ShapeChange(ASender, ABounds, ADiff);
  207. end;
  208. procedure TVectorialSelectTool.QuickDefineEnd;
  209. var
  210. toolDest: TBGRABitmap;
  211. r: TRect;
  212. begin
  213. toolDest := GetToolDrawingLayer;
  214. r := UpdateShape(toolDest);
  215. Action.NotifyChange(toolDest, r);
  216. end;
  217. function TVectorialSelectTool.BigImage: boolean;
  218. begin
  219. result := Manager.Image.Width*Manager.Image.Height > 480000;
  220. end;
  221. function TVectorialSelectTool.GetContextualToolbars: TContextualToolbars;
  222. begin
  223. Result:= [];
  224. end;
  225. { TToolSelectRect }
  226. function TToolSelectRect.ShapeClass: TVectorShapeAny;
  227. begin
  228. result := TRectShape;
  229. end;
  230. function TToolSelectRect.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
  231. VirtualScreenHeight: integer;
  232. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  233. var
  234. ab: TAffineBox;
  235. ptsF: ArrayOfTPointF;
  236. i: Integer;
  237. begin
  238. Result:= inherited Render(VirtualScreen, VirtualScreenWidth,
  239. VirtualScreenHeight, BitmapToVirtualScreen);
  240. if BigImage and FQuickDefine then
  241. begin
  242. ab := TCustomRectShape(FShape).GetAffineBox(
  243. AffineMatrixTranslation(0.5,0.5)*FEditor.Matrix*AffineMatrixTranslation(-0.5,-0.5), false);
  244. if Assigned(VirtualScreen) then
  245. begin
  246. ptsF := ab.AsPolygon;
  247. result := RectUnion(result, NiceFrame(VirtualScreen, Manager.CanvasScale, ptsF, BGRAWhite,BGRABlack));
  248. end else
  249. result := RectUnion(result, NiceFrameBounds(Manager.CanvasScale, ptsF));
  250. end;
  251. end;
  252. function TToolSelectRect.GetContextualToolbars: TContextualToolbars;
  253. begin
  254. Result:= [ctRatio];
  255. end;
  256. { TToolSelectEllipse }
  257. function TToolSelectEllipse.ShapeClass: TVectorShapeAny;
  258. begin
  259. result := TEllipseShape;
  260. end;
  261. function TToolSelectEllipse.GetGridMatrix: TAffineMatrix;
  262. begin
  263. result := AffineMatrixScale(0.5,0.5);
  264. end;
  265. function TToolSelectEllipse.Render(VirtualScreen: TBGRABitmap;
  266. VirtualScreenWidth, VirtualScreenHeight: integer;
  267. BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  268. var
  269. ptsF: ArrayOfTPointF;
  270. i: Integer;
  271. begin
  272. Result:= inherited Render(VirtualScreen, VirtualScreenWidth,
  273. VirtualScreenHeight, BitmapToVirtualScreen);
  274. if BigImage and FQuickDefine then
  275. begin
  276. if Assigned(VirtualScreen) then
  277. begin
  278. with TCustomRectShape(FShape) do
  279. ptsF := BGRAPath.ComputeEllipse(FEditor.Matrix*Origin,
  280. FEditor.Matrix*XAxis,FEditor.Matrix*YAxis);
  281. for i := 0 to high(ptsF) do
  282. ptsF[i] := ptsF[i] + PointF(0.5, 0.5);
  283. result := RectUnion(result, NiceFrame(VirtualScreen, Manager.CanvasScale, ptsF, BGRAWhite,BGRABlack));
  284. end else
  285. result := RectUnion(result, NiceFrameBounds(Manager.CanvasScale, ptsF));
  286. end;
  287. end;
  288. function TToolSelectEllipse.GetContextualToolbars: TContextualToolbars;
  289. begin
  290. Result:= [ctRatio];
  291. end;
  292. { TTransformSelectionTool }
  293. function TTransformSelectionTool.GetIsSelectingTool: boolean;
  294. begin
  295. result := true;
  296. end;
  297. function TTransformSelectionTool.GetAction: TLayerAction;
  298. begin
  299. Result:= nil;
  300. end;
  301. function TTransformSelectionTool.FixSelectionTransform: boolean;
  302. begin
  303. Result:= false;
  304. end;
  305. function TTransformSelectionTool.DoGetToolDrawingLayer: TBGRABitmap;
  306. begin
  307. result := Manager.Image.SelectionMaskReadonly;
  308. end;
  309. { TToolRotateSelection }
  310. function TToolRotateSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  311. ptF: TPointF; rightBtn: boolean): TRect;
  312. begin
  313. result := EmptyRect;
  314. if not FHandRotating and not FHandTranslating and not Manager.Image.SelectionMaskEmpty then
  315. begin
  316. if rightBtn then
  317. begin
  318. if FSnapMode then
  319. begin
  320. ptF.x := round(ptF.x*2)/2;
  321. ptF.y := round(ptF.y*2)/2;
  322. end;
  323. FCurrentAngle := 0;
  324. FFinalOffset := PointF(0, 0);
  325. FCurrentCenter := ptF;
  326. UpdateTransform;
  327. result := OnlyRenderChange;
  328. end else
  329. begin
  330. if VectLen(ptF - (FCurrentCenter + FFinalOffset)) < SelectionMaxPointDistance then
  331. FHandTranslating:= true
  332. else
  333. FHandRotating := true;
  334. FHandOrigin := ptF;
  335. FOffsetBeforeMove := FFinalOffset;
  336. end;
  337. end;
  338. end;
  339. function TToolRotateSelection.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  340. ptF: TPointF): TRect;
  341. var angleDiff: single;
  342. finalCenter, newOfs: TPointF;
  343. begin
  344. if not HintShown then
  345. begin
  346. Manager.ToolPopup(tpmHoldKeyRestrictRotation, VK_CONTROL);
  347. HintShown:= true;
  348. end;
  349. if FHandRotating and ((FHandOrigin.X <> ptF.X) or (FHandOrigin.Y <> ptF.Y)) then
  350. begin
  351. finalCenter := FCurrentCenter + FFinalOffset;
  352. angleDiff := ComputeAngle(ptF.X - finalCenter.X, ptF.Y - finalCenter.Y)-
  353. ComputeAngle(FHandOrigin.X - finalCenter.X, FHandOrigin.Y - finalCenter.Y);
  354. if FSnapMode then
  355. begin
  356. FUnsnappedAngle += angleDiff;
  357. FCurrentAngle := round(FUnsnappedAngle/15)*15;
  358. end else
  359. FCurrentAngle := FCurrentAngle + angleDiff;
  360. UpdateTransform;
  361. FHandOrigin := ptF;
  362. result := OnlyRenderChange;
  363. end else
  364. if FHandTranslating and ((FHandOrigin.X <> ptF.X) or (FHandOrigin.Y <> ptF.Y)) then
  365. begin
  366. newOfs := FOffsetBeforeMove + ptF - FHandOrigin;
  367. if FSnapMode then
  368. begin
  369. newOfs.X := round(newOfs.x*2)/2;
  370. newOfs.Y := round(newOfs.y*2)/2;
  371. end;
  372. if newOfs <> FFinalOffset then
  373. begin
  374. FFinalOffset := newOfs;
  375. UpdateTransform;
  376. result := OnlyRenderChange;
  377. end else
  378. result := EmptyRect;
  379. end else
  380. begin
  381. if VectLen(ptF - (FCurrentCenter + FFinalOffset)) < SelectionMaxPointDistance then
  382. Cursor := crSizeAll else Cursor := crDefault;
  383. result := EmptyRect;
  384. end;
  385. end;
  386. function TToolRotateSelection.GetStatusText: string;
  387. begin
  388. Result:= 'α = '+FloatToStrF(FCurrentAngle,ffFixed,5,1) + '|' +
  389. 'Δx = '+FloatToStrF(FFinalOffset.X,ffFixed,6,1) + '|' +
  390. 'Δy = '+FloatToStrF(FFinalOffset.Y,ffFixed,6,1);
  391. end;
  392. procedure TToolRotateSelection.UpdateTransform;
  393. begin
  394. Manager.Image.SelectionTransform := AffineMatrixTranslation(FFinalOffset.X, FFinalOffset.Y) *
  395. AffineMatrixTranslation(FCurrentCenter.X,FCurrentCenter.Y) *
  396. AffineMatrixRotationDeg(FCurrentAngle) *
  397. AffineMatrixTranslation(-FCurrentCenter.X,-FCurrentCenter.Y) *
  398. FOriginalTransform;
  399. end;
  400. class procedure TToolRotateSelection.ForgetHintShown;
  401. begin
  402. HintShown:= false;
  403. end;
  404. constructor TToolRotateSelection.Create(AManager: TToolManager);
  405. begin
  406. inherited Create(AManager);
  407. FCurrentCenter := Manager.Image.SelectionTransform * Manager.Image.GetSelectionMaskCenter;
  408. FOriginalTransform := Manager.Image.SelectionTransform;
  409. FCurrentAngle := 0;
  410. FFinalOffset := PointF(0, 0);
  411. end;
  412. function TToolRotateSelection.DoToolKeyDown(var key: Word): TRect;
  413. begin
  414. result := EmptyRect;
  415. if key = VK_CONTROL then
  416. begin
  417. if not FSnapMode then
  418. begin
  419. FSnapMode := true;
  420. FUnsnappedAngle := FCurrentAngle;
  421. if FHandRotating then
  422. begin
  423. FCurrentAngle := round(FUnsnappedAngle/15)*15;
  424. UpdateTransform;
  425. result := OnlyRenderChange;
  426. end else
  427. if FHandTranslating then
  428. begin
  429. FFinalOffset.x := round(FFinalOffset.x*2)/2;
  430. FFinalOffset.y := round(FFinalOffset.y*2)/2;
  431. UpdateTransform;
  432. result := OnlyRenderChange;
  433. end;
  434. end;
  435. Key := 0;
  436. end else
  437. if key = VK_ESCAPE then
  438. begin
  439. if FCurrentAngle <> 0 then
  440. begin
  441. FCurrentAngle := 0;
  442. FFinalOffset := PointF(0, 0);
  443. UpdateTransform;
  444. result := OnlyRenderChange;
  445. end;
  446. Key := 0;
  447. end;
  448. end;
  449. function TToolRotateSelection.DoToolKeyUp(var key: Word): TRect;
  450. begin
  451. if key = VK_CONTROL then
  452. begin
  453. FSnapMode := false;
  454. Key := 0;
  455. end;
  456. result := EmptyRect;
  457. end;
  458. function TToolRotateSelection.ToolUp: TRect;
  459. begin
  460. FHandRotating:= false;
  461. FHandTranslating:= false;
  462. Result:= EmptyRect;
  463. end;
  464. function TToolRotateSelection.Render(VirtualScreen: TBGRABitmap;
  465. VirtualScreenWidth, VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  466. var pictureRotateCenter: TPointF;
  467. begin
  468. pictureRotateCenter := BitmapToVirtualScreen(FCurrentCenter + FFinalOffset);
  469. result := NicePoint(VirtualScreen, pictureRotateCenter.X,pictureRotateCenter.Y);
  470. end;
  471. destructor TToolRotateSelection.Destroy;
  472. begin
  473. if FHandRotating then FHandRotating := false;
  474. if FHandTranslating then FHandTranslating := false;
  475. inherited Destroy;
  476. end;
  477. { TToolMoveSelection }
  478. function TToolMoveSelection.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  479. ptF: TPointF; rightBtn: boolean): TRect;
  480. var
  481. untransformedPtF: TPointF;
  482. begin
  483. if not handMoving and not Manager.Image.SelectionMaskEmpty then
  484. begin
  485. handMoving := true;
  486. notMovedAtAll := true;
  487. if IsAffineMatrixInversible(Manager.Image.SelectionTransform) then
  488. begin
  489. untransformedPtF := AffineMatrixInverse(Manager.Image.SelectionTransform) * ptF;
  490. if Manager.Image.SelectionMaskReadonly.GetPixel(untransformedPtF.X, untransformedPtF.Y).green <> 0 then
  491. notMovedAtAll:= false;
  492. end;
  493. handOriginF := ptF;
  494. selectionTransformBefore := Manager.Image.SelectionTransform;
  495. end;
  496. result := EmptyRect;
  497. end;
  498. function TToolMoveSelection.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  499. ptF: TPointF): TRect;
  500. var dx,dy: single;
  501. newSelTransform: TAffineMatrix;
  502. begin
  503. result := EmptyRect;
  504. if handMoving then
  505. begin
  506. dx := ptF.X-HandOriginF.X;
  507. dy := ptF.Y-HandOriginF.Y;
  508. if ssSnap in ShiftState then
  509. begin
  510. dx := round(dx);
  511. dy := round(dy);
  512. end;
  513. newSelTransform := AffineMatrixTranslation(dx,dy) * selectionTransformBefore;
  514. if Manager.Image.SelectionTransform <> newSelTransform then
  515. begin
  516. Manager.Image.SelectionTransform := newSelTransform;
  517. notMovedAtAll := false;
  518. result := OnlyRenderChange;
  519. end;
  520. end;
  521. end;
  522. constructor TToolMoveSelection.Create(AManager: TToolManager);
  523. begin
  524. inherited Create(AManager);
  525. handMoving := false;
  526. end;
  527. function TToolMoveSelection.ToolUp: TRect;
  528. begin
  529. if handMoving then
  530. begin
  531. handMoving := false;
  532. if notMovedAtAll then Manager.QueryExitTool;
  533. end;
  534. result := EmptyRect;
  535. end;
  536. destructor TToolMoveSelection.Destroy;
  537. begin
  538. if handMoving then handMoving := false;
  539. inherited Destroy;
  540. end;
  541. { TToolSelectionPen }
  542. function TToolSelectionPen.GetIsSelectingTool: boolean;
  543. begin
  544. Result:= true;
  545. end;
  546. function TToolSelectionPen.GetUniversalBrush(ARightButton: boolean): TUniversalBrush;
  547. begin
  548. if ARightButton then
  549. TBGRABitmap.SolidBrush(result, BGRABlack, dmLinearBlend)
  550. else
  551. TBGRABitmap.SolidBrush(result, BGRAWhite, dmLinearBlend);
  552. end;
  553. function TToolSelectionPen.GetContextualToolbars: TContextualToolbars;
  554. begin
  555. Result:= [ctPenWidth, ctAliasing];
  556. end;
  557. { TToolMagicWand }
  558. function TToolMagicWand.GetIsSelectingTool: boolean;
  559. begin
  560. Result:= true;
  561. end;
  562. function TToolMagicWand.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  563. ptF: TPointF; rightBtn: boolean): TRect;
  564. var penColor: TBGRAPixel;
  565. ofs: TPoint;
  566. mask: TGrayscaleMask;
  567. source: TBGRABitmap;
  568. targetRect: TRect;
  569. psource: PBGRAPixel;
  570. yb, xb: Integer;
  571. pmask: PByte;
  572. compareColor: TExpandedPixel;
  573. toleranceW: word;
  574. diff, diffDiv: integer;
  575. maskBrush: TUniversalBrush;
  576. begin
  577. if not Manager.Image.CurrentLayerVisible then
  578. begin
  579. result := EmptyRect;
  580. exit;
  581. end;
  582. if rightBtn then penColor := BGRABlack else penColor := BGRAWhite;
  583. ofs := Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex];
  584. source := Manager.Image.CurrentLayerReadOnly;
  585. targetRect := RectWithSize(ofs.x, ofs.y, source.Width, source.Height);
  586. targetRect.Intersect( rect(0,0,toolDest.Width,toolDest.Height) );
  587. if not targetRect.IsEmpty then
  588. begin
  589. if ffProgressive in Manager.FloodFillOptions then
  590. begin
  591. mask := TGrayscaleMask.Create(targetRect.Width, targetRect.Height, 0);
  592. compareColor := GammaExpansion(source.GetPixel(pt.X-ofs.X, pt.Y-ofs.Y));
  593. toleranceW := Manager.Tolerance + (Manager.Tolerance shl 8);
  594. mask.SolidBrush(maskBrush, ByteMaskWhite);
  595. Manager.Image.CurrentLayerReadOnly.ParallelFloodFill(pt.X-ofs.X, pt.Y-ofs.Y,
  596. mask, maskBrush, false, toleranceW,
  597. ofs.X - targetRect.Left, ofs.Y - targetRect.Top);
  598. diffDiv := toleranceW + 1;
  599. for yb := 0 to mask.Height-1 do
  600. begin
  601. psource := PBGRAPixel(source.GetPixelAddress(targetRect.Left - ofs.x, yb + targetRect.Top - ofs.y));
  602. pmask := mask.ScanLine[yb];
  603. for xb := mask.Width-1 downto 0 do
  604. begin
  605. if pmask^ <> 0 then
  606. begin
  607. diff := diffDiv - ExpandedDiff(psource^.ToExpanded, compareColor);
  608. if diff >= 0 then
  609. pmask^ := (pmask^ * diff + (diffDiv shr 1)) div diffDiv
  610. else
  611. pmask^ := 0;
  612. end;
  613. inc(pmask);
  614. inc(psource);
  615. end;
  616. end;
  617. toolDest.FillMask(targetRect.Left, targetRect.Top, mask, penColor, dmDrawWithTransparency);
  618. mask.Free;
  619. end else
  620. source.ParallelFloodFill(pt.X-ofs.X, pt.Y-ofs.Y,
  621. toolDest, penColor, fmDrawWithTransparency, Manager.Tolerance, ofs.X, ofs.Y);
  622. end;
  623. result := targetRect;
  624. Action.NotifyChange(toolDest, result);
  625. ValidateAction;
  626. end;
  627. function TToolMagicWand.GetContextualToolbars: TContextualToolbars;
  628. begin
  629. Result:= [ctTolerance];
  630. end;
  631. initialization
  632. RegisterTool(ptMagicWand,TToolMagicWand);
  633. RegisterTool(ptSelectPen,TToolSelectionPen);
  634. RegisterTool(ptSelectRect,TToolSelectRect);
  635. RegisterTool(ptSelectEllipse,TToolSelectEllipse);
  636. RegisterTool(ptSelectPoly,TToolSelectPoly);
  637. RegisterTool(ptSelectSpline,TToolSelectSpline);
  638. RegisterTool(ptMoveSelection,TToolMoveSelection);
  639. RegisterTool(ptRotateSelection,TToolRotateSelection);
  640. end.