ulayerstackinterface.pas 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit ULayerStackInterface;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, Controls, ComCtrls, ExtCtrls, BGRAVirtualScreen, BCComboBox,
  7. BGRABitmap, BGRABitmapTypes, BGRAGraphics, Menus,
  8. LazPaintType, UDarkTheme, UVolatileScrollBar, UImageObservation;
  9. type
  10. TDrawLayerItemResult = record
  11. PreviewPts: array of TPointF;
  12. NameRect,OpacityBar: TRect;
  13. end;
  14. TLayerItemInfo = record
  15. FullRect: TRect;
  16. RightPart: TDrawLayerItemResult;
  17. VisibleCheckbox: TRect;
  18. KindIcon: TRect;
  19. KindIconHint: string;
  20. end;
  21. { TLayerStackInterface }
  22. TLayerStackInterface = class
  23. private
  24. LazPaintInstance: TLazPaintCustomInstance;
  25. Container: TWinControl;
  26. BGRALayerStack: TBGRAVirtualScreen;
  27. TimerScroll, TimerQuery: TTimer;
  28. PanelToolbar: TPanel;
  29. Toolbar: TToolbar;
  30. ComboBox_BlendOp: TBCComboBox;
  31. VolatileHorzScrollBar, VolatileVertScrollBar: TVolatileScrollBar;
  32. procedure BGRALayerStack_MouseDown(Sender: TObject; Button: TMouseButton;
  33. {%H-}Shift: TShiftState; X, Y: Integer);
  34. procedure BGRALayerStack_MouseMove(Sender: TObject; {%H-}Shift: TShiftState; X,
  35. Y: Integer);
  36. procedure BGRALayerStack_MouseUp(Sender: TObject; Button: TMouseButton;
  37. {%H-}Shift: TShiftState; X, Y: Integer);
  38. procedure BGRALayerStack_MouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
  39. WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
  40. procedure BGRALayerStack_Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  41. procedure ComboBox_BlendOpChange(Sender: TObject);
  42. procedure TimerQuery_Timer(Sender: TObject);
  43. procedure TimerScroll_Timer(Sender: TObject);
  44. procedure Toolbar_Resize(Sender: TObject);
  45. procedure ToolSelectBlendOperation_Click(Sender: TObject);
  46. procedure ToolZoomLayerStackIn_Click(Sender: TObject);
  47. procedure ToolZoomLayerStackOut_Click(Sender: TObject);
  48. procedure LazPaint_ImageChanged(AEvent: TLazPaintImageObservationEvent);
  49. protected
  50. FDPI: integer;
  51. FScaling: Double;
  52. FDarkTheme: boolean;
  53. FScrollStackItemIntoView: Boolean;
  54. FUpdatingComboBlendOp, FQuerySelectBlendOp: Boolean;
  55. FPartialRedraw: Boolean;
  56. FRenaming: Boolean;
  57. FDontUpdateStack: Boolean;
  58. FZoomFactor: single;
  59. FRegularBackground, FIconBackground: TBGRABitmap;
  60. FLayerRectWidth, FLayerRectHeight, FStackWidth, FStackHeight: integer;
  61. FOffset, FScrollPos, FMaxScrollPos: TPoint;
  62. FAvailableWidth, FAvailableHeight: integer;
  63. FChangingLayerOpacityIndex, FAskTransferSelectionLayerIndex: integer;
  64. FScrollButtonRect: TRect;
  65. FInterruptorWidth,FInterruptorHeight: integer;
  66. FLayerInfo: array of TLayerItemInfo;
  67. FMovingItemStart: boolean;
  68. FMovingItemBitmap: TBGRABitmap;
  69. FMovingItemSourceIndex: integer;
  70. FMovingItemMousePos, FMovingItemMouseOrigin, FMovingItemOrigin: TPoint;
  71. FRightClickOrigin: TPoint;
  72. FTimerScrollDeltaY: integer;
  73. FInHandleSelectLayer: Boolean;
  74. FLayerMenu: TPopupMenu;
  75. FQueryLayerMenu: boolean;
  76. FLayerMenuCoord: TPoint;
  77. procedure ApplyThemeAndDPI;
  78. procedure SetDPI(AValue: integer);
  79. procedure SetDarkTheme(AValue: boolean);
  80. function GetBackColor(ASelected: boolean): TColor;
  81. function GetTextColor(ASelected: boolean): TColor;
  82. procedure UpdateComboBlendOp;
  83. procedure SelectBlendOp;
  84. procedure QuerySelectBlendOp;
  85. procedure SetZoomFactor(AValue: single);
  86. function DrawLayerItem(ABitmap: TBGRABitmap; ALayerPos: TPoint; ALayerIndex: integer; ASelected: boolean): TDrawLayerItemResult;
  87. procedure DrawLayerStack(ABitmap: TBGRABitmap; ALayout: boolean; AUpdateItem: Integer);
  88. procedure ComputeLayout(ABitmap: TBGRABitmap);
  89. procedure ComputeScrolling(AWithHorzScrollBar,AWithVertScrollBar: boolean);
  90. procedure DoScrollVertically(AAmount: integer);
  91. function HandleSelectLayer(i,x,y: integer; AStartMoving: boolean = true): boolean;
  92. procedure HandleChangeLayerOpacity(X,{%H-}Y: integer);
  93. procedure UpdateLayerStackItem(AIndex: integer);
  94. procedure NeedCheckers;
  95. public
  96. constructor Create(AContainer: TWinControl; AInstance: TLazPaintCustomInstance);
  97. destructor Destroy; override;
  98. procedure AddButton(AAction: TBasicAction);
  99. procedure AddButton(ACaption: string; AImageIndex: integer; AOnClick: TNotifyEvent);
  100. procedure AddLayerMenu(AAction: TBasicAction);
  101. procedure ScrollToItem(AIndex: integer; AUpdateStack: boolean = true);
  102. procedure InvalidateStack(AScrollIntoView: boolean);
  103. function GetWidthFor(AButtonCount: integer): integer;
  104. property DPI: integer read FDPI write SetDPI;
  105. property DarkTheme: boolean read FDarkTheme write SetDarkTheme;
  106. property ZoomFactor: single read FZoomFactor write SetZoomFactor;
  107. end;
  108. implementation
  109. uses Dialogs, LCScaleDPI, GraphType, Graphics, Toolwin, Forms,
  110. BGRAFillInfo, BGRATransform, BGRALayerOriginal, BGRASVGOriginal, BGRAThumbnail,
  111. UTool, UImage, UBlendOp, UResourceStrings, math;
  112. { TLayerStackInterface }
  113. procedure TLayerStackInterface.SetDarkTheme(AValue: boolean);
  114. begin
  115. if FDarkTheme=AValue then Exit;
  116. FDarkTheme:=AValue;
  117. ApplyThemeAndDPI;
  118. end;
  119. procedure TLayerStackInterface.ToolSelectBlendOperation_Click(Sender: TObject);
  120. begin
  121. SelectBlendOp;
  122. end;
  123. procedure TLayerStackInterface.ComboBox_BlendOpChange(Sender: TObject);
  124. var blendOp: TBlendOperation;
  125. itemStr: string;
  126. begin
  127. if Assigned(LazPaintInstance) then
  128. LazPaintInstance.ExitColorEditor;
  129. if not FUpdatingComboBlendOp then
  130. begin
  131. if ComboBox_BlendOp.ItemIndex <> -1 then
  132. begin
  133. itemStr := ComboBox_BlendOp.Items[ComboBox_BlendOp.ItemIndex];
  134. if itemStr <> rsOtherBlendOp then
  135. begin
  136. if itemStr = rsNormalBlendOp then
  137. blendOp := boTransparent
  138. else
  139. blendOp := StrToBlendOperation(itemStr);
  140. FDontUpdateStack := true;
  141. LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.CurrentLayerIndex] := blendOp;
  142. if LazPaintInstance.Image.CurrentLayerIndex = 0 then
  143. LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
  144. FDontUpdateStack := false;
  145. FQuerySelectBlendOp:= false;
  146. end else
  147. QuerySelectBlendOp;
  148. end;
  149. end;
  150. end;
  151. procedure TLayerStackInterface.TimerQuery_Timer(Sender: TObject);
  152. begin
  153. if FQuerySelectBlendOp then
  154. begin
  155. FQuerySelectBlendOp := false;
  156. SelectBlendOp;
  157. end else
  158. if FQueryLayerMenu then
  159. begin
  160. FQueryLayerMenu := false;
  161. with FLayerMenuCoord do
  162. FLayerMenu.PopUp(X, Y);
  163. end
  164. else
  165. TimerQuery.Enabled:= false;
  166. end;
  167. procedure TLayerStackInterface.TimerScroll_Timer(Sender: TObject);
  168. begin
  169. TimerScroll.Enabled := False;
  170. DoScrollVertically(FTimerScrollDeltaY);
  171. end;
  172. procedure TLayerStackInterface.Toolbar_Resize(Sender: TObject);
  173. begin
  174. PanelToolbar.ClientHeight := Toolbar.Height + PanelToolbar.ChildSizing.TopBottomSpacing*2;
  175. end;
  176. procedure TLayerStackInterface.BGRALayerStack_Redraw(Sender: TObject;
  177. Bitmap: TBGRABitmap);
  178. begin
  179. TVolatileScrollBar.InitDPI((Sender as TControl).GetCanvasScaleFactor);
  180. DrawLayerStack(Bitmap, not FPartialRedraw, -1);
  181. end;
  182. procedure TLayerStackInterface.BGRALayerStack_MouseDown(Sender: TObject;
  183. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  184. var i: integer;
  185. str: string;
  186. isRightClick: boolean;
  187. begin
  188. if Assigned(LazPaintInstance) then LazPaintInstance.ExitColorEditor;
  189. X := round(X*FScaling);
  190. Y := round(Y*FScaling);
  191. if PtInRect(Point(X,Y),FScrollButtonRect) then exit;
  192. isRightClick := (Button = mbRight) {$IFDEF DARWIN}or ((Button = mbLeft) and (ssCtrl in Shift)){$ENDIF};
  193. If (Button = mbLeft) and not isRightClick then
  194. begin
  195. FRightClickOrigin := EmptyPoint;
  196. if ((VolatileHorzScrollBar <> nil) and VolatileHorzScrollBar.MouseDown(X,Y)) or
  197. ((VolatileVertScrollBar <> nil) and VolatileVertScrollBar.MouseDown(X,Y)) then
  198. begin
  199. if VolatileHorzScrollBar <> nil then FScrollPos.X := VolatileHorzScrollBar.Position;
  200. if VolatileVertScrollBar <> nil then FScrollPos.Y := VolatileVertScrollBar.Position;
  201. BGRALayerStack.RedrawBitmap;
  202. exit;
  203. end;
  204. for i := 0 to high(FLayerInfo) do
  205. if PtInRect(Point(x,Y),FLayerInfo[i].VisibleCheckbox) then
  206. begin
  207. if i < LazPaintInstance.Image.NbLayers then
  208. begin
  209. FDontUpdateStack:= true;
  210. LazPaintInstance.Image.LayerVisible[i] := not LazPaintInstance.Image.LayerVisible[i];
  211. FDontUpdateStack:= false;
  212. UpdateLayerStackItem(i);
  213. end;
  214. exit;
  215. end;
  216. for i := 0 to high(FLayerInfo) do
  217. if IsPointInPolygon(FLayerInfo[i].RightPart.PreviewPts,pointF(x,y),true) then
  218. begin
  219. HandleSelectLayer(i,x,y);
  220. exit;
  221. end;
  222. for i := 0 to high(FLayerInfo) do
  223. if PtInRect(Point(x,Y),FLayerInfo[i].RightPart.NameRect) then
  224. begin
  225. if i < LazPaintInstance.Image.NbLayers then
  226. begin
  227. if (i <> LazPaintInstance.image.CurrentLayerIndex) and not FRenaming then
  228. HandleSelectLayer(i,x,y)
  229. else
  230. begin
  231. FRenaming := true;
  232. str := InputBox(rsLayers, rsEnterLayerName, LazPaintInstance.Image.LayerName[i]);
  233. if str <> '' then
  234. begin
  235. if length(str) > MaxLayerNameLength then str := copy(str,1,MaxLayerNameLength);
  236. LazPaintInstance.Image.LayerName[i] := str;
  237. end;
  238. BGRALayerStack.RedrawBitmap; //layer stack width may change
  239. end;
  240. end;
  241. exit;
  242. end;
  243. for i := 0 to high(FLayerInfo) do
  244. if PtInRect(Point(x,Y),FLayerInfo[i].RightPart.OpacityBar) or PtInRect(Point(x+4,Y),FLayerInfo[i].RightPart.OpacityBar) or
  245. PtInRect(Point(x-4,Y),FLayerInfo[i].RightPart.OpacityBar) then
  246. begin
  247. if i < LazPaintInstance.Image.NbLayers then
  248. begin
  249. FChangingLayerOpacityIndex := i;
  250. HandleChangeLayerOpacity(X,Y);
  251. end;
  252. exit;
  253. end;
  254. end else
  255. if isRightClick then
  256. FRightClickOrigin := Point(X,Y);
  257. end;
  258. procedure TLayerStackInterface.BGRALayerStack_MouseMove(Sender: TObject;
  259. Shift: TShiftState; X, Y: Integer);
  260. var
  261. i: Integer;
  262. begin
  263. X := round(X*FScaling);
  264. Y := round(Y*FScaling);
  265. if FMovingItemBitmap <> nil then
  266. begin
  267. FMovingItemMousePos := point(X,Y);
  268. BGRALayerStack.DiscardBitmap;
  269. exit;
  270. end;
  271. if ((VolatileVertScrollBar <> nil) and VolatileVertScrollBar.MouseMove(X,Y)) or
  272. ((VolatileHorzScrollBar <> nil) and VolatileHorzScrollBar.MouseMove(X,Y)) then
  273. begin
  274. if VolatileHorzScrollBar <> nil then FScrollPos.X := VolatileHorzScrollBar.Position;
  275. if VolatileVertScrollBar <> nil then FScrollPos.Y := VolatileVertScrollBar.Position;
  276. BGRALayerStack.DiscardBitmap;
  277. exit;
  278. end;
  279. if FChangingLayerOpacityIndex <> -1 then
  280. begin
  281. HandleChangeLayerOpacity(X,Y);
  282. exit;
  283. end;
  284. for i := 0 to high(FLayerInfo) do
  285. if FLayerInfo[i].KindIcon.Contains(Point(X,Y)) then
  286. begin
  287. BGRALayerStack.Hint := FLayerInfo[i].KindIconHint;
  288. BGRALayerStack.ShowHint:= true;
  289. exit;
  290. end else
  291. if FLayerInfo[i].VisibleCheckbox.Contains(Point(X,Y)) then
  292. begin
  293. BGRALayerStack.Hint := rsVisible;
  294. BGRALayerStack.ShowHint:= true;
  295. exit;
  296. end else
  297. if FLayerInfo[i].RightPart.OpacityBar.Contains(Point(X,Y)) then
  298. begin
  299. BGRALayerStack.Hint := rsOpacity;
  300. BGRALayerStack.ShowHint:= true;
  301. exit;
  302. end;
  303. BGRALayerStack.ShowHint:= false;
  304. end;
  305. procedure TLayerStackInterface.BGRALayerStack_MouseUp(Sender: TObject;
  306. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  307. var destinationIndex, prevIndex, i: integer;
  308. indexF: single;
  309. res: TModalResult;
  310. topmostInfo: TTopMostInfo;
  311. isRightClick: Boolean;
  312. begin
  313. X := round(X*FScaling);
  314. Y := round(Y*FScaling);
  315. isRightClick := (Button = mbRight) {$IFDEF DARWIN}or ((Button = mbLeft) and not IsEmptyPoint(FRightClickOrigin)){$ENDIF};
  316. if (Button = mbLeft) and not isRightClick then
  317. begin
  318. FMovingItemStart := false;
  319. if FMovingItemBitmap <> nil then
  320. begin
  321. FreeAndNil(FMovingItemBitmap);
  322. indexF := LazPaintInstance.Image.NbLayers-1 - (FMovingItemOrigin.Y+FMovingItemMousePos.Y-FMovingItemMouseOrigin.Y)/FLayerRectHeight;
  323. if indexF < FMovingItemSourceIndex-1.15 then indexF += 0.15 else
  324. if indexF > FMovingItemSourceIndex+1.15 then indexF -= 0.15;
  325. destinationIndex := Int32or64(round(indexF));
  326. if destinationIndex = -1 then destinationIndex := 0;
  327. if destinationIndex = LazPaintInstance.Image.NbLayers then destinationIndex := LazPaintInstance.Image.NbLayers-1;
  328. if (destinationIndex >= 0) and (destinationIndex < LazPaintInstance.Image.NbLayers) and (destinationIndex <> FMovingItemSourceIndex) then
  329. begin
  330. FDontUpdateStack:= true;
  331. LazPaintInstance.Image.MoveLayer(FMovingItemSourceIndex, destinationIndex);
  332. FDontUpdateStack:= false;
  333. end;
  334. BGRALayerStack.RedrawBitmap;
  335. end;
  336. if ((VolatileVertScrollBar <> nil) and VolatileVertScrollBar.MouseUp(X,Y)) or
  337. ((VolatileHorzScrollBar <> nil) and VolatileHorzScrollBar.MouseUp(X,Y)) then
  338. begin
  339. BGRALayerStack.RedrawBitmap;
  340. exit;
  341. end;
  342. if FChangingLayerOpacityIndex <> -1 then FChangingLayerOpacityIndex := -1;
  343. if FAskTransferSelectionLayerIndex <> -1 then
  344. begin
  345. FInHandleSelectLayer := true;
  346. topmostInfo := LazPaintInstance.HideTopmost;
  347. res := MessageDlg(rsTransferSelectionToOtherLayer,mtConfirmation,[mbOk,mbCancel],0);
  348. LazPaintInstance.ShowTopmost(topmostInfo);
  349. if res = mrOk then
  350. begin
  351. prevIndex := LazPaintInstance.Image.CurrentLayerIndex;
  352. if LazPaintInstance.Image.SetCurrentLayerByIndex(FAskTransferSelectionLayerIndex) then
  353. begin
  354. FRenaming := false;
  355. UpdateLayerStackItem(prevIndex);
  356. UpdateLayerStackItem(FAskTransferSelectionLayerIndex);
  357. end;
  358. end;
  359. FInHandleSelectLayer := false;
  360. FAskTransferSelectionLayerIndex := -1;
  361. end;
  362. end else
  363. if isRightClick and (Abs(X - FRightClickOrigin.X) <= 2) and
  364. (Abs(Y - FRightClickOrigin.Y) <= 2) then
  365. begin
  366. for i := 0 to high(FLayerInfo) do
  367. if IsPointInPolygon(FLayerInfo[i].RightPart.PreviewPts,pointF(FRightClickOrigin.x,FRightClickOrigin.y),true) then
  368. begin
  369. if HandleSelectLayer(i,x,y,false) then
  370. begin
  371. FQueryLayerMenu := true;
  372. FLayerMenuCoord := BGRALayerStack.ClientToScreen(Point(
  373. round(FRightClickOrigin.X / FScaling),
  374. round(FRightClickOrigin.Y / FScaling)));
  375. TimerQuery.Enabled:= true;
  376. end;
  377. exit;
  378. end;
  379. end;
  380. end;
  381. procedure TLayerStackInterface.BGRALayerStack_MouseWheel(Sender: TObject;
  382. Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  383. var Handled: Boolean);
  384. begin
  385. DoScrollVertically(round(-WheelDelta*ZoomFactor*50/120*FScaling));
  386. Handled := true;
  387. end;
  388. procedure TLayerStackInterface.SetZoomFactor(AValue: single);
  389. var
  390. prevZoom: Single;
  391. begin
  392. if AValue > 10 then AValue := 10;
  393. if AValue < 1/2 then AValue := 1/2;
  394. if FZoomFactor=AValue then Exit;
  395. prevZoom := FZoomFactor;
  396. FZoomFactor := AValue;
  397. if prevZoom > 0 then
  398. FScrollPos.Y := round(FScrollPos.Y * FZoomFactor / prevZoom);
  399. BGRALayerStack.DiscardBitmap;
  400. end;
  401. function TLayerStackInterface.DrawLayerItem(ABitmap: TBGRABitmap;
  402. ALayerPos: TPoint; ALayerIndex: integer; ASelected: boolean): TDrawLayerItemResult;
  403. var
  404. lColor: TBGRAPixel;
  405. barwidth: integer;
  406. sourceCoords: Array Of TPointF;
  407. reduced, bmpOpacity: TBGRABitmap;
  408. reducedBounds, rOpacity, rFill: TRect;
  409. percentStr: String;
  410. begin
  411. lColor := GetTextColor(ASelected);
  412. result.PreviewPts := PointsF([pointf(ALayerPos.X+0.25*FLayerRectWidth,ALayerPos.Y+round(FLayerRectHeight*0.1)),
  413. pointf(ALayerPos.X+0.9*FLayerRectWidth,ALayerPos.Y+round(FLayerRectHeight*0.1)),
  414. pointf(ALayerPos.X+0.7*FLayerRectWidth,ALayerPos.Y+round(FLayerRectHeight*0.9)),
  415. pointf(ALayerPos.X+0.05*FLayerRectWidth,ALayerPos.Y+round(FLayerRectHeight*0.9))]);
  416. reduced := TBGRABitmap.Create(round(FLayerRectWidth*0.65), round(FLayerRectHeight*0.8));
  417. reducedBounds := RectWithSize(LazPaintInstance.Image.LayerOffset[ALayerIndex].X,
  418. LazPaintInstance.Image.LayerOffset[ALayerIndex].Y,
  419. LazPaintInstance.Image.LayerBitmap[ALayerIndex].Width,
  420. LazPaintInstance.Image.LayerBitmap[ALayerIndex].Height);
  421. if LazPaintInstance.Image.Width <> 0 then
  422. begin
  423. reducedBounds.Left := round(reducedBounds.Left*reduced.Width/LazPaintInstance.Image.Width);
  424. reducedBounds.Right := round(reducedBounds.Right*reduced.Width/LazPaintInstance.Image.Width);
  425. end;
  426. if LazPaintInstance.Image.Height <> 0 then
  427. begin
  428. reducedBounds.Top := round(reducedBounds.Top*reduced.Height/LazPaintInstance.Image.Height);
  429. reducedBounds.Bottom := round(reducedBounds.Bottom*reduced.Height/LazPaintInstance.Image.Height);
  430. end;
  431. reduced.StretchPutImage(reducedBounds, LazPaintInstance.Image.LayerBitmap[ALayerIndex], dmDrawWithTransparency);
  432. result.PreviewPts[0].y += 0.5;
  433. result.PreviewPts[1].y += 0.5;
  434. result.PreviewPts[2].y -= 0.5;
  435. result.PreviewPts[3].y -= 0.5;
  436. NeedCheckers;
  437. if LazPaintInstance.Image.IsIconCursor then
  438. ABitmap.FillPolyAntialias(result.PreviewPts, FIconBackground)
  439. else
  440. ABitmap.FillPolyAntialias(result.PreviewPts, FRegularBackground);
  441. sourceCoords := PointsF([pointf(-0.49,-0.49),pointf(reduced.Width-0.51,-0.49),
  442. pointf(reduced.Width-0.51,reduced.Height-0.51),pointf(-0.49,reduced.Height-0.51)]);
  443. ABitmap.FillPolyLinearMapping(result.PreviewPts, reduced, sourceCoords, False);
  444. reduced.Free;
  445. result.PreviewPts[0].y -= 0.5;
  446. result.PreviewPts[1].y -= 0.5;
  447. result.PreviewPts[2].y += 0.5;
  448. result.PreviewPts[3].y += 0.5;
  449. if not LazPaintInstance.Image.LayerVisible[ALayerIndex] then ABitmap.CustomPenStyle := BGRAPenStyle(FLayerRectHeight/8, FLayerRectHeight/8);
  450. ABitmap.DrawPolygonAntialias( result.PreviewPts, lColor,1);
  451. ABitmap.PenStyle := psSolid;
  452. if ASelected then
  453. begin
  454. result.NameRect := rect(ALayerPos.X+round(FLayerRectWidth*0.95), ALayerPos.Y,
  455. ALayerPos.X+FStackWidth, ALayerPos.Y + FLayerRectHeight div 2);
  456. barwidth := FStackWidth - FInterruptorWidth - round(FLayerRectWidth*0.92)- ABitmap.FontFullHeight div 2;
  457. result.OpacityBar := rect(ALayerPos.X + round(FLayerRectWidth*0.92), ALayerPos.Y + FLayerRectHeight div 2,
  458. ALayerPos.X + round(FLayerRectWidth*0.92)+barwidth, ALayerPos.Y+FLayerRectHeight);
  459. rOpacity := rect(result.OpacityBar.left,(result.OpacityBar.top*7+result.OpacityBar.bottom) div 8,
  460. result.OpacityBar.right,(result.OpacityBar.top+result.OpacityBar.bottom*7) div 8);
  461. ABitmap.Rectangle(rOpacity,lColor,dmSet);
  462. rOpacity.Inflate(-1,-1);
  463. bmpOpacity := TBGRABitmap.Create(rOpacity.Width,rOpacity.Height,BGRABlack);
  464. rFill := rect(0,0,round(bmpOpacity.Width*LazPaintInstance.Image.LayerOpacity[ALayerIndex]/255),bmpOpacity.Height);
  465. bmpOpacity.ClipRect := rFill;
  466. bmpOpacity.FillRect(rFill, CSSSilver,dmSet);
  467. bmpOpacity.FontFullHeight := min(bmpOpacity.Height, ABitmap.FontFullHeight*2);
  468. bmpOpacity.FontName := ABitmap.FontName;
  469. bmpOpacity.FontStyle:= [fsBold];
  470. bmpOpacity.FontVerticalAnchor:= fvaCenter;
  471. percentStr := IntToStr(round(LazPaintInstance.Image.LayerOpacity[ALayerIndex]*100/255))+'%';
  472. bmpOpacity.TextOut(bmpOpacity.Width/2,bmpOpacity.Height/2, percentStr, BGRABlack, taCenter);
  473. bmpOpacity.ClipRect := rect(rFill.Right,rFill.Top,bmpOpacity.Width,rFill.Bottom);
  474. bmpOpacity.TextOut(bmpOpacity.Width/2,bmpOpacity.Height/2, percentStr, BGRAWhite, taCenter);
  475. ABitmap.FillMask(rOpacity.Left,rOpacity.Top,bmpOpacity, lColor, dmDrawWithTransparency);
  476. bmpOpacity.Free;
  477. end
  478. else
  479. begin
  480. result.NameRect := rect(ALayerPos.X + round(FLayerRectWidth*0.95), ALayerPos.Y,
  481. ALayerPos.X + FStackWidth, ALayerPos.Y + FLayerRectHeight);
  482. result.OpacityBar := EmptyRect;
  483. end;
  484. {$IFDEF DARWIN}
  485. ABitmap.FontQuality := fqFineAntialiasing;
  486. {$ENDIF}
  487. ABitmap.TextOut(result.NameRect.Left,result.NameRect.Top+(result.NameRect.bottom-result.NameRect.top -ABitmap.FontFullHeight) div 2,
  488. LazPaintInstance.Image.LayerName[ALayerIndex],lColor);
  489. end;
  490. procedure TLayerStackInterface.DrawLayerStack(ABitmap: TBGRABitmap;
  491. ALayout: boolean; AUpdateItem: Integer);
  492. var i: integer;
  493. layerPos: TPoint;
  494. lSelected: boolean;
  495. y: integer;
  496. clipping, prevClip: TRect;
  497. lColor, lColorTrans: TBGRAPixel;
  498. penWidth: single;
  499. procedure DrawKindUnknown(rKind: TRect; out HintText: string);
  500. var
  501. eb: TEasyBezierCurve;
  502. w: single;
  503. i: integer;
  504. m: TAffineMatrix;
  505. begin
  506. eb := EasyBezierCurve([PointF(0.25,0.25),PointF(0.32,0.07),PointF(0.5,0),PointF(0.68,0.07),PointF(0.75,0.20),
  507. PointF(0.75,0.30),PointF(0.70,0.40),PointF(0.5,0.5),PointF(0.5,0.70)],False,cmCurve);
  508. m := AffineMatrixTranslation(rKind.Left,rKind.Top)*AffineMatrixScale(rKind.Width,rKind.Height);
  509. for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
  510. w := max(1,rKind.Height/10);
  511. ABitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
  512. ABitmap.FillEllipseAntialias((rKind.Left+rKind.Right)/2, rKind.Bottom - 1 - (w-1)/2, w*0.6,w*0.6, lColor);
  513. HintText := rsUnknownOriginal;
  514. end;
  515. procedure DrawKind(AClass: TBGRALayerOriginalAny; rKind: TRect; out HintText: string);
  516. var
  517. eb: TEasyBezierCurve;
  518. w: single;
  519. i: integer;
  520. m: TAffineMatrix;
  521. r: TRect;
  522. begin
  523. if AClass = nil then
  524. begin
  525. ABitmap.Rectangle(rKind, lColor,lColorTrans, dmDrawWithTransparency);
  526. ABitmap.HorizLine(rKind.Left+1,rKind.Top+(rKind.Height-1) div 2,rKind.Right-2, lColor, dmDrawWithTransparency);
  527. ABitmap.VertLine(rKind.Left+(rKind.Width-1) div 2,rKind.Top+1,rKind.Bottom-2, lColor, dmDrawWithTransparency);
  528. HintText := rsRasterLayer;
  529. end else
  530. if AClass = TBGRALayerImageOriginal then
  531. begin
  532. r := rect(rKind.Left,(rKind.Top+rKind.Bottom) div 2, (rKind.Left+rKind.Right) div 2, rKind.Bottom);
  533. w := max(1,rKind.Height/10);
  534. ABitmap.Rectangle(r, lColor,lColorTrans, dmDrawWithTransparency);
  535. eb := EasyBezierCurve([PointF(rKind.Left,rKind.Top+rKind.Height/4),
  536. PointF(rKind.Left+rKind.Width/2,rKind.Top+rKind.Height/4),
  537. PointF(rKind.Left+rKind.Width*3/4,rKind.Top+rKind.Height/2),
  538. PointF(rKind.Left+rKind.Width*3/4,rKind.Bottom)],False,cmCurve);
  539. ABitmap.Arrow.StartAsClassic;
  540. ABitmap.Arrow.EndAsClassic;
  541. ABitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w);
  542. ABitmap.Arrow.StartAsNone;
  543. ABitmap.Arrow.EndAsNone;
  544. HintText := rsTransformedRasterLayer;
  545. end else
  546. if AClass = TBGRALayerSVGOriginal then
  547. begin
  548. m := AffineMatrixTranslation(rKind.Left,rKind.Top+rKind.Height*0.1)*AffineMatrixScale(rKind.Width,rKind.Height*0.8);
  549. w := max(1,rKind.Height/10);
  550. eb := EasyBezierCurve([PointF(0.28,0),PointF(0,0),PointF(0,0.5),PointF(0.28,0.5),PointF(1/3,1),PointF(0,1)],False,cmCurve);
  551. for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
  552. ABitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
  553. eb := EasyBezierCurve([PointF(0.33,0),PointF(0.47,1),PointF(0.6,0)],False,cmAngle);
  554. for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
  555. ABitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
  556. eb := EasyBezierCurve([PointF(1,0),PointF(0.7,0),PointF(2/3,1),PointF(1,1),PointF(1,0.5),PointF(5/6,0.5)],False,cmCurve);
  557. eb.CurveMode[eb.PointCount-2] := cmAngle;
  558. for i := 0 to eb.PointCount-1 do eb.Point[i] := m*eb.Point[i];
  559. ABitmap.DrawPolyLineAntialias(eb.ToPoints, lColor, w, true);
  560. HintText := rsVectorialLayer;
  561. end else
  562. begin
  563. ABitmap.EllipseAntialias(rKind.Left+rKind.Width / 3, rKind.Top+rKind.Height / 3,rKind.Width / 3,rKind.Height / 3,
  564. lColor, penWidth, lColorTrans);
  565. ABitmap.DrawPolygonAntialias([PointF(rKind.Left+rKind.Width/4,rKind.Bottom),
  566. PointF(rKind.Left+rKind.Width/2,rKind.Top+rKind.Height/4),
  567. PointF(rKind.Right,rKind.Bottom)],lColor,penWidth, lColorTrans);
  568. HintText := rsVectorialLayer;
  569. end;
  570. end;
  571. begin
  572. if ALayout then
  573. begin
  574. ComputeLayout(ABitmap);
  575. AUpdateItem := -1;
  576. end;
  577. penWidth := 1.25/50*FLayerRectHeight;
  578. if penWidth < 1 then penWidth := 1;
  579. if penWidth > 3 then penWidth := 3;
  580. layerPos.x := -FOffset.X;
  581. layerPos.y := -FOffset.Y;
  582. SetLength(FLayerInfo, LazPaintInstance.Image.NbLayers);
  583. clipping := EmptyRect;
  584. for i := LazPaintInstance.Image.NbLayers-1 downto 0 do
  585. begin
  586. if (i = AUpdateItem) or (AUpdateItem = -1) then
  587. begin
  588. with LazPaintInstance.Image do
  589. begin
  590. FLayerInfo[i].FullRect := rect(layerPos.X, layerPos.Y,
  591. layerPos.X + FStackWidth, layerPos.Y + FLayerRectHeight);
  592. if i = CurrentLayerIndex then
  593. begin
  594. ABitmap.FillRect(FLayerInfo[i].FullRect,
  595. GetBackColor(true),dmSet);
  596. lSelected:= true;
  597. end else
  598. begin
  599. if AUpdateItem <> -1 then
  600. ABitmap.FillRect(FLayerInfo[i].FullRect,
  601. GetBackColor(false),dmSet);
  602. lSelected:= false;
  603. end;
  604. if AUpdateItem <> -1 then
  605. clipping := FLayerInfo[i].FullRect;
  606. FLayerInfo[i].VisibleCheckbox := RectWithSize(layerPos.X + FInterruptorWidth div 5, layerpos.Y + (FLayerRectHeight-5*FInterruptorHeight div 2) div 2,
  607. FInterruptorWidth, FInterruptorHeight);
  608. if FLayerInfo[i].FullRect.IntersectsWith(ABitmap.ClipRect) then
  609. begin
  610. lColor := GetTextColor(lSelected);
  611. lColorTrans := lColor;
  612. lColorTrans.alpha := lColorTrans.alpha div 3;
  613. with FLayerInfo[i].VisibleCheckbox do
  614. ABitmap.RectangleAntialias(left, top, right-1, bottom-1, lColor, penWidth);
  615. if LayerVisible[i] then
  616. with FLayerInfo[i].VisibleCheckbox do
  617. begin
  618. ABitmap.DrawPolyLineAntialias(ABitmap.ComputeBezierSpline([
  619. BezierCurve(pointF(left+2,top+3),PointF((left+right-1)/2,bottom-3)),
  620. BezierCurve(PointF((left+right-1)/2,bottom-3),
  621. PointF((left+right-1)/2,(top*2+bottom-1)/3),
  622. PointF(right-2,top-2))]),lColor, penWidth);
  623. end;
  624. FLayerInfo[i].KindIcon := FLayerInfo[i].VisibleCheckbox;
  625. FLayerInfo[i].KindIcon.Offset(0, FInterruptorHeight*3 div 2);
  626. if LayerOriginalDefined[i] then
  627. begin
  628. if LayerOriginalKnown[i] then
  629. DrawKind(LayerOriginalClass[i], FLayerInfo[i].KindIcon, FLayerInfo[i].KindIconHint)
  630. else
  631. DrawKindUnknown(FLayerInfo[i].KindIcon, FLayerInfo[i].KindIconHint);
  632. end
  633. else
  634. DrawKind(nil, FLayerInfo[i].KindIcon, FLayerInfo[i].KindIconHint);
  635. inc(layerPos.X, FInterruptorWidth);
  636. if FMovingItemStart and (i = FMovingItemSourceIndex) then
  637. begin
  638. FreeAndNil(FMovingItemBitmap);
  639. FMovingItemBitmap := TBGRABitmap.Create(FStackWidth - FInterruptorWidth, FLayerRectHeight, GetBackColor(true));
  640. FMovingItemBitmap.FontName := ABitmap.FontName;
  641. FMovingItemBitmap.FontQuality := ABitmap.FontQuality;
  642. FMovingItemBitmap.FontFullHeight := ABitmap.FontFullHeight;
  643. DrawLayerItem(FMovingItemBitmap, Point(0,0), i, lSelected);
  644. FMovingItemOrigin := point(layerPos.X + FOffset.X, layerPos.Y + FOffset.Y);
  645. FMovingItemStart:= false;
  646. end;
  647. FLayerInfo[i].RightPart := DrawLayerItem(ABitmap,layerPos,i,lSelected);
  648. dec(layerPos.X, FInterruptorWidth);
  649. end;
  650. end;
  651. end;
  652. inc(layerPos.Y, FLayerRectHeight);
  653. end;
  654. ABitmap.HorizLine(0, 0, ABitmap.Width-1, DarkThemeInstance.GetColorButtonFace(DarkTheme),
  655. dmDrawWithTransparency, 32768);
  656. prevClip := ABitmap.ClipRect;
  657. if (clipping.right > clipping.left) and (clipping.bottom > clipping.top) then
  658. ABitmap.IntersectClip(clipping);
  659. if VolatileHorzScrollBar <> nil then VolatileHorzScrollBar.Draw(ABitmap);
  660. if VolatileVertScrollBar <> nil then VolatileVertScrollBar.Draw(ABitmap);
  661. if not FScrollButtonRect.IsEmpty then
  662. ABitmap.FillRect(FScrollButtonRect, ColorToBGRA(ColorToRGB(clBtnFace)), dmSet);
  663. ABitmap.ClipRect := prevClip;
  664. if ALayout then
  665. begin
  666. if (FMovingItemBitmap <> nil) and ((FMovingItemMousePos.X <> FMovingItemMouseOrigin.X) or (FMovingItemMousePos.Y <> FMovingItemMouseOrigin.Y)) then
  667. begin
  668. y := FMovingItemOrigin.Y + FMovingItemMousePos.Y - FMovingItemMouseOrigin.Y - FOffset.Y;
  669. if y < 0 then
  670. begin
  671. FTimerScrollDeltaY := -FMovingItemBitmap.Height div 3;
  672. TimerScroll.Enabled := true;
  673. end else
  674. if y + FMovingItemBitmap.Height > ABitmap.Height then
  675. begin
  676. FTimerScrollDeltaY := +FMovingItemBitmap.Height div 3;
  677. TimerScroll.Enabled := true;
  678. end;
  679. ABitmap.PutImage(FMovingItemOrigin.X + FMovingItemMousePos.X - FMovingItemMouseOrigin.X - FOffset.X,
  680. y, FMovingItemBitmap, dmDrawWithTransparency,128);
  681. end;
  682. end;
  683. FMovingItemStart := false;
  684. end;
  685. procedure TLayerStackInterface.ComputeLayout(ABitmap: TBGRABitmap);
  686. var i,temp,h: integer;
  687. begin
  688. FScaling := Container.GetCanvasScaleFactor;
  689. FLayerInfo := nil;
  690. FLayerRectWidth := round(100*zoomFactor*FScaling);
  691. FLayerRectHeight := round(50*zoomFactor*FScaling);
  692. ABitmap.FontName := 'Arial';
  693. ABitmap.FontQuality := fqSystemClearType;
  694. temp := ScaleY(round(20*FScaling),OriginalDPI);
  695. h := FLayerRectHeight div 3;
  696. if h > temp then h := temp;
  697. temp := ScaleY(round(12*FScaling),OriginalDPI);
  698. if h < temp then h := temp;
  699. ABitmap.FontFullHeight := h;
  700. FInterruptorWidth := FLayerRectHeight div 4;
  701. FInterruptorHeight := FLayerRectHeight div 4;
  702. temp := ScaleY(round(28*FScaling),OriginalDPI);
  703. if FInterruptorWidth > temp then FInterruptorWidth := temp;
  704. if FInterruptorHeight > temp then FInterruptorHeight := temp;
  705. temp := ScaleY(round(7*FScaling),OriginalDPI);
  706. if FInterruptorHeight < temp then FInterruptorHeight := temp;
  707. if FInterruptorWidth < temp then FInterruptorWidth := temp;
  708. FStackWidth := FInterruptorWidth + FLayerRectWidth + FInterruptorWidth*6;
  709. FStackHeight := FLayerRectHeight * LazPaintInstance.Image.NbLayers;
  710. for i := 0 to LazPaintInstance.Image.NbLayers-1 do
  711. begin
  712. temp := FInterruptorWidth+ FLayerRectWidth+ ABitmap.TextSize(LazPaintInstance.Image.LayerName[i]).cx;
  713. if temp > FStackWidth then FStackWidth := temp;
  714. end;
  715. if ((VolatileHorzScrollBar = nil) or not VolatileHorzScrollBar.ScrollThumbDown) and
  716. ((VolatileVertScrollBar = nil) or not VolatileVertScrollBar.ScrollThumbDown) then
  717. ComputeScrolling(False, False);
  718. FOffset := FScrollPos;
  719. if FStackHeight < FAvailableHeight then FOffset.Y := -(FAvailableHeight-FStackHeight);
  720. if (VolatileHorzScrollBar <> nil) and (VolatileVertScrollBar <> nil) then
  721. FScrollButtonRect := rect(FAvailableWidth,FAvailableHeight,FAvailableWidth+VolatileScrollBarSize,FAvailableHeight+VolatileScrollBarSize)
  722. else
  723. FScrollButtonRect := EmptyRect;
  724. end;
  725. procedure TLayerStackInterface.ComputeScrolling(AWithHorzScrollBar,
  726. AWithVertScrollBar: boolean);
  727. var
  728. NeedHorzScrollBar,NeedVertScrollBar: boolean;
  729. WithHorzScrollBar,WithVertScrollBar: boolean;
  730. begin
  731. FreeAndNil(VolatileHorzScrollBar);
  732. FreeAndNil(VolatileVertScrollBar);
  733. WithHorzScrollBar:= AWithHorzScrollBar;
  734. WithVertScrollBar:= AWithVertScrollBar;
  735. FAvailableWidth := round(BGRALayerStack.Width*FScaling);
  736. FAvailableHeight := round(BGRALayerStack.Height*FScaling);
  737. if FAvailableWidth <= VolatileThumbSize then WithHorzScrollBar := false;
  738. if FAvailableHeight <= VolatileThumbSize then WithVertScrollBar := false;
  739. if FAvailableWidth <= VolatileScrollBarSize then WithVertScrollBar:= false;
  740. if FAvailableHeight <= VolatileScrollBarSize then WithHorzScrollBar := false;
  741. if WithVertScrollBar then dec(FAvailableWidth, VolatileScrollBarSize);
  742. if FAvailableWidth <= VolatileThumbSize then WithHorzScrollBar := false;
  743. if WithHorzScrollBar then dec(FAvailableHeight, VolatileScrollBarSize);
  744. if FAvailableHeight <= VolatileThumbSize then
  745. begin
  746. WithVertScrollBar := false;
  747. FAvailableWidth := BGRALayerStack.Width;
  748. end;
  749. FMaxScrollPos := point(FStackWidth-FAvailableWidth,FStackHeight-FAvailableHeight);
  750. if FMaxScrollPos.X < 0 then FMaxScrollPos.X := 0;
  751. if FMaxScrollPos.Y < 0 then FMaxScrollPos.Y := 0;
  752. //check if scrollbars should be added
  753. if not AWithHorzScrollBar or not AWithVertScrollBar then
  754. begin
  755. NeedHorzScrollBar:= (FMaxScrollPos.X > 0);
  756. NeedVertScrollBar:= (FMaxScrollPos.Y > 0);
  757. if (NeedHorzScrollBar and not AWithHorzScrollBar) or (NeedVertScrollBar and not AWithVertScrollBar) then
  758. begin
  759. ComputeScrolling(WithHorzScrollBar or NeedHorzScrollBar, WithVertScrollBar or NeedVertScrollBar);
  760. exit;
  761. end;
  762. end;
  763. if FScrollStackItemIntoView then
  764. begin
  765. FScrollPos.X := 0;
  766. FScrollPos.Y := (LazPaintInstance.Image.NbLayers-1-LazPaintInstance.Image.CurrentLayerIndex)*FLayerRectHeight;
  767. FScrollStackItemIntoView := false;
  768. end;
  769. if FScrollPos.X < 0 then FScrollPos.X := 0;
  770. if FScrollPos.Y < 0 then FScrollPos.Y := 0;
  771. if FScrollPos.X > FMaxScrollPos.X then FScrollPos.X := FMaxScrollPos.X;
  772. if FScrollPos.Y > FMaxScrollPos.Y then FScrollPos.Y := FMaxScrollPos.Y;
  773. if WithHorzScrollBar then
  774. VolatileHorzScrollBar := TVolatileScrollBar.Create(0,FAvailableHeight,FAvailableWidth,VolatileScrollBarSize,sbHorizontal,FScrollPos.X,0,FMaxScrollPos.X);
  775. if WithVertScrollBar then
  776. VolatileVertScrollBar := TVolatileScrollBar.Create(FAvailableWidth,0,VolatileScrollBarSize,FAvailableHeight,sbVertical,FScrollPos.Y,0,FMaxScrollPos.Y);
  777. end;
  778. procedure TLayerStackInterface.DoScrollVertically(AAmount: integer);
  779. var prevY: integer;
  780. begin
  781. prevY := FScrollPos.Y;
  782. FScrollPos.Y += AAmount;
  783. if FScrollPos.Y < 0 then FScrollPos.Y := 0;
  784. if FScrollPos.Y > FMaxScrollPos.Y then FScrollPos.Y := FMaxScrollPos.Y;
  785. if FScrollPos.Y <> prevY then
  786. begin
  787. FMovingItemMouseOrigin.Y -= FScrollPos.Y-prevY;
  788. BGRALayerStack.DiscardBitmap;
  789. end;
  790. end;
  791. function TLayerStackInterface.HandleSelectLayer(i, x, y: integer; AStartMoving: boolean): boolean;
  792. var prevIndex: integer;
  793. begin
  794. result := false;
  795. FInHandleSelectLayer := true;
  796. if (i < LazPaintInstance.Image.NbLayers) then
  797. begin
  798. prevIndex := LazPaintInstance.Image.CurrentLayerIndex;
  799. if not LazPaintInstance.Image.SelectionLayerIsEmpty and
  800. (i <> LazPaintInstance.Image.CurrentLayerIndex) then
  801. begin
  802. FAskTransferSelectionLayerIndex := i;
  803. exit;
  804. end;
  805. if LazPaintInstance.Image.SetCurrentLayerByIndex(i) then
  806. begin
  807. FRenaming := false;
  808. UpdateLayerStackItem(prevIndex);
  809. if AStartMoving then
  810. begin
  811. FMovingItemStart := true;
  812. FMovingItemSourceIndex := i;
  813. FMovingItemMouseOrigin := point(x,y);
  814. FMovingItemMousePos := point(x,y);
  815. end;
  816. UpdateLayerStackItem(i);
  817. result := true;
  818. end;
  819. end;
  820. FInHandleSelectLayer := false;
  821. end;
  822. procedure TLayerStackInterface.HandleChangeLayerOpacity(X, Y: integer);
  823. var newOpacity: integer;
  824. begin
  825. if (FChangingLayerOpacityIndex <> -1) and (FChangingLayerOpacityIndex <= high(FLayerInfo)) then
  826. with FLayerInfo[FChangingLayerOpacityIndex].RightPart do
  827. begin
  828. if FChangingLayerOpacityIndex >= LazPaintInstance.Image.NbLayers then exit;
  829. newOpacity := round((X-(OpacityBar.left+1))/(OpacityBar.right-OpacityBar.left-2)*255);
  830. if newOpacity < 0 then newOpacity:= 0;
  831. if newOpacity > 255 then newOpacity:= 255;
  832. if LazPaintInstance.Image.LayerOpacity[FChangingLayerOpacityIndex] = newOpacity then exit;
  833. FDontUpdateStack := true;
  834. LazPaintInstance.Image.LayerOpacity[FChangingLayerOpacityIndex] := newOpacity;
  835. FDontUpdateStack := false;
  836. UpdateLayerStackItem(FChangingLayerOpacityIndex);
  837. end;
  838. end;
  839. procedure TLayerStackInterface.UpdateLayerStackItem(AIndex: integer);
  840. begin
  841. if Assigned(BGRALayerStack.Bitmap) then
  842. begin
  843. FPartialRedraw := true;
  844. if (AIndex >= 0) and (AIndex < length(FLayerInfo)) then
  845. BGRALayerStack.RedrawBitmap(FLayerInfo[AIndex].FullRect);
  846. FPartialRedraw := false;
  847. end;
  848. end;
  849. procedure TLayerStackInterface.NeedCheckers;
  850. var
  851. checkerSize: Integer;
  852. begin
  853. checkerSize := DoScaleX(round(2*FScaling), OriginalDPI, DPI);
  854. if Assigned(FRegularBackground) and (FRegularBackground.Width <> checkerSize*2) then
  855. begin
  856. FreeAndNil(FRegularBackground);
  857. FreeAndNil(FIconBackground);
  858. end;
  859. if FRegularBackground = nil then
  860. begin
  861. FRegularBackground := TBGRABitmap.Create(checkerSize*2,checkerSize*2, ImageCheckersColor1);
  862. FRegularBackground.FillRect(0,0, checkerSize,checkerSize, ImageCheckersColor2, dmDrawWithTransparency);
  863. FRegularBackground.FillRect(checkerSize,checkerSize, checkerSize*2,checkerSize*2, ImageCheckersColor2, dmDrawWithTransparency);
  864. FIconBackground := TBGRABitmap.Create(checkerSize*2,checkerSize*2, IconCheckersColor1);
  865. FIconBackground.FillRect(0,0, checkerSize,checkerSize, IconCheckersColor2, dmDrawWithTransparency);
  866. FIconBackground.FillRect(checkerSize,checkerSize, checkerSize*2,checkerSize*2, IconCheckersColor2, dmDrawWithTransparency);
  867. end;
  868. end;
  869. procedure TLayerStackInterface.LazPaint_ImageChanged(
  870. AEvent: TLazPaintImageObservationEvent);
  871. begin
  872. UpdateComboBlendOp;
  873. if not AEvent.DelayedStackUpdate and
  874. not FInHandleSelectLayer then InvalidateStack(False);
  875. end;
  876. procedure TLayerStackInterface.UpdateComboBlendOp;
  877. var
  878. blendOps: TStringList;
  879. str,selectedStr: string;
  880. i: integer;
  881. begin
  882. if FUpdatingComboBlendOp then exit;
  883. FUpdatingComboBlendOp := true;
  884. FQuerySelectBlendOp:= false;
  885. blendOps := TStringList.Create;
  886. selectedStr := '';
  887. blendOps.AddStrings(ComboBox_BlendOp.Items);
  888. i := blendOps.IndexOf(rsOtherBlendOp);
  889. if i <> -1 then blendOps.Delete(i);
  890. i := blendOps.IndexOf(rsNormalBlendOp);
  891. if i <> -1 then blendOps.Delete(i);
  892. with LazPaintInstance.Image do
  893. for i := 0 to NbLayers-1 do
  894. begin
  895. str := BlendOperationStr[BlendOperation[i]];
  896. if blendOps.IndexOf(str) = -1 then
  897. blendOps.Add(str);
  898. if i = LazPaintInstance.Image.CurrentLayerIndex then
  899. selectedStr := str;
  900. end;
  901. if selectedStr = BlendOperationStr[boTransparent] then
  902. selectedStr := rsNormalBlendOp;
  903. i := blendOps.IndexOf(BlendOperationStr[boTransparent]);
  904. if i <> -1 then blendOps.Delete(i);
  905. blendOps.Sort;
  906. blendOps.Insert(0,rsNormalBlendOp);
  907. blendOps.Add(rsOtherBlendOp);
  908. if not blendOps.Equals(ComboBox_BlendOp.Items) then
  909. ComboBox_BlendOp.Items.Assign(blendOps);
  910. if ComboBox_BlendOp.ItemIndex <> ComboBox_BlendOp.Items.IndexOf(selectedStr) then
  911. ComboBox_BlendOp.ItemIndex := ComboBox_BlendOp.Items.IndexOf(selectedStr);
  912. blendOps.Free;
  913. FUpdatingComboBlendOp := false;
  914. end;
  915. procedure TLayerStackInterface.SelectBlendOp;
  916. var blendOp: TBlendOperation;
  917. topmostInfo: TTopMostInfo;
  918. tempUnder: TBGRABitmap;
  919. begin
  920. blendOp := boTransparent;
  921. topmostInfo := LazPaintInstance.HideTopmost;
  922. if LazPaintInstance.Image.CurrentLayerIndex > 0 then
  923. tempUnder := LazPaintInstance.Image.ComputeFlatImage(0,LazPaintInstance.Image.CurrentLayerIndex-1,False)
  924. else
  925. tempUnder := TBGRABitmap.Create(1,1);
  926. if UBlendOp.ShowBlendOpDialog(LazPaintInstance, blendOp, tempUnder,LazPaintInstance.Image.CurrentLayerReadOnly) then
  927. begin
  928. FDontUpdateStack := true;
  929. LazPaintInstance.Image.BlendOperation[LazPaintInstance.Image.CurrentLayerIndex] := blendOp;
  930. FDontUpdateStack := false;
  931. end;
  932. UpdateComboBlendOp;
  933. tempUnder.Free;
  934. LazPaintInstance.ShowTopmost(topmostInfo);
  935. if LazPaintInstance.Image.CurrentLayerIndex = 0 then
  936. LazPaintInstance.ToolManager.ToolPopup(tpmBlendOpBackground);
  937. end;
  938. procedure TLayerStackInterface.QuerySelectBlendOp;
  939. begin
  940. TimerQuery.Enabled := true;
  941. FQuerySelectBlendOp := true;
  942. end;
  943. procedure TLayerStackInterface.ToolZoomLayerStackIn_Click(Sender: TObject);
  944. begin
  945. if Assigned(LazPaintInstance) then
  946. LazPaintInstance.ExitColorEditor;
  947. ZoomFactor := ZoomFactor * 1.3;
  948. end;
  949. procedure TLayerStackInterface.ToolZoomLayerStackOut_Click(Sender: TObject);
  950. begin
  951. if Assigned(LazPaintInstance) then
  952. LazPaintInstance.ExitColorEditor;
  953. ZoomFactor := ZoomFactor / 1.3;
  954. end;
  955. procedure TLayerStackInterface.ApplyThemeAndDPI;
  956. var
  957. spacing, iconSize: Integer;
  958. begin
  959. iconSize := DoScaleX(16, OriginalDPI, DPI);
  960. Toolbar.Images := LazPaintInstance.Icons[iconSize];
  961. Toolbar.ButtonWidth:= iconSize + DoScaleX(4, OriginalDPI, DPI);
  962. Toolbar.ButtonHeight:= iconSize + DoScaleY(4, OriginalDPI, DPI);
  963. FLayerMenu.Images := LazPaintInstance.Icons[DoScaleX(20,OriginalDPI)];
  964. ComboBox_BlendOp.Width := Toolbar.ButtonWidth*7;
  965. ComboBox_BlendOp.Height := Toolbar.ButtonHeight;
  966. DarkThemeInstance.Apply(PanelToolbar, DarkTheme);
  967. BGRALayerStack.Color:= GetBackColor(False);
  968. BGRALayerStack.DiscardBitmap;
  969. spacing := DoScaleX(2, OriginalDPI, DPI);
  970. if PanelToolbar.BevelOuter <> bvNone then dec(spacing, PanelToolbar.BevelWidth);
  971. PanelToolbar.ChildSizing.TopBottomSpacing:= spacing;
  972. PanelToolbar.ChildSizing.LeftRightSpacing:= spacing;
  973. Container.Color := DarkThemeInstance.GetColorButtonFace(DarkTheme);
  974. end;
  975. procedure TLayerStackInterface.SetDPI(AValue: integer);
  976. var
  977. prevDPI: Integer;
  978. begin
  979. if FDPI=AValue then Exit;
  980. prevDPI := FDPI;
  981. FDPI:=AValue;
  982. ApplyThemeAndDPI;
  983. if prevDPI > 0 then
  984. ZoomFactor:= ZoomFactor*AValue/prevDPI;
  985. end;
  986. constructor TLayerStackInterface.Create(AContainer: TWinControl; AInstance: TLazPaintCustomInstance);
  987. begin
  988. Container := AContainer;
  989. LazPaintInstance := AInstance;
  990. FDPI := OriginalDPI;
  991. PanelToolbar := TPanel.Create(Container);
  992. PanelToolbar.Parent := Container;
  993. PanelToolbar.Align := alBottom;
  994. PanelToolbar.AutoSize:= false;
  995. PanelToolbar.Height := 200;
  996. Toolbar := TToolBar.Create(PanelToolbar);
  997. Toolbar.Parent := PanelToolbar;
  998. Toolbar.ShowHint := true;
  999. Toolbar.Wrapable := true;
  1000. Toolbar.AutoSize := true;
  1001. Toolbar.EdgeInner:= esNone;
  1002. Toolbar.EdgeOuter:= esNone;
  1003. Toolbar.Indent := 0;
  1004. Toolbar.Align:= alTop;
  1005. Toolbar.OnResize:= @Toolbar_Resize;
  1006. ComboBox_BlendOp := TBCComboBox.Create(ToolBar);
  1007. ComboBox_BlendOp.Parent := Toolbar;
  1008. ComboBox_BlendOp.OnChange:= @ComboBox_BlendOpChange;
  1009. UpdateComboBlendOp;
  1010. AddButton(rsSelectBlendOperation, 103, @ToolSelectBlendOperation_Click);
  1011. AddButton(rsZoomLayerStackIn, 6, @ToolZoomLayerStackIn_Click);
  1012. AddButton(rsZoomLayerStackOut, 7, @ToolZoomLayerStackOut_Click);
  1013. FRenaming := false;
  1014. FMovingItemStart := false;
  1015. FScrollPos := point(0,0);
  1016. FZoomFactor := 1;
  1017. FChangingLayerOpacityIndex:= -1;
  1018. FAskTransferSelectionLayerIndex := -1;
  1019. VolatileHorzScrollBar := nil;
  1020. VolatileVertScrollBar := nil;
  1021. BGRALayerStack := TBGRAVirtualScreen.Create(Container);
  1022. BGRALayerStack.Parent := Container;
  1023. BGRALayerStack.Align:= alClient;
  1024. BGRALayerStack.Caption := '';
  1025. BGRALayerStack.OnRedraw:= @BGRALayerStack_Redraw;
  1026. BGRALayerStack.OnMouseDown:=@BGRALayerStack_MouseDown;
  1027. BGRALayerStack.OnMouseMove:=@BGRALayerStack_MouseMove;
  1028. BGRALayerStack.OnMouseUp:=@BGRALayerStack_MouseUp;
  1029. BGRALayerStack.OnMouseWheel:=@BGRALayerStack_MouseWheel;
  1030. BGRALayerStack.BitmapAutoScale:= false;
  1031. TimerScroll := TTimer.Create(Container);
  1032. TimerScroll.Enabled := false;
  1033. TimerScroll.Interval := 30;
  1034. TimerScroll.OnTimer:=@TimerScroll_Timer;
  1035. TimerQuery := TTimer.Create(Container);
  1036. TimerQuery.Enabled := false;
  1037. TimerQuery.Interval := 200;
  1038. TimerQuery.OnTimer:=@TimerQuery_Timer;
  1039. FQuerySelectBlendOp:= false;
  1040. FLayerMenu := TPopupMenu.Create(AContainer);
  1041. FQueryLayerMenu:= false;
  1042. ApplyThemeAndDPI;
  1043. LazPaintInstance.Image.OnImageChanged.AddObserver(@LazPaint_ImageChanged);
  1044. end;
  1045. destructor TLayerStackInterface.Destroy;
  1046. begin
  1047. LazPaintInstance.Image.OnImageChanged.RemoveObserver(@LazPaint_ImageChanged);
  1048. FreeAndNil(VolatileHorzScrollBar);
  1049. FreeAndNil(VolatileVertScrollBar);
  1050. FreeAndNil(FRegularBackground);
  1051. FreeAndNil(FIconBackground);
  1052. FreeAndNil(FMovingItemBitmap);
  1053. inherited Destroy;
  1054. end;
  1055. procedure TLayerStackInterface.AddButton(AAction: TBasicAction);
  1056. var button: TToolButton;
  1057. begin
  1058. if not Assigned(Toolbar) then exit;
  1059. button := TToolButton.Create(Toolbar);
  1060. button.Action := AAction;
  1061. button.Style := tbsButton;
  1062. button.Parent := Toolbar;
  1063. end;
  1064. function TLayerStackInterface.GetTextColor(ASelected: boolean): TColor;
  1065. begin
  1066. if ASelected then
  1067. result := DarkThemeInstance.GetColorHighlightText(DarkTheme)
  1068. else result := DarkThemeInstance.GetColorEditableText(DarkTheme);
  1069. end;
  1070. procedure TLayerStackInterface.AddButton(ACaption: string;
  1071. AImageIndex: integer; AOnClick: TNotifyEvent);
  1072. var button: TToolButton;
  1073. begin
  1074. if not Assigned(Toolbar) then exit;
  1075. button := TToolButton.Create(Toolbar);
  1076. button.Hint := ACaption;
  1077. button.ImageIndex := AImageIndex;
  1078. button.Style := tbsButton;
  1079. button.Parent := Toolbar;
  1080. button.OnClick := AOnClick;
  1081. end;
  1082. procedure TLayerStackInterface.AddLayerMenu(AAction: TBasicAction);
  1083. var
  1084. item: TMenuItem;
  1085. begin
  1086. item := TMenuItem.Create(FLayerMenu);
  1087. item.Action := AAction;
  1088. FLayerMenu.Items.Add(item);
  1089. end;
  1090. procedure TLayerStackInterface.ScrollToItem(AIndex: integer; AUpdateStack: boolean);
  1091. begin
  1092. FScrollPos.X := 0;
  1093. FScrollPos.Y := (LazPaintInstance.Image.NbLayers-1-AIndex)*FLayerRectHeight+FLayerRectHeight div 2-FAvailableHeight div 2;
  1094. if AUpdateStack then BGRALayerStack.DiscardBitmap;
  1095. end;
  1096. procedure TLayerStackInterface.InvalidateStack(AScrollIntoView: boolean);
  1097. begin
  1098. if not FDontUpdateStack then
  1099. begin
  1100. if Assigned(BGRALayerStack) then
  1101. BGRALayerStack.DiscardBitmap;
  1102. if AScrollIntoView then FScrollStackItemIntoView := true;
  1103. FRenaming := false;
  1104. end;
  1105. end;
  1106. function TLayerStackInterface.GetWidthFor(AButtonCount: integer): integer;
  1107. begin
  1108. result := Toolbar.ButtonWidth*AButtonCount + DoScaleX(2, OriginalDPI, DPI)*2;
  1109. end;
  1110. function TLayerStackInterface.GetBackColor(ASelected: boolean): TColor;
  1111. begin
  1112. if ASelected then
  1113. result := DarkThemeInstance.GetColorHighlightBack(DarkTheme) else
  1114. begin
  1115. if DarkTheme then
  1116. result := MergeBGRAWithGammaCorrection(
  1117. DarkThemeInstance.GetColorButtonFace(true), 1,
  1118. DarkThemeInstance.GetColorEditableFace(true), 1)
  1119. else
  1120. result := DarkThemeInstance.GetColorEditableFace(DarkTheme);
  1121. end;
  1122. end;
  1123. end.