uresample.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UResample;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  7. StdCtrls, Spin, ComCtrls, BGRAVirtualScreen, LazPaintType, LCScaleDPI,
  8. uresourcestrings, BGRABitmap, uscripting;
  9. type
  10. { TFResample }
  11. TFResample = class(TForm)
  12. Button_OK: TButton;
  13. Button_Cancel: TButton;
  14. CheckBox_Ratio: TCheckBox;
  15. ComboBox_MUnit: TComboBox;
  16. ComboBox_Quality: TComboBox;
  17. Label_Quality: TLabel;
  18. Label_Width: TLabel;
  19. Label_Height: TLabel;
  20. SpinEdit_Width: TSpinEdit;
  21. SpinEdit_Height: TSpinEdit;
  22. ToolBar8: TToolBar;
  23. ToolButton23: TToolButton;
  24. vsPreview: TBGRAVirtualScreen;
  25. procedure Button_OKClick(Sender: TObject);
  26. procedure ComboBox_MUnitChange(Sender: TObject);
  27. procedure CheckBox_RatioChange(Sender: TObject);
  28. procedure FormCreate(Sender: TObject);
  29. procedure FormShow(Sender: TObject);
  30. procedure SpinEdit_HeightChange(Sender: TObject);
  31. procedure SpinEdit_WidthChange(Sender: TObject);
  32. procedure ToolButton23Click(Sender: TObject);
  33. procedure vsPreviewRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  34. private
  35. FLazPaintInstance: TLazPaintCustomInstance;
  36. FIgnoreInput: boolean;
  37. FLockedAspectRatio: single;
  38. FParameters: TVariableSet;
  39. FMUnit: integer;
  40. procedure SetLazPaintInstance(AValue: TLazPaintCustomInstance);
  41. procedure ThemeChanged(Sender: TObject);
  42. procedure UpdatePreview;
  43. procedure ComputeAspectRatio;
  44. function NewHeight: integer;
  45. function NewWidth: integer;
  46. public
  47. destructor Destroy; override;
  48. property LazPaintInstance: TLazPaintCustomInstance read FLazPaintInstance write SetLazPaintInstance;
  49. end;
  50. function ShowResampleDialog(Instance: TLazPaintCustomInstance; AParameters: TVariableSet):boolean;
  51. implementation
  52. uses ugraph, BGRABitmapTypes, umac, uimage;
  53. { TFResample }
  54. function ShowResampleDialog(Instance: TLazPaintCustomInstance; AParameters: TVariableSet):boolean;
  55. var
  56. Resample: TFResample;
  57. topmostInfo: TTopMostInfo;
  58. begin
  59. result := false;
  60. Resample := nil;
  61. topmostInfo := instance.HideTopmost;
  62. try
  63. Resample:= TFResample.create(nil);
  64. Resample.LazPaintInstance := Instance;
  65. Resample.FParameters := AParameters;
  66. result:= (Resample.ShowModal = mrOk);
  67. except
  68. on ex:Exception do
  69. Instance.ShowError('ShowResampleDialog',ex.Message);
  70. end;
  71. instance.ShowTopmost(topmostInfo);
  72. Resample.free;
  73. end;
  74. procedure TFResample.FormCreate(Sender: TObject);
  75. begin
  76. FIgnoreInput := true;
  77. ScaleControl(Self,OriginalDPI);
  78. vsPreview.BitmapAutoScale:= false;
  79. SpinEdit_Width.MaxValue := MaxImageWidth;
  80. SpinEdit_Height.MaxValue := MaxImageHeight;
  81. CheckOKCancelBtns(Button_OK,Button_Cancel);
  82. CheckSpinEdit(SpinEdit_Width);
  83. CheckSpinEdit(SpinEdit_Height);
  84. with ComboBox_Quality.Items do begin
  85. Add(rsFast);
  86. Add(rsLinear);
  87. Add(rsHalfCosine);
  88. Add(rsCosine);
  89. Add(rsMitchell);
  90. Add(rsSpline);
  91. Add(rsBestQuality);
  92. Add(StringReplace(rsLanczos,'%1','2',[]));
  93. Add(StringReplace(rsLanczos,'%1','3',[]));
  94. Add(StringReplace(rsLanczos,'%1','4',[]));
  95. end;
  96. with ComboBox_MUnit do begin
  97. Clear;
  98. Items.Add (rsPx) ;
  99. Items.Add (rsPercent);
  100. end;
  101. FIgnoreInput := false;
  102. end;
  103. procedure TFResample.FormShow(Sender: TObject);
  104. var idxQuality: integer;
  105. begin
  106. ToolBar8.Images := LazPaintInstance.Icons[DoScaleY(16,OriginalDPI)];
  107. FIgnoreInput := true;
  108. idxQuality := LazPaintInstance.Config.DefaultResampleQuality;
  109. if (idxQuality >= 0) and (idxQuality < ComboBox_Quality.Items.Count) then
  110. ComboBox_Quality.ItemIndex := idxQuality else
  111. ComboBox_Quality.ItemIndex := 0;
  112. CheckBox_Ratio.Checked := LazPaintInstance.Config.DefaultResampleKeepAspectRatio;
  113. FMUnit:=0;
  114. ComboBox_MUnit.ItemIndex:= FMUnit;
  115. SpinEdit_Width.Value := LazPaintInstance.Image.Width;
  116. SpinEdit_Height.Value := LazPaintInstance.Image.Height;
  117. if LazPaintInstance.Image.Height = 0 then
  118. FLockedAspectRatio:= 1
  119. else
  120. FLockedAspectRatio:= LazPaintInstance.Image.Width/LazPaintInstance.Image.Height;
  121. UpdatePreview;
  122. FIgnoreInput := false;
  123. end;
  124. procedure TFResample.SpinEdit_HeightChange(Sender: TObject);
  125. begin
  126. if FIgnoreInput then exit;
  127. FIgnoreInput:= true;
  128. case FMUnit of
  129. 0: if CheckBox_Ratio.Checked and (LazPaintInstance.Image.Height <> 0) then
  130. SpinEdit_Width.Value := round(SpinEdit_Height.Value*FLockedAspectRatio);
  131. 1: if CheckBox_Ratio.Checked and (LazPaintInstance.Image.Height <> 0) and (LazPaintInstance.Image.Width <> 0) then
  132. SpinEdit_Width.Value := round(NewHeight*FLockedAspectRatio/LazPaintInstance.Image.Width*100);
  133. end;
  134. FIgnoreInput:= false;
  135. UpdatePreview;
  136. end;
  137. procedure TFResample.SpinEdit_WidthChange(Sender: TObject);
  138. begin
  139. if FIgnoreInput then exit;
  140. FIgnoreInput:= true;
  141. case FMUnit of
  142. 0: if CheckBox_Ratio.Checked and (LazPaintInstance.Image.Width <> 0) then
  143. SpinEdit_Height.Value := round(SpinEdit_Width.Value/FLockedAspectRatio);
  144. 1: if CheckBox_Ratio.Checked and (LazPaintInstance.Image.Width <> 0) and (LazPaintInstance.Image.Height <> 0) then
  145. SpinEdit_Height.Value:= round(NewWidth/FLockedAspectRatio/LazPaintInstance.Image.Height*100);
  146. end;
  147. FIgnoreInput:= false;
  148. UpdatePreview;
  149. end;
  150. procedure TFResample.ToolButton23Click(Sender: TObject);
  151. var tx,ty: integer;
  152. begin
  153. if FLockedAspectRatio <> 0 then
  154. begin
  155. FIgnoreInput := true;
  156. tx := SpinEdit_Width.Value;
  157. ty := SpinEdit_Height.Value;
  158. SpinEdit_Width.Value := ty;
  159. SpinEdit_Height.Value := tx;
  160. ComputeAspectRatio;
  161. UpdatePreview;
  162. FIgnoreInput := false;
  163. end;
  164. end;
  165. function TFResample.NewWidth: integer;
  166. begin
  167. case FMUnit of
  168. 0: Result:=SpinEdit_Width.Value;
  169. 1: Result:=round(SpinEdit_Width.Value*LazPaintInstance.Image.Width/100);
  170. else
  171. Result:=SpinEdit_Width.Value;
  172. end;
  173. if result <= 1 then result := 1;
  174. end;
  175. destructor TFResample.Destroy;
  176. begin
  177. if Assigned(FLazPaintInstance) then
  178. FLazPaintInstance.RegisterThemeListener(@ThemeChanged, false);
  179. inherited Destroy;
  180. end;
  181. function TFResample.NewHeight: integer;
  182. begin
  183. case FMUnit of
  184. 0: Result:=SpinEdit_Height.Value;
  185. 1: Result:=round(SpinEdit_Height.Value*LazPaintInstance.Image.Height/100);
  186. else
  187. Result:=SpinEdit_Height.Value;
  188. end;
  189. if result <= 1 then result := 1;
  190. end;
  191. procedure TFResample.vsPreviewRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  192. var
  193. tx,ty,px,py,x,y,px2,py2,x2,y2: NativeInt;
  194. ratio,zoom,scaling: double;
  195. deltaX: NativeInt;
  196. begin
  197. scaling := DoScaleX(60, OriginalDPI)/60 * GetCanvasScaleFactor;
  198. deltaX := Bitmap.Width-Bitmap.Height;
  199. if deltaX < 0 then deltaX := 0;
  200. tx := NewWidth;
  201. ty := NewHeight;
  202. if LazPaintInstance.Image.Width > tx then tx := LazPaintInstance.Image.Width;
  203. if LazPaintInstance.Image.Height > ty then ty := LazPaintInstance.Image.Height;
  204. if (tx > 0) and (ty > 0) then
  205. begin
  206. ratio := tx/ty;
  207. if (Bitmap.Width-deltaX)/ratio < Bitmap.Height then
  208. zoom := (Bitmap.Width-deltaX)/tx
  209. else
  210. zoom := Bitmap.height/ty;
  211. px := round(NewWidth*zoom);
  212. py := round(NewHeight*zoom);
  213. if px < 1 then px := 1;
  214. if py < 1 then py := 1;
  215. x := Bitmap.Width-px;
  216. y := (Bitmap.height-py) div 2;
  217. px2 := round(LazPaintInstance.Image.Width*zoom);
  218. py2 := round(LazPaintInstance.Image.Height*zoom);
  219. x2 := 0;
  220. y2 := (Bitmap.height-py2) div 2;
  221. if (px = 1) or (py = 1) then
  222. Bitmap.FillRect(x,y,x+px,y+py,BGRA(0,0,0,192),dmDrawWithTransparency)
  223. else
  224. begin
  225. Bitmap.Rectangle(x,y,x+px,y+py,BGRA(0,0,0,192),dmDrawWithTransparency);
  226. DrawCheckers(Bitmap, rect(x+1,y+1,x+px-1,y+py-1), scaling);
  227. end;
  228. Bitmap.StretchPutImage(rect(x,y,x+px,y+py),LazPaintInstance.Image.RenderedImage,dmDrawWithTransparency);
  229. if (px2 = 1) or (py2 = 1) then
  230. Bitmap.DrawLineAntialias(x2,y2,x2+px2-1,y2+py2-1,BGRA(0,0,0,160),BGRA(255,255,255,160),round(scaling),True)
  231. else
  232. Bitmap.DrawPolyLineAntialias([Point(x2,y2),Point(x2+px2-1,y2),Point(x2+px2-1,y2+py2-1),Point(x2,y2+py2-1),Point(x2,y2)],BGRA(0,0,0,160),BGRA(255,255,255,160),round(scaling),False);
  233. Bitmap.StretchPutImage(rect(x2,y2,x2+px2,y2+py2),LazPaintInstance.Image.RenderedImage,dmDrawWithTransparency,48);
  234. end;
  235. end;
  236. procedure TFResample.UpdatePreview;
  237. begin
  238. vsPreview.RedrawBitmap;
  239. end;
  240. procedure TFResample.SetLazPaintInstance(AValue: TLazPaintCustomInstance);
  241. begin
  242. if FLazPaintInstance=AValue then Exit;
  243. if Assigned(FLazPaintInstance) then
  244. FLazPaintInstance.RegisterThemeListener(@ThemeChanged, false);
  245. FLazPaintInstance:=AValue;
  246. if Assigned(FLazPaintInstance) then
  247. FLazPaintInstance.RegisterThemeListener(@ThemeChanged, true);
  248. end;
  249. procedure TFResample.ThemeChanged(Sender: TObject);
  250. begin
  251. vsPreview.DiscardBitmap;
  252. end;
  253. procedure TFResample.ComputeAspectRatio;
  254. begin
  255. if (NewWidth >= 1) and (NewHeight >= 1) then
  256. FLockedAspectRatio:= NewWidth/NewHeight;
  257. end;
  258. procedure TFResample.Button_OKClick(Sender: TObject);
  259. var filter: TResampleFilter;
  260. begin
  261. if ((FMUnit=0) and (SpinEdit_Width.Value = LazPaintInstance.Image.Width) and
  262. (SpinEdit_Height.Value = LazPaintInstance.Image.Height))
  263. or
  264. ((FMUnit=1) and (SpinEdit_Width.Value = 100) and
  265. (SpinEdit_Height.Value = 100))
  266. then
  267. ModalResult := mrCancel
  268. else
  269. begin
  270. Button_OK.Enabled := false;
  271. filter := CaptionToResampleFilter(ComboBox_Quality.Text);
  272. FParameters.Integers['Width']:=NewWidth;
  273. FParameters.Integers['Height']:=NewHeight;
  274. if not FParameters.IsDefined('Quality') then LazPaintInstance.Config.SetDefaultResampleQuality(ComboBox_Quality.ItemIndex);
  275. FParameters.Strings['Quality'] := ResampleFilterStr[filter];
  276. LazPaintInstance.Config.SetDefaultResampleKeepAspectRatio(CheckBox_Ratio.Checked);
  277. LazPaintInstance.Image.Resample(NewWidth, NewHeight,filter);
  278. ModalResult := mrOK;
  279. end;
  280. end;
  281. procedure TFResample.ComboBox_MUnitChange(Sender: TObject);
  282. begin
  283. if FMUnit= ComboBox_MUnit.ItemIndex then exit;
  284. FMUnit:= ComboBox_MUnit.ItemIndex;
  285. FIgnoreInput:=True;
  286. case FMUnit of
  287. 0: begin //pixels
  288. SpinEdit_Width.Value:= round (LazPaintInstance.Image.Width*SpinEdit_Width.Value/100);
  289. SpinEdit_Height.Value:= round (LazPaintInstance.Image.Height*SpinEdit_Height.Value/100);
  290. end;
  291. 1: begin //percent
  292. SpinEdit_Width.Value:= round (SpinEdit_Width.Value/ LazPaintInstance.Image.Width*100);
  293. SpinEdit_Height.Value:= round (SpinEdit_Height.Value/ LazPaintInstance.Image.Height*100);
  294. end;
  295. end;
  296. FIgnoreInput:=False;
  297. end;
  298. procedure TFResample.CheckBox_RatioChange(Sender: TObject);
  299. begin
  300. ComputeAspectRatio;
  301. end;
  302. {$R *.lfm}
  303. end.