ublendop.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UBlendOp;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  7. StdCtrls, ExtCtrls, types, BGRABitmap, BGRABitmapTypes, LazPaintType;
  8. type
  9. { TFBlendOp }
  10. TFBlendOp = class(TForm)
  11. Bevel1: TBevel;
  12. Button_Cancel: TButton;
  13. Button_OK: TButton;
  14. Label_PreviewWith: TLabel;
  15. Label_BlendOpCategory: TLabel;
  16. Label_BlendOpValue: TLabel;
  17. Label_SelectedBlendOp: TLabel;
  18. Label_SvgOver: TLabel;
  19. Label_KritaOver: TLabel;
  20. Label_OtherOver: TLabel;
  21. Label_PatternUnder: TLabel;
  22. Label_PatternOver: TLabel;
  23. ListBox_BlendOther: TListBox;
  24. ListBox_BlendSvg: TListBox;
  25. ListBox_BlendKrita: TListBox;
  26. ListBox_PatternUnder: TListBox;
  27. ListBox_PatternOver: TListBox;
  28. ScrollBar1: TScrollBar;
  29. TimerResize: TTimer;
  30. procedure Button_OKClick(Sender: TObject);
  31. procedure FormCreate(Sender: TObject);
  32. procedure FormHide(Sender: TObject);
  33. procedure FormResize(Sender: TObject);
  34. procedure FormShow(Sender: TObject);
  35. procedure ListBox_BlendDblClick(Sender: TObject);
  36. procedure ListBox_BlendSelectionChange(Sender: TObject; {%H-}User: boolean);
  37. procedure ListBox_DrawBlendItem(Control: TWinControl; Index: Integer;
  38. ARect: TRect; State: TOwnerDrawState);
  39. procedure ListBox_DrawPatternItem(Control: TWinControl;
  40. Index: Integer; ARect: TRect; State: TOwnerDrawState);
  41. procedure ListBox_PatternSelectionChange(Sender: TObject; {%H-}User: boolean
  42. );
  43. procedure ListBox_MeasureItem(Control: TWinControl;
  44. {%H-}Index: Integer; var AHeight: Integer);
  45. procedure TimerResizeTimer(Sender: TObject);
  46. private
  47. FPatterns: array of record
  48. name:string;
  49. bmp: TBGRABitmap;
  50. width,height: integer;
  51. end;
  52. FListBoxInternalMargin: integer;
  53. FFirstColumnLeft: integer;
  54. FLastColumnRightMargin: integer;
  55. FComputedWidth,FComputedHeight: integer;
  56. procedure DrawPattern(ACanvas: TCanvas; ARect: TRect; APattern: string;
  57. State: TOwnerDrawState);
  58. function GetPattern(AWidth, AHeight: integer; APattern: string;
  59. ACheckers: boolean): TBGRABitmap;
  60. { private declarations }
  61. procedure UpdateBlendOpLabel;
  62. procedure DiscardPatterns;
  63. public
  64. { public declarations }
  65. SelectedBlendOp: TBlendOperation;
  66. PatternUnder,PatternOver: TBGRABitmap;
  67. end;
  68. function ShowBlendOpDialog(AInstance: TLazPaintCustomInstance; var BlendOp: TBlendOperation; APatternUnder, APatternOver: TBGRABitmap): boolean;
  69. implementation
  70. uses LCLType,LCScaleDPI,umac,uresourcestrings,ugraph,BGRAThumbnail,Math, BGRATextFX;
  71. function TFBlendOp.GetPattern(AWidth,AHeight: integer; APattern: string; ACheckers: boolean): TBGRABitmap;
  72. var lColor: TBGRAPixel;
  73. idx: integer;
  74. fullPatternName, attr: string;
  75. i: integer;
  76. begin
  77. fullPatternName:= APattern;
  78. for i := 0 to high(FPatterns) do
  79. begin
  80. if (FPatterns[i].name = fullPatternName) and (FPatterns[i].width = AWidth) and (FPatterns[i].height = AHeight) then
  81. begin
  82. result := FPatterns[i].bmp;
  83. exit;
  84. end;
  85. end;
  86. if APattern = 'Under' then
  87. begin
  88. result := GetBitmapThumbnail(PatternUnder,AWidth,AHeight,BGRAPixelTransparent,ACheckers) as TBGRABitmap;
  89. end else
  90. if APattern = 'Over' then
  91. begin
  92. result := GetBitmapThumbnail(PatternOver,AWidth,AHeight,BGRAPixelTransparent,ACheckers) as TBGRABitmap;
  93. end else
  94. begin
  95. result := TBGRABitmap.Create(AWidth,AHeight, BGRABlack);
  96. lColor := BGRAWhite;
  97. idx := pos('.',APattern);
  98. if idx <> 0 then
  99. begin
  100. attr := copy(APattern,idx+1,length(APattern)-idx);
  101. delete(APattern,idx,length(APattern)-idx+1);
  102. lColor := StrToBGRA(attr,BGRAWhite);
  103. end;
  104. if APattern = 'LeftToRight' then
  105. result.GradientFill(0,0,result.Width,result.Height,BGRABlack,lColor,gtLinear,PointF(0,0),PointF(result.Width-1,0),dmSet,False) else
  106. if APattern = 'TopToBottom' then
  107. result.GradientFill(0,0,result.Width,result.Height,BGRABlack,lColor,gtLinear,PointF(0,0),PointF(0,result.Height-1),dmSet,False) else
  108. if APattern = 'Ellipse' then
  109. result.GradientFill(0,0,result.Width,result.Height,lColor,BGRABlack,gtRadial,PointF((result.Width-1)/2,(result.Height-1)/2),PointF(0,(result.Height-1)/2),dmSet,False);
  110. BGRAReplace(result,GetBitmapThumbnail(result,AWidth,AHeight,BGRAPixelTransparent,false));
  111. end;
  112. setlength(FPatterns,length(FPatterns)+1);
  113. FPatterns[high(FPatterns)].name := fullPatternName;
  114. FPatterns[high(FPatterns)].bmp := result;
  115. FPatterns[high(FPatterns)].width:= AWidth;
  116. FPatterns[high(FPatterns)].height:= AHeight;
  117. end;
  118. procedure DrawPatternHighlight(ABmp: TBGRABitmap);
  119. begin
  120. ABmp.FillPoly([PointF(0,0),PointF(ABmp.Width,0),PointF(ABmp.Width,ABmp.Height),PointF(0,ABmp.Height),EmptyPointF,
  121. PointF(ABmp.Width div 8,ABmp.Height*7 div 8),PointF(ABmp.Width*7 div 8,ABmp.Height*7 div 8),
  122. PointF(ABmp.Width*7 div 8,ABmp.Height div 8),PointF(ABmp.Width div 8,ABmp.Height div 8)],
  123. ColorToBGRA(ColorToRGB(clHighlight),128),dmDrawWithTransparency);
  124. end;
  125. procedure AddCheckersIfNeeded(var ABmp: TBGRABitmap);
  126. var temp: TBGRABitmap;
  127. begin
  128. if ABmp.HasTransparentPixels then
  129. begin
  130. temp := TBGRABitmap.Create(ABmp.Width,ABmp.Height);
  131. DrawCheckers(temp, rect(0,0,temp.Width,temp.Height));
  132. temp.PutImage(0,0,ABmp,dmDrawWithTransparency);
  133. ABmp.Free;
  134. ABmp := temp;
  135. end;
  136. end;
  137. procedure TFBlendOp.DrawPattern(ACanvas: TCanvas; ARect: TRect; APattern: string; State: TOwnerDrawState);
  138. var bmp: TBGRABitmap;
  139. begin
  140. if (ARect.Right <= ARect.Left) or (ARect.Bottom <= ARect.Top) then exit;
  141. bmp := TBGRABitmap.Create(ARect.Right-ARect.Left,ARect.Bottom-ARect.Top,ColorToRGB(clBtnFace));
  142. bmp.PutImage(0,0,GetPattern(bmp.width,bmp.height,APattern,True),dmDrawWithTransparency);
  143. if odSelected in State then DrawPatternHighlight(bmp);
  144. bmp.Draw(ACanvas,ARect.Left,ARect.Top,false);
  145. bmp.Free;
  146. end;
  147. function ShowBlendOpDialog(AInstance: TLazPaintCustomInstance; var BlendOp: TBlendOperation; APatternUnder,
  148. APatternOver: TBGRABitmap): boolean;
  149. var f: TFBlendOp;
  150. begin
  151. result := false;
  152. f:= TFBlendOp.Create(nil);
  153. f.PatternOver := APatternOver;
  154. f.PatternUnder := APatternUnder;
  155. try
  156. if f.ShowModal = mrOK then
  157. begin
  158. result := true;
  159. BlendOp := f.SelectedBlendOp;
  160. end;
  161. except on ex:Exception do
  162. AInstance.ShowError('ShowBlendOpDialog',ex.Message);
  163. end;
  164. f.Free;
  165. end;
  166. function BlendThumbNailSize: integer;
  167. begin
  168. result := ScaleY(80,OriginalDPI);
  169. end;
  170. { TFBlendOp }
  171. procedure TFBlendOp.ListBox_DrawPatternItem(Control: TWinControl;
  172. Index: Integer; ARect: TRect; State: TOwnerDrawState);
  173. begin
  174. {$IFDEF LINUX}
  175. ARect.Right := ARect.Left+Control.Width-FListBoxInternalMargin;
  176. {$ENDIF}
  177. if Index <> -1 then
  178. DrawPattern((Control as TListBox).Canvas,ARect,(Control as TListBox).Items[Index],State);
  179. end;
  180. procedure TFBlendOp.ListBox_PatternSelectionChange(Sender: TObject;
  181. User: boolean);
  182. begin
  183. ListBox_BlendSvg.Invalidate;
  184. ListBox_BlendKrita.Invalidate;
  185. ListBox_BlendOther.Invalidate;
  186. end;
  187. procedure TFBlendOp.ListBox_MeasureItem(Control: TWinControl;
  188. Index: Integer; var AHeight: Integer);
  189. begin
  190. AHeight := (Control as TListBox).ItemHeight;
  191. end;
  192. procedure TFBlendOp.TimerResizeTimer(Sender: TObject);
  193. var leftPos: integer;
  194. columnWidth, rowHeight: integer;
  195. begin
  196. DiscardPatterns;
  197. leftPos := FFirstColumnLeft;
  198. columnWidth := (ClientWidth - FLastColumnRightMargin - leftPos) div 3;
  199. if columnWidth < 4 then columnWidth:= 4;
  200. rowHeight := columnWidth*600 div 800;
  201. Label_SvgOver.Left := leftPos;
  202. Label_SvgOver.Width := columnWidth-2;
  203. ListBox_BlendSvg.Left := leftPos;
  204. ListBox_BlendSvg.Width := columnWidth-2;
  205. ListBox_BlendSvg.ItemHeight := rowHeight;
  206. leftPos += columnWidth;
  207. Label_KritaOver.Left := leftPos;
  208. Label_KritaOver.Width := columnWidth-2;
  209. ListBox_BlendKrita.Left := leftPos;
  210. ListBox_BlendKrita.Width := columnWidth-2;
  211. ListBox_BlendKrita.ItemHeight := rowHeight;
  212. leftPos += columnWidth;
  213. Label_OtherOver.Left := leftPos;
  214. Label_OtherOver.Width := columnWidth-2;
  215. ListBox_BlendOther.Left := leftPos;
  216. ListBox_BlendOther.Width := columnWidth-2;
  217. ListBox_BlendOther.ItemHeight := rowHeight;
  218. TimerResize.Enabled := false;
  219. end;
  220. procedure TFBlendOp.UpdateBlendOpLabel;
  221. var str: string;
  222. compatible: TStringList;
  223. begin
  224. if SelectedBlendOp = boTransparent then
  225. str := rsNormalBlendOp
  226. else
  227. begin
  228. str := BlendOperationStr[SelectedBlendOp];
  229. compatible := TStringList.Create;
  230. if SelectedBlendOp in[boColorBurn,boColorDodge,boDarken,boHardLight,boLighten,
  231. boMultiply,boOverlay,boScreen,boSoftLight,boLinearDifference] then compatible.Add(rsAllApplications);
  232. if SelectedBlendOp in[boLinearAdd,boXor,boGlow,boReflect,boLinearNegation] then compatible.Add('Paint.NET');
  233. if SelectedBlendOp in[boDivide,boLinearAdd,boLinearExclusion,boLinearSubtract,boLinearSubtractInverse] then compatible.Add('Krita');
  234. if compatible.Count = 0 then str += ' ('+rsLazPaintOnly+')' else
  235. str += ' (' + compatible.CommaText+')';
  236. compatible.Free;
  237. end;
  238. Label_BlendOpValue.Left := Label_SelectedBlendOp.Left + Label_SelectedBlendOp.Width + ScaleX(8,OriginalDPI);
  239. Label_BlendOpValue.Caption := str;
  240. end;
  241. procedure TFBlendOp.DiscardPatterns;
  242. var i: integer;
  243. begin
  244. for i := 0 to high(FPatterns) do
  245. FPatterns[i].bmp.free;
  246. FPatterns := nil;
  247. end;
  248. procedure TFBlendOp.FormCreate(Sender: TObject);
  249. begin
  250. ScaleControl(self,OriginalDPI);
  251. FListBoxInternalMargin:= ListBox_PatternUnder.Width - ListBox_PatternUnder.ClientWidth + ScrollBar1.Height;
  252. {$IFDEF LINUX}
  253. ListBox_PatternUnder.Style := lbOwnerDrawVariable;
  254. ListBox_PatternUnder.ScrollWidth := 0;
  255. ListBox_PatternOver.Style := lbOwnerDrawVariable;
  256. ListBox_PatternOver.ScrollWidth := 0;
  257. ListBox_BlendSvg.Style := lbOwnerDrawVariable;
  258. ListBox_BlendSvg.ScrollWidth := 0;
  259. ListBox_BlendKrita.Style := lbOwnerDrawVariable;
  260. ListBox_BlendKrita.ScrollWidth := 0;
  261. ListBox_BlendOther.Style := lbOwnerDrawVariable;
  262. ListBox_BlendOther.ScrollWidth := 0;
  263. {$ENDIF}
  264. ListBox_PatternUnder.ItemHeight := BlendThumbNailSize;
  265. ListBox_PatternOver.ItemHeight := BlendThumbNailSize;
  266. ListBox_BlendSvg.ItemHeight := BlendThumbNailSize;
  267. ListBox_BlendKrita.ItemHeight := BlendThumbNailSize;
  268. ListBox_BlendOther.ItemHeight := BlendThumbNailSize;
  269. ListBox_PatternUnder.ItemIndex := 0;
  270. ListBox_PatternOver.ItemIndex := 0;
  271. CheckOKCancelBtns(Button_OK,Button_Cancel);
  272. FFirstColumnLeft := ListBox_BlendSvg.Left;
  273. FLastColumnRightMargin:= ClientWidth-(ListBox_BlendOther.Left+ListBox_BlendOther.Width);
  274. TimerResizeTimer(nil);
  275. end;
  276. procedure TFBlendOp.FormHide(Sender: TObject);
  277. begin
  278. DiscardPatterns;
  279. end;
  280. procedure TFBlendOp.FormResize(Sender: TObject);
  281. begin
  282. TimerResize.Enabled := false;
  283. TimerResize.Enabled := true;
  284. end;
  285. procedure TFBlendOp.Button_OKClick(Sender: TObject);
  286. begin
  287. ModalResult:= mrOk;
  288. end;
  289. procedure TFBlendOp.FormShow(Sender: TObject);
  290. begin
  291. SelectedBlendOp := boTransparent;
  292. FComputedWidth := Max(PatternOver.Width,PatternUnder.Width);
  293. FComputedHeight := Max(PatternOver.Height,PatternUnder.Height);
  294. UpdateBlendOpLabel;
  295. end;
  296. procedure TFBlendOp.ListBox_BlendDblClick(Sender: TObject);
  297. begin
  298. if not Visible then exit;
  299. with Sender as TListBox do
  300. begin
  301. if ItemIndex <> -1 then
  302. begin
  303. SelectedBlendOp := StrToBlendOperation(Items[ItemIndex]);
  304. UpdateBlendOpLabel;
  305. ModalResult := mrOk;
  306. end;
  307. end;
  308. end;
  309. procedure TFBlendOp.ListBox_BlendSelectionChange(Sender: TObject;
  310. User: boolean);
  311. begin
  312. if not Visible then exit;
  313. with Sender as TListBox do
  314. begin
  315. if ItemIndex <> -1 then
  316. begin
  317. SelectedBlendOp := StrToBlendOperation(Items[ItemIndex]);
  318. UpdateBlendOpLabel;
  319. if not (Sender = ListBox_BlendSvg) then ListBox_BlendSvg.ItemIndex := -1;
  320. if not (Sender = ListBox_BlendKrita) then ListBox_BlendKrita.ItemIndex := -1;
  321. if not (Sender = ListBox_BlendOther) then ListBox_BlendOther.ItemIndex := -1;
  322. end;
  323. end;
  324. end;
  325. procedure TFBlendOp.ListBox_DrawBlendItem(Control: TWinControl; Index: Integer;
  326. ARect: TRect; State: TOwnerDrawState);
  327. var
  328. background,preview,over: TBGRABitmap;
  329. w,h: integer;
  330. BlendStr: string;
  331. fx: TBGRATextEffect;
  332. begin
  333. {$IFDEF LINUX}
  334. ARect.Right := ARect.Left+Control.Width-FListBoxInternalMargin;
  335. {$ENDIF}
  336. if (ListBox_PatternUnder.ItemIndex <> -1) and
  337. (ListBox_PatternOver.ItemIndex <> -1) and
  338. (Index <> -1) then
  339. begin
  340. if (ARect.Right <= ARect.Left) or (ARect.Bottom <= ARect.Top) then exit;
  341. BlendStr := (Control as TListBox).Items[Index];
  342. w := ARect.Right-ARect.Left;
  343. h := ARect.Bottom-ARect.Top;
  344. background := TBGRABitmap.Create(w,h,ColorToBGRA(ColorToRGB(clBtnFace)));
  345. background.DrawCheckers(background.ClipRect, ImageCheckersColor1, ImageCheckersColor2);
  346. preview := GetPattern(w,h,ListBox_PatternUnder.Items[ListBox_PatternUnder.ItemIndex],False).Duplicate as TBGRABitmap;
  347. over := GetPattern(w,h,ListBox_PatternOver.Items[ListBox_PatternOver.ItemIndex],False);
  348. preview.BlendImageOver(0,0,over,StrToBlendOperation(BlendStr));
  349. background.PutImage(0,0,preview,dmDrawWithTransparency);
  350. preview.Free;
  351. if odSelected in State then DrawPatternHighlight(background);
  352. fx := TBGRATextEffect.Create(BlendStr,'Arial',Max(DoScaleY(12,OriginalDPI),h div 10),true);
  353. fx.DrawOutline(background,1,1,BGRABlack);
  354. fx.Draw(background,1,1,BGRAWhite);
  355. fx.Free;
  356. background.FontName := 'Arial';
  357. background.Draw((Control as TListBox).Canvas,ARect.Left,ARect.Top,True);
  358. background.Free;
  359. end;
  360. end;
  361. {$R *.lfm}
  362. end.