utooldeformationgrid.pas 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UToolDeformationGrid;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, Types, Math, SysUtils, utool, BGRABitmapTypes, BGRABitmap, UImage,
  7. UImageType, ULayerAction, LCVectorialFill;
  8. type
  9. { TToolDeformationGrid }
  10. TToolDeformationGrid = class(TGenericTool)
  11. private
  12. FCurrentBounds,FMergedBounds: TRect;
  13. procedure ReleaseGrid;
  14. function ToolDeformationGridNeeded: boolean;
  15. procedure ValidateDeformationGrid;
  16. protected
  17. deformationGridNbX,deformationGridNbY,deformationGridX,deformationGridY: integer;
  18. deformationGridMoving: boolean;
  19. deformationOrigin: TPointF;
  20. DoingDeformation: boolean;
  21. deformationGrid: array of array of TPointF;
  22. deformationGridTexCoord: array of array of TPointF;
  23. function GetPointAt(const ptF: TPointF; var x,y: integer): boolean;
  24. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  25. {%H-}rightBtn: boolean): TRect; override;
  26. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): Trect;
  27. override;
  28. function DoToolKeyDown(var key: Word): TRect; override;
  29. function GetIsSelectingTool: boolean; override;
  30. function DoToolUpdate({%H-}toolDest: TBGRABitmap): TRect; override;
  31. public
  32. function ToolUp: TRect; override;
  33. function GetContextualToolbars: TContextualToolbars; override;
  34. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
  35. function ToolCommand(ACommand: TToolCommand): boolean; override;
  36. function ToolProvideCommand(ACommand: TToolCommand): boolean; override;
  37. destructor Destroy; override;
  38. end;
  39. { TToolTextureMapping }
  40. TToolTextureMapping = class(TGenericTool)
  41. private
  42. class var FHintShowed: boolean;
  43. FCurrentBounds: TRect;
  44. FLastTexture: TBGRABitmap;
  45. FTextureAfterAlpha: TBGRABitmap;
  46. FAdaptedTexture: TBGRABitmap;
  47. FCanReadaptTexture: boolean;
  48. FHighQuality: boolean;
  49. procedure ToolQuadNeeded;
  50. procedure ValidateQuad; virtual;
  51. procedure DrawQuad; virtual;
  52. function GetAdaptedTexture: TBGRABitmap;
  53. procedure UpdateBoundsMode(var ARectResult: TRect);
  54. protected
  55. boundsMode: boolean;
  56. quadDefined: boolean;
  57. definingQuad: boolean;
  58. quad: array of TPointF;
  59. boundsPts: array of TPointF;
  60. quadMovingIndex: integer;
  61. quadMoving,quadMovingBounds: boolean;
  62. quadMovingDelta: TPointF;
  63. function SnapIfNecessary(ptF: TPointF): TPointF;
  64. function DoToolDown({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF;
  65. {%H-}rightBtn: boolean): TRect; override;
  66. function DoToolMove({%H-}toolDest: TBGRABitmap; {%H-}pt: TPoint; ptF: TPointF): TRect;
  67. override;
  68. function DoToolKeyDown(var key: Word): TRect; override;
  69. function DoToolKeyUp(var key: Word): TRect; override;
  70. function GetIsSelectingTool: boolean; override;
  71. function GetTexture: TBGRABitmap; virtual;
  72. function GetTextureRepetition: TTextureRepetition; virtual;
  73. procedure OnTryStop({%H-}sender: TCustomLayerAction); override;
  74. function ComputeBoundsPoints: ArrayOfTPointF;
  75. procedure PrepareBackground({%H-}toolDest: TBGRABitmap; AFirstTime: boolean); virtual;
  76. function DefaultTextureCenter: TPointF; virtual;
  77. function DoToolUpdate({%H-}toolDest: TBGRABitmap): TRect; override;
  78. function GetStatusText: string; override;
  79. function GetAllowedBackFillTypes: TVectorialFillTypes; override;
  80. public
  81. constructor Create(AManager: TToolManager); override;
  82. function ToolUp: TRect; override;
  83. function GetContextualToolbars: TContextualToolbars; override;
  84. function Render(VirtualScreen: TBGRABitmap; {%H-}VirtualScreenWidth, {%H-}VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction):TRect; override;
  85. function ToolProvideCommand(ACommand: TToolCommand): boolean; override;
  86. function ToolCommand(ACommand: TToolCommand): boolean; override;
  87. destructor Destroy; override;
  88. end;
  89. { TToolLayerMapping }
  90. TToolLayerMapping = class(TToolTextureMapping)
  91. protected
  92. FTexture: TBGRABitmap;
  93. FDefaultTextureCenter: TPointF;
  94. FAlreadyDrawnOnce: boolean;
  95. procedure PrepareTexture;
  96. procedure PrepareBackground(toolDest: TBGRABitmap; {%H-}AFirstTime: boolean); override;
  97. function GetTexture: TBGRABitmap; override;
  98. function DefaultTextureCenter: TPointF; override;
  99. function GetTextureRepetition: TTextureRepetition; override;
  100. procedure ValidateQuad; override;
  101. function GetAllowedBackFillTypes: TVectorialFillTypes; override;
  102. public
  103. constructor Create(AManager: TToolManager); override;
  104. function GetContextualToolbars: TContextualToolbars; override;
  105. destructor Destroy; override;
  106. end;
  107. implementation
  108. uses LCLType, ugraph, LCScaleDPI, LazPaintType, BGRAFillInfo, BGRATransform, Controls;
  109. { TToolLayerMapping }
  110. procedure TToolLayerMapping.PrepareTexture;
  111. var src: TBGRABitmap;
  112. bounds: TRect;
  113. begin
  114. if FTexture = nil then
  115. begin
  116. src := Action.BackupDrawingLayer;
  117. bounds := src.GetImageBounds;
  118. if IsRectEmpty(bounds) then
  119. bounds := rect(0,0,1,1);
  120. FTexture := src.GetPart(bounds) as TBGRABitmap;
  121. FDefaultTextureCenter := PointF((bounds.Left+bounds.Right)/2-0.5,(bounds.Top+bounds.Bottom)/2-0.5);
  122. end;
  123. end;
  124. procedure TToolLayerMapping.PrepareBackground(toolDest: TBGRABitmap;
  125. AFirstTime: boolean);
  126. var
  127. r: TRect;
  128. begin
  129. if not FAlreadyDrawnOnce then
  130. begin
  131. FAlreadyDrawnOnce := true;
  132. r := toolDest.GetImageBounds;
  133. end else
  134. r := FCurrentBounds;
  135. toolDest.FillRect(r, BGRAPixelTransparent, dmSet);
  136. Action.NotifyChange(toolDest, r);
  137. end;
  138. function TToolLayerMapping.GetTexture: TBGRABitmap;
  139. begin
  140. PrepareTexture;
  141. result := FTexture;
  142. end;
  143. function TToolLayerMapping.DefaultTextureCenter: TPointF;
  144. begin
  145. PrepareTexture;
  146. result := FDefaultTextureCenter;
  147. end;
  148. function TToolLayerMapping.GetTextureRepetition: TTextureRepetition;
  149. begin
  150. if poRepeat in Manager.PerspectiveOptions then
  151. Result:= trRepeatBoth;
  152. end;
  153. procedure TToolLayerMapping.ValidateQuad;
  154. begin
  155. inherited ValidateQuad;
  156. Manager.QueryExitTool;
  157. end;
  158. function TToolLayerMapping.GetAllowedBackFillTypes: TVectorialFillTypes;
  159. begin
  160. Result:= [vftSolid,vftGradient,vftTexture];
  161. end;
  162. constructor TToolLayerMapping.Create(AManager: TToolManager);
  163. begin
  164. inherited Create(AManager);
  165. ToolQuadNeeded;
  166. end;
  167. function TToolLayerMapping.GetContextualToolbars: TContextualToolbars;
  168. begin
  169. Result:= [ctPerspective];
  170. end;
  171. destructor TToolLayerMapping.Destroy;
  172. begin
  173. FreeAndNil(FTexture);
  174. inherited Destroy;
  175. end;
  176. { TToolTextureMapping }
  177. procedure TToolTextureMapping.ToolQuadNeeded;
  178. var
  179. tx,ty: single;
  180. ratio,temp: single;
  181. center: TPointF;
  182. begin
  183. if not quadDefined and (GetTexture <> nil) and (GetTexture.Width > 0) and (GetTexture.Height > 0) then
  184. begin
  185. tx := GetTexture.Width;
  186. ty := GetTexture.Height;
  187. ratio := 1;
  188. if tx > Manager.Image.Width then
  189. ratio := Manager.Image.Width/tx;
  190. if ty > Manager.Image.Height then
  191. begin
  192. temp := Manager.Image.Height/ty;
  193. if temp < ratio then ratio := temp;
  194. end;
  195. if ratio > 0 then
  196. begin
  197. setlength(quad,4);
  198. center := DefaultTextureCenter;
  199. quad[0] := PointF(round(center.x-tx*ratio/2+0.5)-0.5,round(center.y -ty*ratio/2 + 0.5)-0.5);
  200. quad[1] := PointF(quad[0].x + tx*ratio,quad[0].y);
  201. quad[2] := PointF(quad[1].x, quad[1].Y + ty*ratio);
  202. quad[3] := PointF(quad[0].x, quad[2].y);
  203. quadDefined:= true;
  204. PrepareBackground(GetToolDrawingLayer, True);
  205. DrawQuad;
  206. Action.NotifyChange(GetToolDrawingLayer, FCurrentBounds);
  207. end;
  208. end;
  209. end;
  210. procedure TToolTextureMapping.ValidateQuad;
  211. begin
  212. if quadDefined then
  213. begin
  214. if Manager.Image.Width*Manager.Image.Height <= 786432 then
  215. begin
  216. PrepareBackground(GetToolDrawingLayer,False);
  217. FHighQuality := true;
  218. FCanReadaptTexture:= true;
  219. DrawQuad;
  220. FCanReadaptTexture:= false;
  221. FHighQuality := false;
  222. Action.NotifyChange(GetToolDrawingLayer, FCurrentBounds);
  223. end;
  224. ValidateAction;
  225. quadDefined := false;
  226. quad := nil;
  227. FLastTexture.FreeReference;
  228. FLastTexture := nil;
  229. end;
  230. end;
  231. procedure TToolTextureMapping.DrawQuad;
  232. const OversampleQuality = 2;
  233. var
  234. tex: TBGRABitmap;
  235. persp: TBGRAPerspectiveScannerTransform;
  236. dest: TBGRABitmap;
  237. quadHQ: array of TPointF;
  238. i: integer;
  239. scanRepeat: TBGRABitmapScanner;
  240. scan: IBGRAScanner;
  241. function AlmostInt(value: single): boolean;
  242. begin
  243. result := (value-round(value)) < 1e-6;
  244. end;
  245. begin
  246. if quadDefined then
  247. begin
  248. if (quad[1].y = quad[0].y) and (quad[3].x = quad[0].x) and (quad[2].x = quad[1].x) and (quad[3].y = quad[2].y) and
  249. AlmostInt(quad[0].x+0.5) and AlmostInt(quad[0].y+0.5) and AlmostInt(quad[2].x+0.5) and AlmostInt(quad[2].y+0.5) and
  250. (round(quad[2].x-quad[0].x) = GetTexture.Width) and (round(quad[2].y-quad[0].y) = GetTexture.Height) then
  251. FHighQuality := false;
  252. tex := GetAdaptedTexture;
  253. if tex <> nil then
  254. begin
  255. if GetTextureRepetition <> trNone then
  256. FCurrentBounds := rect(0,0,Manager.Image.Width,Manager.Image.Height)
  257. else
  258. FCurrentBounds := GetShapeBounds([quad[0],quad[1],quad[2],quad[3]],1);
  259. if FHighQuality then
  260. begin
  261. dest := TBGRABitmap.Create(FCurrentBounds.Width*OversampleQuality,FCurrentBounds.Height*OversampleQuality);
  262. setlength(quadHQ, length(quad));
  263. for i := 0 to high(quad) do quadHQ[i] := (quad[i]+PointF(0.5,0.5))*OversampleQuality - PointF(0.5,0.5) - PointF(FCurrentBounds.TopLeft)*OversampleQuality;
  264. end
  265. else
  266. begin
  267. dest := GetToolDrawingLayer;
  268. quadHQ := quad;
  269. dest.ClipRect := FCurrentBounds;
  270. end;
  271. if GetTextureRepetition <> trNone then
  272. begin
  273. if GetTextureRepetition <> trRepeatBoth then
  274. begin
  275. scanRepeat := TBGRABitmapScanner.Create(tex,
  276. GetTextureRepetition in [trRepeatX,trRepeatBoth],
  277. GetTextureRepetition in [trRepeatY,trRepeatBoth], Point(0,0) );
  278. scan := scanRepeat;
  279. end else
  280. begin
  281. scanRepeat := nil;
  282. scan := tex;
  283. end;
  284. persp := TBGRAPerspectiveScannerTransform.Create(scan,[PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
  285. PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5)],quadHQ);
  286. persp.IncludeOppositePlane := poTwoPlanes in Manager.PerspectiveOptions;
  287. dest.FillRect(0,0,dest.Width,dest.Height,persp,dmDrawWithTransparency);
  288. persp.Free;
  289. scan := nil;
  290. scanRepeat.Free;
  291. end else
  292. begin
  293. dest.FillQuadPerspectiveMappingAntialias(quadHQ[0],quadHQ[1],quadHQ[2],quadHQ[3],tex,PointF(-0.5,-0.5),PointF(tex.Width-0.5,-0.5),
  294. PointF(tex.Width-0.5,tex.Height-0.5),PointF(-0.5,tex.Height-0.5), rect(0,0,tex.Width,tex.Height));
  295. end;
  296. if FHighQuality then
  297. begin
  298. BGRAReplace(dest, dest.Resample(dest.Width div OversampleQuality, dest.Height div OversampleQuality,rmSimpleStretch));
  299. BGRAReplace(dest, dest.FilterSharpen(96/256));
  300. GetToolDrawingLayer.PutImage(FCurrentBounds.Left,FCurrentBounds.Top,dest,dmDrawWithTransparency);
  301. FreeAndNil(dest);
  302. end else
  303. dest.NoClip;
  304. end;
  305. end
  306. else
  307. FCurrentBounds := EmptyRect;
  308. end;
  309. function TToolTextureMapping.GetAdaptedTexture: TBGRABitmap;
  310. var tx,ty: integer;
  311. precisionFactor: single;
  312. begin
  313. if GetTextureRepetition <> trNone then //cannot optimize size
  314. begin
  315. result := GetTexture;
  316. exit;
  317. end;
  318. if GetTexture = nil then
  319. begin
  320. result := nil;
  321. exit;
  322. end else
  323. begin
  324. if FHighQuality then precisionFactor := 3
  325. else precisionFactor:= 1.5;
  326. tx := ceil(Max(VectLen(quad[1]-quad[0]),VectLen(quad[2]-quad[3]))*precisionFactor);
  327. ty := ceil(Max(VectLen(quad[2]-quad[1]),VectLen(quad[3]-quad[0]))*precisionFactor);
  328. if tx < 1 then tx := 1;
  329. if ty < 1 then ty := 1;
  330. if tx > GetTexture.Width then tx := GetTexture.Width;
  331. if ty > GetTexture.Height then ty := GetTexture.Height;
  332. if (tx = GetTexture.Width) and (ty = GetTexture.Height) then
  333. begin
  334. result := GetTexture;
  335. exit;
  336. end;
  337. if (FAdaptedTexture = nil) or FCanReadaptTexture then
  338. begin
  339. if (FAdaptedTexture <> nil) and ((FAdaptedTexture.Width <> tx) or (FAdaptedTexture.Height <> ty)) then
  340. FreeAndNil(FAdaptedTexture);
  341. if FAdaptedTexture = nil then
  342. begin
  343. GetTexture.ResampleFilter := rfLinear;
  344. FAdaptedTexture := GetTexture.Resample(tx,ty,rmFineResample) as TBGRABitmap;
  345. end;
  346. end;
  347. result := FAdaptedTexture;
  348. exit;
  349. end;
  350. end;
  351. procedure TToolTextureMapping.UpdateBoundsMode(var ARectResult: TRect);
  352. begin
  353. if not boundsMode and not quadMoving and ([ssAlt, ssShift]*ShiftState <> []) then
  354. begin
  355. boundsMode := true;
  356. boundsPts := ComputeBoundsPoints;
  357. if IsRectEmpty(ARectResult) then
  358. ARectResult := OnlyRenderChange;
  359. end else
  360. if boundsMode and not quadMoving and ([ssAlt, ssShift]*ShiftState = []) then
  361. begin
  362. boundsMode := false;
  363. boundsPts := ComputeBoundsPoints;
  364. if IsRectEmpty(ARectResult) then
  365. ARectResult := OnlyRenderChange;
  366. end;
  367. end;
  368. function TToolTextureMapping.SnapIfNecessary(ptF: TPointF): TPointF;
  369. begin
  370. if not (ssSnap in ShiftState) then result := ptF else
  371. result := PointF(round(ptF.X),round(ptF.Y));
  372. end;
  373. function TToolTextureMapping.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  374. ptF: TPointF; rightBtn: boolean): TRect;
  375. var
  376. n: Integer;
  377. curDist,minDist: single;
  378. pts: array of TPointF;
  379. begin
  380. result := EmptyRect;
  381. if rightBtn then exit;
  382. if not quadDefined then
  383. begin
  384. if not definingQuad then
  385. begin
  386. if GetTexture = nil then
  387. Manager.ToolPopup(tpmNothingToBeDeformed)
  388. else
  389. begin
  390. definingQuad := true;
  391. setlength(quad,4);
  392. quad[0] := ptF;
  393. quad[1] := ptF;
  394. quad[2] := ptF;
  395. quad[3] := ptF;
  396. end;
  397. end;
  398. exit;
  399. end;
  400. UpdateBoundsMode(result);
  401. if boundsMode then
  402. pts := boundsPts
  403. else
  404. pts := quad;
  405. minDist := sqr(DoScaleX(10,OriginalDPI));
  406. for n := 0 to high(pts) do
  407. begin
  408. curDist := sqr(ptF.x-pts[n].x)+sqr(ptF.y-pts[n].y);
  409. if curDist < minDist then
  410. begin
  411. minDist := curDist;
  412. quadMovingIndex := n;
  413. quadMovingDelta := pts[n]-PtF;
  414. quadMoving := True;
  415. quadMovingBounds := boundsMode;
  416. end;
  417. end;
  418. if not quadMoving and IsPointInPolygon(pts, ptF, true) then
  419. begin
  420. quadMovingIndex := -1;
  421. quadMovingDelta := (quad[0]+quad[2])*0.5-ptF;
  422. quadMoving := true;
  423. quadMovingBounds := boundsMode;
  424. end;
  425. end;
  426. function NonZero(AValue, ADefault: single): single;
  427. begin
  428. if AValue = 0 then result := ADefault
  429. else result := AValue;
  430. end;
  431. function TToolTextureMapping.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  432. ptF: TPointF): TRect;
  433. var n: integer;
  434. delta,prevSize,newSize: TPointF;
  435. curBounds: array of TPointF;
  436. ratioX,ratioY,ratio: single;
  437. avgSize: single;
  438. begin
  439. if definingQuad then
  440. begin
  441. if ssShift in ShiftState then
  442. begin
  443. if (GetTexture <> nil) and (GetTexture.Height <> 0)
  444. and (GetTexture.Width <> 0) then
  445. ratio := GetTexture.Width/GetTexture.Height;
  446. newSize := ptF - quad[0];
  447. avgSize := (abs(newSize.x)+abs(newSize.y))/2;
  448. ptF.x := quad[0].x+avgSize*NonZero(sign(newSize.x),1)*ratio/((ratio+1)/2);
  449. ptF.y := quad[0].y+avgSize*NonZero(sign(newSize.y),1)*1/((ratio+1)/2);
  450. end;
  451. quad[2] := ptF;
  452. quad[1].x := ptF.x;
  453. quad[3].y := ptF.y;
  454. result := OnlyRenderChange;
  455. exit;
  456. end;
  457. result := EmptyRect;
  458. if not FHintShowed then
  459. begin
  460. Manager.ToolPopup(tpmHoldKeysScaleMode, VK_SHIFT);
  461. FHintShowed:= true;
  462. end;
  463. Manager.HintReturnValidates;
  464. if quadMoving then
  465. begin
  466. if quadMovingIndex = -1 then
  467. begin
  468. delta := SnapIfNecessary(quadMovingDelta + ptF) - ((quad[0]+quad[2])*0.5);
  469. for n := 0 to high(quad) do
  470. quad[n] += delta;
  471. if quadMovingBounds then boundsPts := ComputeBoundsPoints;
  472. end
  473. else
  474. if quadMovingBounds then
  475. begin
  476. boundsPts[quadMovingIndex] := SnapIfNecessary(quadMovingDelta + ptF);
  477. case quadMovingIndex of
  478. 0:begin
  479. boundsPts[1].y := boundsPts[quadMovingIndex].y;
  480. boundsPts[3].x := boundsPts[quadMovingIndex].x;
  481. end;
  482. 1:begin
  483. boundsPts[0].y := boundsPts[quadMovingIndex].y;
  484. boundsPts[2].x := boundsPts[quadMovingIndex].x;
  485. end;
  486. 2:begin
  487. boundsPts[3].y := boundsPts[quadMovingIndex].y;
  488. boundsPts[1].x := boundsPts[quadMovingIndex].x;
  489. end;
  490. 3:begin
  491. boundsPts[2].y := boundsPts[quadMovingIndex].y;
  492. boundsPts[0].x := boundsPts[quadMovingIndex].x;
  493. end;
  494. end;
  495. if ssShift in ShiftState then
  496. begin
  497. curBounds := ComputeBoundsPoints;
  498. prevSize := curBounds[2]-curBounds[0];
  499. newSize := boundsPts[2]-boundsPts[0];
  500. if (abs(prevSize.x) > 1e-6) and (abs(prevSize.y) > 1e-6) then
  501. begin
  502. ratioX := abs(newSize.X/prevSize.X);
  503. ratioY := abs(newSize.Y/prevSize.Y);
  504. ratio := (ratioX+ratioY)/2;
  505. newSize.X := abs(prevSize.X)*ratio*NonZero(Sign(newSize.X),1);
  506. newSize.Y := abs(prevSize.Y)*ratio*NonZero(Sign(newSize.Y),1);
  507. case quadMovingIndex of
  508. 0: boundsPts[0] := boundsPts[2]-newSize;
  509. 1: boundsPts[1] := boundsPts[3]+PointF(newSize.X,-newSize.Y);
  510. 2: boundsPts[2] := boundsPts[0]+newSize;
  511. 3: boundsPts[3] := boundsPts[1]+PointF(-newSize.X,newSize.Y);
  512. end;
  513. case quadMovingIndex of
  514. 0:begin
  515. boundsPts[1].y := boundsPts[quadMovingIndex].y;
  516. boundsPts[3].x := boundsPts[quadMovingIndex].x;
  517. end;
  518. 1:begin
  519. boundsPts[0].y := boundsPts[quadMovingIndex].y;
  520. boundsPts[2].x := boundsPts[quadMovingIndex].x;
  521. end;
  522. 2:begin
  523. boundsPts[3].y := boundsPts[quadMovingIndex].y;
  524. boundsPts[1].x := boundsPts[quadMovingIndex].x;
  525. end;
  526. 3:begin
  527. boundsPts[2].y := boundsPts[quadMovingIndex].y;
  528. boundsPts[0].x := boundsPts[quadMovingIndex].x;
  529. end;
  530. end;
  531. end;
  532. end;
  533. end
  534. else
  535. quad[quadMovingIndex] := SnapIfNecessary(quadMovingDelta + ptF);
  536. PrepareBackground(toolDest,False);
  537. DrawQuad;
  538. result := FCurrentBounds;
  539. end;
  540. UpdateBoundsMode(result);
  541. end;
  542. function TToolTextureMapping.GetIsSelectingTool: boolean;
  543. begin
  544. Result:= false;
  545. end;
  546. function TToolTextureMapping.GetTexture: TBGRABitmap;
  547. begin
  548. if (Manager.BackFill.Texture = nil) or (Manager.BackFill.Texture = FLastTexture) then
  549. begin
  550. if FTextureAfterAlpha <> nil then
  551. result := FTextureAfterAlpha
  552. else
  553. result := FLastTexture;
  554. end
  555. else
  556. begin
  557. if (Manager.BackFill.Texture <> nil) and (Manager.BackFill.TextureOpacity <> 255) then
  558. begin
  559. FTextureAfterAlpha := Manager.BackFill.Texture.Duplicate as TBGRABitmap;
  560. FTextureAfterAlpha.ApplyGlobalOpacity(Manager.BackFill.TextureOpacity);
  561. result := FTextureAfterAlpha;
  562. end else
  563. begin
  564. result := Manager.BackFill.Texture;
  565. FreeAndNil(FTextureAfterAlpha);
  566. end;
  567. FLastTexture.FreeReference;
  568. FLastTexture := Manager.BackFill.Texture.NewReference as TBGRABitmap;
  569. end;
  570. end;
  571. function TToolTextureMapping.GetTextureRepetition: TTextureRepetition;
  572. begin
  573. if Manager.BackFill.FillType = vftTexture then
  574. result := Manager.BackFill.TextureRepetition
  575. else
  576. result := trNone;
  577. end;
  578. procedure TToolTextureMapping.OnTryStop(sender: TCustomLayerAction);
  579. begin
  580. //nothing
  581. end;
  582. function TToolTextureMapping.ComputeBoundsPoints: ArrayOfTPointF;
  583. var
  584. minPt,maxPt: TPointF;
  585. i: integer;
  586. begin
  587. if quadDefined then
  588. begin
  589. minPt := quad[low(quad)];
  590. maxPt := quad[low(quad)];
  591. for i := 1 to high(quad) do
  592. begin
  593. if quad[i].x < minPt.X then minPt.x := quad[i].x;
  594. if quad[i].x > maxPt.X then maxPt.x := quad[i].x;
  595. if quad[i].y < minPt.y then minPt.y := quad[i].y;
  596. if quad[i].y > maxPt.y then maxPt.y := quad[i].y;
  597. end;
  598. result := PointsF([minPt, PointF(maxPt.X,minPt.Y), maxPt, PointF(MinPt.X,MaxPt.Y)]);
  599. end else
  600. result := nil;
  601. end;
  602. procedure TToolTextureMapping.PrepareBackground(toolDest: TBGRABitmap;
  603. AFirstTime: boolean);
  604. begin
  605. if not AFirstTime then RestoreBackupDrawingLayer;
  606. end;
  607. function TToolTextureMapping.DefaultTextureCenter: TPointF;
  608. begin
  609. result := PointF(Manager.Image.Width/2-0.5-LayerOffset.X,Manager.Image.Height/2-0.5-LayerOffset.Y);
  610. end;
  611. function TToolTextureMapping.DoToolUpdate(toolDest: TBGRABitmap): TRect;
  612. begin
  613. if quadDefined then
  614. begin
  615. PrepareBackground(GetToolDrawingLayer,False);
  616. DrawQuad;
  617. result := FCurrentBounds;
  618. end
  619. else
  620. result := EmptyRect;
  621. end;
  622. function TToolTextureMapping.GetStatusText: string;
  623. var
  624. i: Integer;
  625. begin
  626. result := '';
  627. for i := 0 to high(quad) do
  628. begin
  629. if i > 0 then result += '|';
  630. result += 'x'+inttostr(i+1)+' = '+inttostr(round(quad[i].x+0.5))+'|'+
  631. 'y'+inttostr(i+1)+' = '+inttostr(round(quad[i].y+0.5));
  632. end;
  633. end;
  634. function TToolTextureMapping.GetAllowedBackFillTypes: TVectorialFillTypes;
  635. begin
  636. Result:= [vftTexture];
  637. end;
  638. constructor TToolTextureMapping.Create(AManager: TToolManager);
  639. begin
  640. inherited Create(AManager);
  641. FCurrentBounds := EmptyRect;
  642. FHighQuality:= False;
  643. FLastTexture := nil;
  644. quadDefined:= false;
  645. definingQuad:= false;
  646. end;
  647. function TToolTextureMapping.DoToolKeyDown(var key: Word): TRect;
  648. begin
  649. result := EmptyRect;
  650. if Key = VK_RETURN then
  651. begin
  652. if quadDefined then
  653. begin
  654. ValidateQuad;
  655. result := EmptyRect;
  656. key := 0;
  657. end;
  658. end else
  659. if Key = VK_ESCAPE then
  660. begin
  661. if quadDefined then
  662. begin
  663. CancelActionPartially;
  664. result := OnlyRenderChange;
  665. manager.QueryExitTool;
  666. key := 0;
  667. end;
  668. end else
  669. if (Key = VK_SHIFT) or (Key = VK_MENU) then
  670. UpdateBoundsMode(result);
  671. end;
  672. function TToolTextureMapping.DoToolKeyUp(var key: Word): TRect;
  673. begin
  674. Result:= EmptyRect;
  675. if (Key = VK_SHIFT) or (Key = VK_MENU) then
  676. UpdateBoundsMode(result);
  677. end;
  678. function TToolTextureMapping.ToolUp: TRect;
  679. var prevSize,newSize: TPointF;
  680. oldBounds: array of TPointF;
  681. i: integer;
  682. redraw: boolean;
  683. begin
  684. if definingQuad then
  685. begin
  686. definingQuad:= false;
  687. quadDefined:= true;
  688. PrepareBackground(GetToolDrawingLayer,False);
  689. FCanReadaptTexture:= true;
  690. DrawQuad;
  691. FCanReadaptTexture:= false;
  692. result := FCurrentBounds;
  693. exit;
  694. end;
  695. if quadMoving then
  696. begin
  697. redraw := GetTextureRepetition = trNone;
  698. if quadMovingBounds then
  699. begin
  700. oldBounds := ComputeBoundsPoints;
  701. prevSize := oldBounds[2]-oldBounds[0];
  702. newSize := boundsPts[2]-boundsPts[0];
  703. if (abs(newSize.x) > 1e-6) and (abs(newSize.y) > 1e-6) and
  704. (abs(prevSize.x) > 1e-6) and (abs(prevSize.y) > 1e-6) then
  705. begin
  706. for i := low(quad) to high(quad) do
  707. begin
  708. quad[i] -= oldBounds[0];
  709. quad[i].x *= newSize.X/prevSize.X;
  710. quad[i].y *= newSize.Y/prevSize.Y;
  711. quad[i] += boundsPts[0];
  712. end;
  713. end;
  714. quadMovingBounds := false;
  715. boundsPts := ComputeBoundsPoints;
  716. redraw := true;
  717. end;
  718. if redraw then
  719. begin
  720. PrepareBackground(GetToolDrawingLayer,False);
  721. FCanReadaptTexture:= true;
  722. DrawQuad;
  723. FCanReadaptTexture:= false;
  724. result := FCurrentBounds;
  725. end else
  726. result := EmptyRect;
  727. quadMoving := false;
  728. end else
  729. result := EmptyRect;
  730. end;
  731. function TToolTextureMapping.GetContextualToolbars: TContextualToolbars;
  732. begin
  733. Result:= [ctBackFill,ctPerspective];
  734. end;
  735. function TToolTextureMapping.Render(VirtualScreen: TBGRABitmap;
  736. VirtualScreenWidth, VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  737. procedure DrawPoints(pts: array of TPointF; alpha: byte);
  738. var curPt,nextPt: TPointF;
  739. n: Integer;
  740. begin
  741. For n := 0 to high(pts) do
  742. begin
  743. curPt := BitmapToVirtualScreen(pts[n]);
  744. nextPt := BitmapToVirtualScreen(pts[(n+1)mod length(pts)]);
  745. NiceLine(VirtualScreen, curPt.X,curPt.Y,nextPt.x,nextPt.y,alpha);
  746. end;
  747. For n := 0 to high(pts) do
  748. begin
  749. curPt := BitmapToVirtualScreen(pts[n]);
  750. result := RectUnion(result,NicePoint(VirtualScreen, curPt.X,curPt.Y,alpha));
  751. end;
  752. end;
  753. begin
  754. result := EmptyRect;
  755. if not quadDefined and not definingQuad then exit;
  756. if boundsMode or quadMovingBounds then
  757. begin
  758. DrawPoints(quad,80);
  759. DrawPoints(boundsPts,192);
  760. end else
  761. DrawPoints(quad,192);
  762. end;
  763. function TToolTextureMapping.ToolProvideCommand(ACommand: TToolCommand
  764. ): boolean;
  765. begin
  766. case ACommand of
  767. tcFinish: result := quadDefined;
  768. else result := false;
  769. end;
  770. end;
  771. function TToolTextureMapping.ToolCommand(ACommand: TToolCommand): boolean;
  772. begin
  773. case ACommand of
  774. tcFinish: if quadDefined then
  775. begin
  776. ValidateQuad;
  777. result := true;
  778. end else
  779. result := false;
  780. else result := false;
  781. end;
  782. end;
  783. destructor TToolTextureMapping.Destroy;
  784. begin
  785. ValidateAction;
  786. FLastTexture.FreeReference;
  787. FreeAndNil(FTextureAfterAlpha);
  788. FreeAndNil(FAdaptedTexture);
  789. inherited Destroy;
  790. end;
  791. { TToolDeformationGrid }
  792. function TToolDeformationGrid.ToolDeformationGridNeeded: boolean;
  793. var xb,yb: integer;
  794. layer: TBGRABitmap;
  795. begin
  796. if (DeformationGrid = nil) then
  797. begin
  798. layer := GetToolDrawingLayer;
  799. if layer = nil then
  800. begin
  801. result := false;
  802. exit;
  803. end;
  804. deformationGridNbX:= Manager.DeformationGridNbX;
  805. deformationGridNbY:= Manager.DeformationGridNbY;
  806. SetLength(DeformationGrid, deformationGridNbY, deformationGridNbX);
  807. SetLength(DeformationGridTexCoord, deformationGridNbY, deformationGridNbX);
  808. for yb := 0 to deformationGridNbY-1 do
  809. for xb := 0 to deformationGridNbX-1 do
  810. begin
  811. DeformationGridTexCoord[yb,xb] := PointF(xb/(deformationGridNbX-1)*layer.Width-0.5,
  812. yb/(deformationGridNbY-1)*layer.Height-0.5);
  813. DeformationGrid[yb,xb] :=DeformationGridTexCoord[yb,xb];
  814. end;
  815. end;
  816. result := true;
  817. end;
  818. function TToolDeformationGrid.ToolCommand(ACommand: TToolCommand): boolean;
  819. begin
  820. case ACommand of
  821. tcFinish: if DoingDeformation then
  822. begin
  823. ValidateDeformationGrid;
  824. result := true;
  825. end
  826. else result := false;
  827. else result := false;
  828. end;
  829. end;
  830. function TToolDeformationGrid.ToolProvideCommand(ACommand: TToolCommand
  831. ): boolean;
  832. begin
  833. case ACommand of
  834. tcFinish: result := DoingDeformation;
  835. else result := false;
  836. end;
  837. end;
  838. destructor TToolDeformationGrid.Destroy;
  839. begin
  840. ValidateDeformationGrid;
  841. inherited Destroy;
  842. end;
  843. procedure TToolDeformationGrid.ReleaseGrid;
  844. var
  845. xb,yb: Integer;
  846. begin
  847. if DoingDeformation then
  848. begin
  849. ValidateAction;
  850. DoingDeformation := false;
  851. for yb := 0 to deformationGridNbY-2 do
  852. for xb := 0 to deformationGridNbX-2 do
  853. DeformationGridTexCoord[yb,xb] := DeformationGrid[yb,xb];
  854. end;
  855. end;
  856. procedure TToolDeformationGrid.ValidateDeformationGrid;
  857. begin
  858. if DoingDeformation then
  859. begin
  860. DeformationGrid := nil;
  861. DeformationGridTexCoord := nil;
  862. ValidateAction;
  863. DoingDeformation := false;
  864. end;
  865. end;
  866. function TToolDeformationGrid.GetPointAt(const ptF: TPointF; var x, y: integer): boolean;
  867. var
  868. yb, xb: Integer;
  869. curDist, minDist: single;
  870. begin
  871. result := false;
  872. minDist := sqr(SelectionMaxPointDistance);
  873. for yb := 1 to deformationGridNbY-2 do
  874. for xb := 1 to deformationGridNbX-2 do
  875. begin
  876. curDist := sqr(ptF.x-DeformationGrid[yb,xb].x) + sqr(ptF.y-DeformationGrid[yb,xb].y);
  877. if curDist < minDist then
  878. begin
  879. minDist := curDist;
  880. x := xb;
  881. y := yb;
  882. result := True;
  883. end;
  884. end;
  885. end;
  886. function TToolDeformationGrid.DoToolDown(toolDest: TBGRABitmap; pt: TPoint;
  887. ptF: TPointF; rightBtn: boolean): TRect;
  888. begin
  889. result := EmptyRect;
  890. deformationGridX := 1;
  891. deformationGridY := 1;
  892. if DeformationGrid <> nil then
  893. begin
  894. if GetPointAt(ptF, deformationGridX, deformationGridY) then
  895. begin
  896. deformationGridMoving := True;
  897. deformationOrigin := ptF;
  898. end;
  899. end;
  900. end;
  901. function TToolDeformationGrid.DoToolMove(toolDest: TBGRABitmap; pt: TPoint;
  902. ptF: TPointF): Trect;
  903. var xb,yb,NbX,NbY: integer;
  904. gridDone: array of array of boolean;
  905. layer,backupLayer : TBGRABitmap;
  906. PreviousClipRect: TRect;
  907. previousBounds: TRect;
  908. gridMinX,gridMinY,gridMaxX,gridMaxY, dummyX, dummyY: integer;
  909. procedure AddToDeformationArea(xi,yi: integer);
  910. var ptF: TPointF;
  911. pix: TRect;
  912. begin
  913. if (xi >= 0) and (yi >= 0) and (xi < NbX) and (yi < NbY) then
  914. begin
  915. ptF := deformationGrid[yi,xi];
  916. pix := rect(floor(ptF.X)-1,floor(ptF.Y)-1,ceil(ptF.X)+2,ceil(ptF.Y)+2);
  917. if IsRectEmpty(FCurrentBounds) then
  918. FCurrentBounds := pix
  919. else
  920. UnionRect(FCurrentBounds,FCurrentBounds,pix);
  921. end;
  922. end;
  923. begin
  924. result := EmptyRect;
  925. Manager.HintReturnValidates;
  926. if not deformationGridMoving then
  927. begin
  928. dummyX := 1;
  929. dummyY := 1;
  930. if GetPointAt(ptF, dummyX, dummyY) then
  931. Cursor := crHandPoint
  932. else Cursor := crDefault;
  933. exit;
  934. end;
  935. if Manager.DeformationGridMode = gmMovePointWithoutDeformation then
  936. begin
  937. ReleaseGrid;
  938. DeformationGrid[deformationGridY,deformationGridX] := PointF(
  939. DeformationGrid[deformationGridY,deformationGridX].X + ptF.X-deformationOrigin.X,
  940. DeformationGrid[deformationGridY,deformationGridX].Y + ptF.Y-deformationOrigin.Y);
  941. DeformationGridTexCoord[deformationGridY,deformationGridX] := DeformationGrid[deformationGridY,deformationGridX];
  942. result := OnlyRenderChange;
  943. end else
  944. begin
  945. if not DoingDeformation then
  946. begin
  947. FCurrentBounds := EmptyRect;
  948. DoingDeformation := True;
  949. end;
  950. layer := GetToolDrawingLayer;
  951. backupLayer := GetBackupLayerIfExists;
  952. NbX := deformationGridNbX;
  953. NbY := deformationGridNbY;
  954. DeformationGrid[deformationGridY,deformationGridX] := PointF(
  955. DeformationGrid[deformationGridY,deformationGridX].X + ptF.X-deformationOrigin.X,
  956. DeformationGrid[deformationGridY,deformationGridX].Y + ptF.Y-deformationOrigin.Y);
  957. previousBounds := FCurrentBounds;
  958. FCurrentBounds := EmptyRect;
  959. gridMinX := deformationGridX-1;
  960. if gridMinX < 0 then gridMinX := 0;
  961. gridMinY := deformationGridY-1;
  962. if gridMinY < 0 then gridMinY := 0;
  963. gridMaxX := deformationGridX+1;
  964. if gridMaxX > NbX-1 then gridMaxX := NbX-1;
  965. gridMaxY := deformationGridY+1;
  966. if gridMaxY > NbY-1 then gridMaxY := NbY-1;
  967. for yb := gridMinY to gridMaxY do
  968. for xb := gridMinX to gridMaxX do
  969. AddToDeformationArea(xb,yb);
  970. FMergedBounds := RectUnion(previousBounds,FCurrentBounds);
  971. gridMinX := 0;
  972. gridMinY := 0;
  973. gridMaxX := NbX-1;
  974. gridMaxY := NbY-1;
  975. //progressive drawing of deformation zones
  976. setlength(gridDone,NbY-1,NbX-1);
  977. for yb := gridMinY to gridMaxY-1 do
  978. for xb := gridMinX to gridMaxX-1 do
  979. gridDone[yb,xb] := false;
  980. if not IsRectEmpty(FMergedBounds) and (backupLayer <>nil) then
  981. begin
  982. PreviousClipRect := layer.ClipRect;
  983. layer.ClipRect := FMergedBounds;
  984. layer.FillRect(0,0,layer.Width,layer.Height,BGRAPixelTransparent,dmSet);
  985. //drawing zones that are not deformed
  986. for yb := gridMinY to gridMaxY-1 do
  987. for xb := gridMinX to gridMaxX-1 do
  988. if (DeformationGrid[yb,xb] = DeformationGridTexCoord[yb,xb]) and
  989. (DeformationGrid[yb,xb+1] = DeformationGridTexCoord[yb,xb+1]) and
  990. (DeformationGrid[yb+1,xb+1] = DeformationGridTexCoord[yb+1,xb+1]) and
  991. (DeformationGrid[yb+1,xb] = DeformationGridTexCoord[yb+1,xb]) then
  992. begin
  993. layer.FillPoly([DeformationGrid[yb,xb],DeformationGrid[yb,xb+1],
  994. DeformationGrid[yb+1,xb+1],DeformationGrid[yb+1,xb]],backupLayer,dmDrawWithTransparency);
  995. gridDone[yb,xb] := true;
  996. end;
  997. //drawing zones that are concave
  998. for yb := gridMinY to gridMaxY-1 do
  999. for xb := gridMinX to gridMaxX-1 do
  1000. if not gridDone[yb,xb] and
  1001. not IsConvex([DeformationGrid[yb,xb],DeformationGrid[yb,xb+1],
  1002. DeformationGrid[yb+1,xb+1],DeformationGrid[yb+1,xb]]) then
  1003. begin
  1004. layer.FillQuadLinearMapping(DeformationGrid[yb,xb],DeformationGrid[yb,xb+1],
  1005. DeformationGrid[yb+1,xb+1],DeformationGrid[yb+1,xb],backupLayer,
  1006. DeformationGridTexCoord[yb,xb],DeformationGridTexCoord[yb,xb+1],DeformationGridTexCoord[yb+1,xb+1],
  1007. DeformationGridTexCoord[yb+1,xb],true, fcKeepCW);
  1008. gridDone[yb,xb] := true;
  1009. end;
  1010. //drawing convex zones
  1011. for yb := gridMinY to gridMaxY-1 do
  1012. for xb := gridMinX to gridMaxX-1 do
  1013. if not gridDone[yb,xb] and IsClockwise([DeformationGrid[yb,xb],DeformationGrid[yb,xb+1],
  1014. DeformationGrid[yb+1,xb+1],DeformationGrid[yb+1,xb]]) then
  1015. layer.FillQuadLinearMapping(DeformationGrid[yb,xb],DeformationGrid[yb,xb+1],
  1016. DeformationGrid[yb+1,xb+1],DeformationGrid[yb+1,xb],backupLayer,
  1017. DeformationGridTexCoord[yb,xb],DeformationGridTexCoord[yb,xb+1],DeformationGridTexCoord[yb+1,xb+1],
  1018. DeformationGridTexCoord[yb+1,xb],true);
  1019. layer.ClipRect := PreviousClipRect;
  1020. end;
  1021. result := FMergedBounds;
  1022. end;
  1023. deformationOrigin := ptF;
  1024. end;
  1025. function TToolDeformationGrid.GetIsSelectingTool: boolean;
  1026. begin
  1027. Result:= false;
  1028. end;
  1029. function TToolDeformationGrid.DoToolUpdate(toolDest: TBGRABitmap): TRect;
  1030. begin
  1031. if (deformationGridNbX <> Manager.DeformationGridNbX) or
  1032. (deformationGridNbY <> Manager.DeformationGridNbY) then
  1033. begin
  1034. ReleaseGrid;
  1035. DeformationGrid := nil;
  1036. DeformationGridTexCoord := nil;
  1037. Result:= OnlyRenderChange;
  1038. end
  1039. else
  1040. result := EmptyRect;
  1041. end;
  1042. function TToolDeformationGrid.Render(VirtualScreen: TBGRABitmap;
  1043. VirtualScreenWidth, VirtualScreenHeight: integer; BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
  1044. var curPt,rightPt,downPt: TPointF;
  1045. xb,yb: Integer;
  1046. begin
  1047. result := EmptyRect;
  1048. if not ToolDeformationGridNeeded then exit;
  1049. for xb := 0 to deformationGridNbX-1 do
  1050. for yb := 0 to deformationGridNbY-1 do
  1051. begin
  1052. curPt := BitmapToVirtualScreen(DeformationGrid[yb,xb]);
  1053. if not deformationGridMoving or ((xb+1 >= deformationGridX) and (xb <= deformationGridX) and
  1054. (yb >= deformationGridY-1) and (yb <= deformationGridY+1)) then
  1055. begin
  1056. if (xb < deformationGridNbX-1) and (yb > 0) and (yb < deformationGridNbY-1) then
  1057. begin
  1058. rightPt := BitmapToVirtualScreen(DeformationGrid[yb,xb+1]);
  1059. if Assigned(VirtualScreen) then NiceLine(VirtualScreen, curPt.X,curPt.Y, rightPt.X,rightPt.Y);
  1060. result := RectUnion(result,rect(floor(curPt.x)-1,floor(curPt.y)-1,
  1061. ceil(curPt.x)+2,ceil(curPt.y)+2));
  1062. result := RectUnion(result,rect(floor(rightPt.x)-1,floor(rightPt.y)-1,
  1063. ceil(rightPt.x)+2,ceil(rightPt.y)+2));
  1064. end;
  1065. end;
  1066. if not deformationGridMoving or ((xb >= deformationGridX-1) and (xb <= deformationGridX+1) and
  1067. (yb+1 >= deformationGridY) and (yb <= deformationGridY)) then
  1068. begin
  1069. if (yb < deformationGridNbY-1) and (xb > 0) and (xb < deformationGridNbX-1) then
  1070. begin
  1071. downPt := BitmapToVirtualScreen(DeformationGrid[yb+1,xb]);
  1072. if Assigned(virtualScreen) then NiceLine(VirtualScreen, curPt.X,curPt.Y, downPt.X,downPt.Y);
  1073. result := RectUnion(result,rect(floor(curPt.x)-1,floor(curPt.y)-1,
  1074. ceil(curPt.x)+2,ceil(curPt.y)+2));
  1075. result := RectUnion(result,rect(floor(downPt.x)-1,floor(downPt.y)-1,
  1076. ceil(downPt.x)+2,ceil(downPt.y)+2));
  1077. end;
  1078. end;
  1079. end;
  1080. for xb := 1 to deformationGridNbX-2 do
  1081. for yb := 1 to deformationGridNbY-2 do
  1082. begin
  1083. if not deformationGridMoving or ((xb >= deformationGridX-1) and (xb <= deformationGridX+1) and
  1084. (yb >= deformationGridY-1) and (yb <= deformationGridY+1)) then
  1085. begin
  1086. curPt := BitmapToVirtualScreen(DeformationGrid[yb,xb]);
  1087. result := RectUnion(result,NicePoint(VirtualScreen, curPt.X,curPt.Y));
  1088. end;
  1089. end;
  1090. end;
  1091. function TToolDeformationGrid.DoToolKeyDown(var key: Word): TRect;
  1092. begin
  1093. result := EmptyRect;
  1094. if Key = VK_RETURN then
  1095. begin
  1096. if DoingDeformation then
  1097. begin
  1098. ValidateDeformationGrid;
  1099. result := EmptyRect;
  1100. manager.QueryExitTool;
  1101. Key := 0;
  1102. end;
  1103. end else
  1104. if Key = VK_ESCAPE then
  1105. begin
  1106. if DoingDeformation then
  1107. begin
  1108. CancelActionPartially;
  1109. result := OnlyRenderChange;
  1110. manager.QueryExitTool;
  1111. Key := 0;
  1112. end;
  1113. end;
  1114. end;
  1115. function TToolDeformationGrid.ToolUp: TRect;
  1116. begin
  1117. if deformationGridMoving then
  1118. result := OnlyRenderChange
  1119. else
  1120. Result:=EmptyRect;
  1121. deformationGridMoving := false;
  1122. end;
  1123. function TToolDeformationGrid.GetContextualToolbars: TContextualToolbars;
  1124. begin
  1125. Result:= [ctDeformation];
  1126. end;
  1127. initialization
  1128. RegisterTool(ptDeformation, TToolDeformationGrid);
  1129. RegisterTool(ptTextureMapping, TToolTextureMapping);
  1130. RegisterTool(ptLayerMapping, TToolLayerMapping);
  1131. end.