2
0

ufilterfunction.pas 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UFilterFunction;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  7. StdCtrls, ExtCtrls, ComCtrls, fpexprpars, UFilterConnector, BGRABitmap,
  8. BGRABitmapTypes, UScripting;
  9. const
  10. StatsName: array[1..10] of string =
  11. ('red','green','blue','alpha','hue','saturation','lightness','L','a','b');
  12. type
  13. TLabABitmap = specialize TGenericUniversalBitmap<TLabA,TLabAColorspace>;
  14. { TFFilterFunction }
  15. TFFilterFunction = class(TForm)
  16. Button_Cancel: TButton;
  17. Button_OK: TButton;
  18. CheckBox_Preview: TCheckBox;
  19. CheckBox_Gamma: TCheckBox;
  20. CheckBox_GSBA: TCheckBox;
  21. Edit_Alpha: TEdit;
  22. Edit_Blue: TEdit;
  23. Edit_Green: TEdit;
  24. Edit_Hue: TEdit;
  25. Edit_L: TEdit;
  26. Edit_Lightness: TEdit;
  27. Edit_b: TEdit;
  28. Edit_Red: TEdit;
  29. Edit_Saturation: TEdit;
  30. Edit_a: TEdit;
  31. Label_BlueEquals: TLabel;
  32. Label_GreenEquals: TLabel;
  33. Label_HueEquals: TLabel;
  34. Label_bEquals: TLabel;
  35. Label_LEquals: TLabel;
  36. Label_LightnessEquals: TLabel;
  37. Label_RedEquals: TLabel;
  38. Label_aEquals: TLabel;
  39. Label_SaturationEquals: TLabel;
  40. Label_Variables: TLabel;
  41. Label_AlphaEquals: TLabel;
  42. PageControl_Color: TPageControl;
  43. PanelLab: TPanel;
  44. PanelLabelLab: TPanel;
  45. PanelLabelRGB: TPanel;
  46. PanelLabelHSL: TPanel;
  47. PanelRGB: TPanel;
  48. PanelHSL: TPanel;
  49. TabSheet_Lab: TTabSheet;
  50. TabSheet_RGB: TTabSheet;
  51. TabSheet_HSL: TTabSheet;
  52. Timer1: TTimer;
  53. Timer_AdjustVerticalSize: TTimer;
  54. procedure Button_CancelClick(Sender: TObject);
  55. procedure Button_OKClick(Sender: TObject);
  56. procedure CheckBox_GammaChange(Sender: TObject);
  57. procedure CheckBox_GSBAChange(Sender: TObject);
  58. procedure CheckBox_PreviewChange(Sender: TObject);
  59. procedure Edit_aChange(Sender: TObject);
  60. procedure Edit_AlphaChange(Sender: TObject);
  61. procedure Edit_bChange(Sender: TObject);
  62. procedure Edit_BlueChange(Sender: TObject);
  63. procedure Edit_GreenChange(Sender: TObject);
  64. procedure Edit_HueChange(Sender: TObject);
  65. procedure Edit_LChange(Sender: TObject);
  66. procedure Edit_LightnessChange(Sender: TObject);
  67. procedure Edit_RedChange(Sender: TObject);
  68. procedure Edit_SaturationChange(Sender: TObject);
  69. procedure FormCreate(Sender: TObject);
  70. procedure FormDestroy(Sender: TObject);
  71. procedure FormShow(Sender: TObject);
  72. procedure PageControl_ColorChange(Sender: TObject);
  73. procedure Timer1Timer(Sender: TObject);
  74. procedure Timer_AdjustVerticalSizeTimer(Sender: TObject);
  75. private
  76. { private declarations }
  77. FRedExpr, FGreenExpr, FBlueExpr, FAlphaExpr,
  78. FHueExpr, FSaturationExpr, FLightnessExpr,
  79. FLExpr, FaExpr, FbExpr: TFPExpressionParser;
  80. FRedError, FGreenError, FBlueError, FAlphaError,
  81. FHueError, FSaturationError, FLightnessError,
  82. FLError, FaError, FbError: boolean;
  83. FComputing,
  84. FComputationRestarted: boolean;
  85. FSourceAsLab: TLabABitmap;
  86. FComputedImage: TBGRABitmap;
  87. FComputedLines: integer;
  88. FFilterConnector: TFilterConnector;
  89. FInitializing: boolean;
  90. FStats: array[low(StatsName)..high(StatsName)] of record
  91. min,max,sum,avg: single;
  92. count: integer;
  93. computed: boolean;
  94. end;
  95. procedure DisplayComputedImage;
  96. procedure UpdateExpr(AExpr: TFPExpressionParser; AEdit: TEdit;
  97. var AError: boolean);
  98. procedure InitParams;
  99. procedure PreviewNeeded;
  100. function CreateExpr: TFPExpressionParser;
  101. function ExprResultToFloat(const AResult: TFPExpressionResult): single;
  102. procedure ExprFunctionMin_Call(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
  103. procedure ExprFunctionMax_Call(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
  104. procedure StatsNotComputed(AFrom,ATo: integer);
  105. procedure NeedStats(AStatIndex: integer);
  106. function ReplaceStats(AExpr: string): string;
  107. public
  108. { public declarations }
  109. end;
  110. function ShowFilterFunctionDlg(AFilterConnector: TObject): TScriptResult;
  111. implementation
  112. uses UMac, LazPaintType, UResourceStrings, math;
  113. function ShowFilterFunctionDlg(AFilterConnector: TObject): TScriptResult;
  114. var
  115. FFilterFunction: TFFilterFunction;
  116. begin
  117. FFilterFunction:= TFFilterFunction.create(nil);
  118. FFilterFunction.FFilterConnector := AFilterConnector as TFilterConnector;
  119. try
  120. if FFilterFunction.FFilterConnector.ActiveLayer <> nil then
  121. begin
  122. if Assigned(FFilterFunction.FFilterConnector.Parameters) and
  123. FFilterFunction.FFilterConnector.Parameters.Booleans['Validate'] then
  124. begin
  125. FFilterFunction.InitParams;
  126. FFilterFunction.PreviewNeeded;
  127. while FFilterFunction.FComputing do FFilterFunction.Timer1Timer(FFilterFunction);
  128. FFilterFunction.FFilterConnector.ValidateAction;
  129. result := srOk;
  130. end else
  131. begin
  132. if FFilterFunction.showModal = mrOk then result := srOk
  133. else result:= srCancelledByUser;
  134. end;
  135. end
  136. else
  137. result := srException;
  138. finally
  139. FFilterFunction.free;
  140. end;
  141. end;
  142. function LocateIdentifier(AExpr: string; AVar: string): integer;
  143. var i: integer;
  144. inStr: boolean;
  145. begin
  146. result := 0;
  147. if (AExpr = '') or (AVar = '') then exit;
  148. inStr := false;
  149. for i := 1 to length(AExpr)-length(AVar)+1 do
  150. if AExpr[i] = '''' then
  151. begin
  152. inStr := not inStr
  153. end else
  154. if (UpCase(AExpr[i]) = UpCase(AVar[1])) and not inStr then
  155. begin
  156. if (i = 1) or not (AExpr[i-1] in['a'..'z','A'..'Z','_','0'..'9']) then
  157. begin
  158. if (i+length(AVar) = length(AExpr)+1) or
  159. not (AExpr[i+length(AVar)] in ['a'..'z','A'..'Z','_','0'..'9']) then
  160. begin
  161. if CompareText(copy(AExpr,i,length(AVar)), AVar) = 0 then
  162. begin
  163. result := i;
  164. exit;
  165. end;
  166. end;
  167. end;
  168. end;
  169. end;
  170. function ContainsIdentifier(AExpr: string; AVar: string): boolean;
  171. begin
  172. result := LocateIdentifier(AExpr, AVar) <> 0;
  173. end;
  174. function ReplaceIdentifier(AExpr: string; AVar, ANewVar: string): string;
  175. var
  176. idx: Integer;
  177. begin
  178. if LowerCase(ANewVar).Contains(LowerCase(AVar)) then exit;
  179. result := AExpr;
  180. repeat
  181. idx := LocateIdentifier(result, AVar);
  182. if idx = 0 then break;
  183. delete(result, idx, length(AVar));
  184. insert(ANewVar, result, idx);
  185. until false;
  186. end;
  187. { TFFilterFunction }
  188. procedure TFFilterFunction.FormCreate(Sender: TObject);
  189. begin
  190. CheckOKCancelBtns(Button_OK,Button_Cancel);
  191. FRedExpr := CreateExpr;
  192. FRedError:= false;
  193. FGreenExpr := CreateExpr;
  194. FGreenError := false;
  195. FBlueExpr := CreateExpr;
  196. FBlueError := false;
  197. FAlphaExpr := CreateExpr;
  198. FAlphaError := false;
  199. FHueExpr := CreateExpr;
  200. FHueError := false;
  201. FSaturationExpr := CreateExpr;
  202. FSaturationError := false;
  203. FLightnessExpr := CreateExpr;
  204. FLightnessError := false;
  205. FLExpr := CreateExpr;
  206. FLError := false;
  207. FaExpr := CreateExpr;
  208. FaError := false;
  209. FbExpr := CreateExpr;
  210. FbError := false;
  211. Label_RedEquals.Caption := 'red (0..1) = ';
  212. Label_GreenEquals.Caption := 'green (0..1) = ';
  213. Label_BlueEquals.Caption := 'blue (0..1) = ';
  214. Label_AlphaEquals.Caption := 'alpha (0..1) = ';
  215. Label_HueEquals.Caption := 'hue (0..1) = ';
  216. Label_SaturationEquals.Caption := 'saturation (0..1) = ';
  217. Label_LightnessEquals.Caption := 'lightness (0..1) = ';
  218. Label_LEquals.Caption := 'L (0..1) = ';
  219. Label_aEquals.Caption := 'a (-1..1) = ';
  220. Label_bEquals.Caption := 'b (-1..1) = ';
  221. Label_Variables.Caption := Label_Variables.Caption+' x,y,width,height,random,min,max,avg';
  222. FComputedImage := nil;
  223. StatsNotComputed(low(FStats), high(FStats));
  224. PageControl_Color.ActivePage := TabSheet_RGB;
  225. end;
  226. procedure TFFilterFunction.FormDestroy(Sender: TObject);
  227. begin
  228. FSourceAsLab.Free;
  229. FComputedImage.Free;
  230. FRedExpr.Free;
  231. FGreenExpr.Free;
  232. FBlueExpr.Free;
  233. FAlphaExpr.Free;
  234. FHueExpr.Free;
  235. FSaturationExpr.Free;
  236. FLightnessExpr.Free;
  237. FLExpr.Free;
  238. FaExpr.Free;
  239. FbExpr.Free;
  240. end;
  241. procedure TFFilterFunction.FormShow(Sender: TObject);
  242. begin
  243. InitParams;
  244. PreviewNeeded;
  245. Timer_AdjustVerticalSize.Interval:= 50;
  246. Timer_AdjustVerticalSize.Enabled := true;
  247. end;
  248. procedure TFFilterFunction.PageControl_ColorChange(Sender: TObject);
  249. begin
  250. if not FInitializing then PreviewNeeded;
  251. end;
  252. procedure TFFilterFunction.Timer1Timer(Sender: TObject);
  253. const
  254. TimeGrain = 150/1000/60/60/24;
  255. oneOver255 = 1/255;
  256. oneOver65535 = 1/65535;
  257. oneOver65536 = 1/65536;
  258. type
  259. TExprValues = record
  260. Red, Green, Blue, Alpha,
  261. X, Y, Random,
  262. Hue, Saturation, Lightness,
  263. L, a, b: TExprFloat;
  264. end;
  265. TEvaluateFunc = function: TFPExpressionResult of object;
  266. TExprVariables = record
  267. Evaluate: TEvaluateFunc;
  268. XValue, YValue,
  269. RedValue, GreenValue, BlueValue, AlphaValue,
  270. HueValue, SaturationValue, LightnessValue,
  271. LValue, aValue, bValue,
  272. RandomValue: TFPExprIdentifierDef;
  273. XYUsed, RGBUsed, AlphaUsed, HSLUsed, LabUsed, RandomUsed: boolean;
  274. IsCopySrc: boolean;
  275. CopyOffset: PtrInt;
  276. IsConstant: boolean;
  277. ConstantValue: Word;
  278. ConstantValueF: single;
  279. end;
  280. var
  281. values: TExprValues;
  282. rgbUsedByAny, hslUsedByAny, labUsedByAny, xyUsedByAny: boolean;
  283. rgbUsedInExpr, hslUsedInExpr, xyUsedInExpr: boolean;
  284. src: packed record
  285. red,green,blue,alpha: word;
  286. L,a,b,filler: word;
  287. x,y: word;
  288. case boolean of
  289. false: (hslaValue: THSLAPixel);
  290. true: (gsbaValue: TGSBAPixel);
  291. end;
  292. procedure InitUsedValues(var AVars: TExprVariables); inline;
  293. begin
  294. if AVars.XYUsed then
  295. begin
  296. AVars.XValue.AsFloat := values.x;
  297. AVars.YValue.AsFloat := values.y;
  298. end;
  299. if AVars.RGBUsed then
  300. begin
  301. AVars.RedValue.AsFloat := values.Red;
  302. AVars.GreenValue.AsFloat := values.Green;
  303. AVars.BlueValue.AsFloat := values.Blue;
  304. end;
  305. AVars.AlphaValue.AsFloat := values.Alpha;
  306. if AVars.HSLUsed then
  307. begin
  308. AVars.HueValue.AsFloat := values.Hue;
  309. AVars.SaturationValue.AsFloat := values.Saturation;
  310. AVars.LightnessValue.AsFloat := values.Lightness;
  311. end;
  312. if AVars.LabUsed then
  313. begin
  314. AVars.LValue.AsFloat := values.L;
  315. AVars.aValue.AsFloat := values.a;
  316. AVars.bValue.AsFloat := values.b;
  317. end;
  318. if AVars.RandomUsed then AVars.RandomValue.AsFloat := Random;
  319. end;
  320. function ComputeExpr(var AVars: TExprVariables; AFactor: integer = 65535): integer; inline;
  321. var {%H-}code: integer;
  322. floatValue: single;
  323. begin
  324. if AVars.IsCopySrc then exit((PWord(@src) + AVars.CopyOffset)^) else
  325. if AVars.IsConstant then exit(AVars.ConstantValue);
  326. InitUsedValues(AVars);
  327. with AVars.Evaluate do
  328. begin
  329. case ResultType of
  330. rtFloat: if resFloat < 0 then result := 0 else
  331. if resFloat > 1 then result := AFactor else
  332. result := round(ResFloat*AFactor);
  333. rtInteger: if ResInteger <= 0 then result := 0 else result := AFactor;
  334. rtBoolean: if ResBoolean then result := AFactor else result := 0;
  335. rtDateTime: result := 0;
  336. rtString: begin
  337. val(ResString, floatValue, code);
  338. if floatValue < 0 then result := 0 else
  339. if floatValue > 1 then result := AFactor else
  340. result := round(floatValue*AFactor);
  341. end;
  342. else result := 0;
  343. end;
  344. end;
  345. if result < 0 then result := 0;
  346. if result > 65535 then dec(result, 65536);
  347. end;
  348. function ComputeExprF(var AVars: TExprVariables): single; inline;
  349. var {%H-}code: integer;
  350. begin
  351. if AVars.IsCopySrc then
  352. begin
  353. case AVars.CopyOffset of
  354. 4: result := values.L;
  355. 5: result := values.a;
  356. 6: result := values.b;
  357. else exit((PWord(@src) + AVars.CopyOffset)^ / 65535);
  358. end;
  359. end else
  360. if AVars.IsConstant then exit(AVars.ConstantValueF);
  361. InitUsedValues(AVars);
  362. with AVars.Evaluate do
  363. begin
  364. case ResultType of
  365. rtFloat: result := ResFloat;
  366. rtInteger: result := ResInteger;
  367. rtBoolean: if ResBoolean then result := 1 else result := 0;
  368. rtDateTime: result := 0;
  369. rtString: val(ResString, result, code);
  370. else result := 0;
  371. end;
  372. end;
  373. end;
  374. procedure PrepareXY(AExpr: TFPExpressionParser; out AVars: TExprVariables);
  375. var exprComp: string;
  376. i: Integer;
  377. begin
  378. with AVars do
  379. begin
  380. Evaluate := @AExpr.Evaluate;
  381. XYUsed := ContainsIdentifier(AExpr.Expression,'x') or
  382. ContainsIdentifier(AExpr.Expression,'y');
  383. RGBUsed := (ContainsIdentifier(AExpr.Expression,'red') or
  384. ContainsIdentifier(AExpr.Expression,'green') or
  385. ContainsIdentifier(AExpr.Expression,'blue'));
  386. HSLUsed := (ContainsIdentifier(AExpr.Expression,'hue') or
  387. ContainsIdentifier(AExpr.Expression,'saturation') or
  388. ContainsIdentifier(AExpr.Expression,'lightness'));
  389. LabUsed := (ContainsIdentifier(AExpr.Expression,'L') or
  390. ContainsIdentifier(AExpr.Expression,'a') or
  391. ContainsIdentifier(AExpr.Expression,'b'));
  392. RandomUsed:= ContainsIdentifier(AExpr.Expression,'random');
  393. AlphaUsed:= ContainsIdentifier(AExpr.Expression,'alpha');
  394. XValue := AExpr.IdentifierByName('x');
  395. YValue:= AExpr.IdentifierByName('y');
  396. RedValue:= AExpr.IdentifierByName('red');
  397. GreenValue:= AExpr.IdentifierByName('green');
  398. BlueValue:= AExpr.IdentifierByName('blue');
  399. AlphaValue:= AExpr.IdentifierByName('alpha');
  400. HueValue:= AExpr.IdentifierByName('hue');
  401. SaturationValue:= AExpr.IdentifierByName('saturation');
  402. LightnessValue:= AExpr.IdentifierByName('lightness');
  403. LValue:= AExpr.IdentifierByName('L');
  404. aValue:= AExpr.IdentifierByName('a');
  405. bValue:= AExpr.IdentifierByName('b');
  406. RandomValue:= AExpr.IdentifierByName('random');
  407. AExpr.IdentifierByName('width').AsInteger := FFilterConnector.BackupLayer.Width;
  408. AExpr.IdentifierByName('height').AsInteger := FFilterConnector.BackupLayer.Height;
  409. IsCopySrc:= false;
  410. IsConstant := false;
  411. for i := low(StatsName) to high(StatsName) do
  412. if ContainsIdentifier(AExpr.Expression, 'min_'+StatsName[i]) or
  413. ContainsIdentifier(AExpr.Expression, 'max_'+StatsName[i]) or
  414. ContainsIdentifier(AExpr.Expression, 'avg_'+StatsName[i]) then
  415. begin
  416. NeedStats(i);
  417. AExpr.IdentifierByName('min_'+StatsName[i]).AsFloat := FStats[i].min;
  418. AExpr.IdentifierByName('max_'+StatsName[i]).AsFloat := FStats[i].max;
  419. AExpr.IdentifierByName('avg_'+StatsName[i]).AsFloat := FStats[i].avg;
  420. end;
  421. if not HSLUsed and not RGBUsed and not LabUsed and not XYUsed and not RandomUsed and not AlphaUsed then
  422. begin
  423. ConstantValue := ComputeExpr(AVars);
  424. ConstantValueF := ComputeExprF(AVars);
  425. IsConstant := true; //set flag after computing value
  426. end else
  427. begin
  428. ConstantValue := 0;
  429. ConstantValueF := 0;
  430. IsConstant := false;
  431. end;
  432. exprComp := LowerCase(trim(AExpr.Expression));
  433. CopyOffset:= 0;
  434. IsCopySrc:= true;
  435. case exprComp of
  436. 'red': copyOffset := 0;
  437. 'green': copyOffset := 1;
  438. 'blue': copyOffset := 2;
  439. 'alpha': copyOffset := 3;
  440. 'l': copyOffset := 4;
  441. 'a': copyOffset := 5;
  442. 'b': copyOffset := 6;
  443. 'x': copyOffset := 8;
  444. 'y': copyOffset := 9;
  445. 'hue': copyOffset := 10;
  446. 'saturation': copyOffset := 11;
  447. 'lightness': copyOffset := 12;
  448. else IsCopySrc:= false;
  449. end;
  450. if RGBUsed then rgbUsedByAny:= true;
  451. if HSLUsed then hslUsedByAny:= true;
  452. if LabUsed then labUsedByAny:= true;
  453. if XYUsed then xyUsedByAny:= true;
  454. if RGBUsed and not IsCopySrc then rgbUsedInExpr:= true;
  455. if HSLUsed and not IsCopySrc then hslUsedInExpr:= true;
  456. if XYUsed and not IsCopySrc then xyUsedInExpr:= true;
  457. end;
  458. end;
  459. var PrevDate: TDateTime;
  460. x,y,w,h,xcount: integer;
  461. pdest,psrc: PBGRAPixel;
  462. psrcLab: PLabA;
  463. RedVars, GreenVars, BlueVars, AlphaVars,
  464. HueVars, SaturationVars, LightnessVars,
  465. LVars, aVars, bVars: TExprVariables;
  466. prevComputedLines: integer;
  467. gsba,rgbMode,hslMode,labMode,gammaCorr: boolean;
  468. labValue: TLabA;
  469. converter: TBridgedConversion;
  470. begin
  471. Timer1.Enabled:= false;
  472. if FComputing then
  473. begin
  474. if FComputedImage = nil then
  475. FComputedImage := TBGRABitmap.Create(FFilterConnector.BackupLayer.Width,FFilterConnector.BackupLayer.Height);
  476. if FComputationRestarted then
  477. begin
  478. FComputationRestarted := false;
  479. FComputedLines := FFilterConnector.WorkArea.Top;
  480. FFilterConnector.RestoreBackup;
  481. end;
  482. gsba := CheckBox_GSBA.Checked;
  483. gammaCorr := CheckBox_Gamma.Checked;
  484. PrevDate := Now;
  485. prevComputedLines:= FComputedLines;
  486. try
  487. rgbMode := PageControl_Color.ActivePage = TabSheet_RGB;
  488. hslMode := PageControl_Color.ActivePage = TabSheet_HSL;
  489. labMode := PageControl_Color.ActivePage = TabSheet_Lab;
  490. w := FFilterConnector.BackupLayer.Width;
  491. h := FFilterConnector.BackupLayer.Height;
  492. rgbUsedByAny := false;
  493. hslUsedByAny := false;
  494. labUsedByAny := false;
  495. xyUsedByAny := false;
  496. rgbUsedInExpr := false;
  497. hslUsedInExpr := false;
  498. xyUsedInExpr := false;
  499. fillchar({%H-}values, sizeOf(values), 0);
  500. if rgbMode then
  501. begin
  502. PrepareXY(FRedExpr, RedVars);
  503. PrepareXY(FGreenExpr, GreenVars);
  504. PrepareXY(FBlueExpr, BlueVars);
  505. end else
  506. if hslMode then
  507. begin
  508. PrepareXY(FHueExpr, HueVars);
  509. PrepareXY(FSaturationExpr, SaturationVars);
  510. PrepareXY(FLightnessExpr, LightnessVars);
  511. end else
  512. if labMode then
  513. begin
  514. PrepareXY(FLExpr, LVars);
  515. PrepareXY(FaExpr, aVars);
  516. PrepareXY(FbExpr, bVars);
  517. end
  518. else raise exception.Create('Unknown selected page');
  519. PrepareXY(FAlphaExpr, AlphaVars);
  520. if labUsedByAny then
  521. begin
  522. if Assigned(FSourceAsLab) and ((FSourceAsLab.Width <> FFilterConnector.WorkArea.Width)
  523. or (FSourceAsLab.Height <> FFilterConnector.WorkArea.Height)) then FreeAndNil(FSourceAsLab);
  524. if FSourceAsLab = nil then
  525. begin
  526. Screen.Cursor := crHourGlass;
  527. FSourceAsLab := TLabABitmap.Create(FFilterConnector.WorkArea.Width, FFilterConnector.WorkArea.Height, BGRAPixelTransparent);
  528. converter := FFilterConnector.BackupLayer.Colorspace.GetBridgedConversion(FSourceAsLab.Colorspace);
  529. for y := FFilterConnector.WorkArea.Top to FFilterConnector.WorkArea.Bottom-1 do
  530. converter.Convert(FFilterConnector.BackupLayer.ScanLine[y] + FFilterConnector.WorkArea.Left,
  531. FSourceAsLab.ScanLine[y - FFilterConnector.WorkArea.Top], FSourceAsLab.Width,
  532. sizeof(TBGRAPixel), sizeof(TLabA), nil);
  533. Screen.Cursor := crDefault;
  534. end;
  535. end;
  536. while FComputedLines < FFilterConnector.WorkArea.Bottom do
  537. begin
  538. y := FComputedLines;
  539. psrc := FFilterConnector.BackupLayer.ScanLine[y]+FFilterConnector.WorkArea.Left;
  540. if labUsedByAny then psrcLab:= PLabA(FSourceAsLab.GetPixelAddress(0, y - FFilterConnector.WorkArea.Top));
  541. pdest := FComputedImage.ScanLine[y]+FFilterConnector.WorkArea.Left;
  542. xcount := FFilterConnector.WorkArea.Right - FFilterConnector.WorkArea.Left;
  543. src.y := (y*65535+(h shr 1)) div h;
  544. if xyUsedInExpr then values.Y := y/h;
  545. try
  546. for x := 0 to xcount-1 do
  547. begin
  548. if xyUsedByAny then
  549. begin
  550. src.x := ((x + FFilterConnector.WorkArea.Left)*65535+(w shr 1)) div w;
  551. if xyUsedInExpr then values.X := src.x*oneOver65535;
  552. end;
  553. if rgbUsedByAny then
  554. begin
  555. if gammaCorr then
  556. begin
  557. src.red := GammaExpansionTab[psrc^.red];
  558. src.green := GammaExpansionTab[psrc^.green];
  559. src.blue := GammaExpansionTab[psrc^.blue];
  560. end else
  561. begin
  562. src.red := psrc^.red + (psrc^.red shl 8);
  563. src.green := psrc^.green + (psrc^.green shl 8);
  564. src.blue := psrc^.blue + (psrc^.blue shl 8);
  565. end;
  566. if rgbUsedInExpr then
  567. begin
  568. values.Red := src.red *oneOver65535;
  569. values.Green := src.green *oneOver65535;
  570. values.Blue := src.blue *oneOver65535;
  571. end;
  572. end;
  573. if hslUsedByAny then
  574. begin
  575. if gsba then src.gsbaValue := BGRAToGSBA(psrc^) else
  576. if gammaCorr then src.hslaValue := BGRAToHSLA(psrc^) else
  577. with psrc^.ToStdHSLA do
  578. begin
  579. src.hslaValue.hue := round(hue*(65536/360)) mod 65536;
  580. src.hslaValue.saturation := round(saturation*65535);
  581. src.hslaValue.lightness := round(lightness*65535);
  582. src.hslaValue.alpha := psrc^.alpha + (psrc^.alpha shl 8);
  583. end;
  584. if hslUsedInExpr then
  585. with src.hslaValue do
  586. begin
  587. values.Hue := hue*oneOver65536;
  588. values.Saturation := saturation*oneOver65535;
  589. values.Lightness := lightness*oneOver65535;
  590. end;
  591. end;
  592. if labUsedByAny then
  593. begin
  594. labValue := psrcLab^;
  595. inc(psrcLab);
  596. values.L := labValue.L/100;
  597. values.a := labValue.a/127;
  598. values.b := labValue.b/127;
  599. src.L := min(65535,max(0,round(values.L*65535)));
  600. src.a := min(65535,max(0,round(values.a*65535)));
  601. src.b := min(65535,max(0,round(values.b*65535)));
  602. end;
  603. src.alpha := psrc^.alpha + (psrc^.alpha shl 8);
  604. values.Alpha := psrc^.alpha * oneOver255;
  605. if rgbMode then
  606. begin
  607. if gammaCorr then
  608. begin
  609. pdest^.red := GammaCompressionTab[ComputeExpr(RedVars)];
  610. pdest^.green := GammaCompressionTab[ComputeExpr(GreenVars)];
  611. pdest^.blue := GammaCompressionTab[ComputeExpr(BlueVars)];
  612. pdest^.alpha := ComputeExpr(AlphaVars) shr 8;
  613. end else
  614. begin
  615. pdest^.red := ComputeExpr(RedVars) shr 8;
  616. pdest^.green := ComputeExpr(GreenVars) shr 8;
  617. pdest^.blue := ComputeExpr(BlueVars) shr 8;
  618. pdest^.alpha := ComputeExpr(AlphaVars) shr 8;
  619. end;
  620. inc(pdest);
  621. inc(psrc);
  622. end else
  623. if hslMode then
  624. begin
  625. if gsba then
  626. pdest^ := TGSBAPixel.New(
  627. ComputeExpr(HueVars, 65536),
  628. ComputeExpr(SaturationVars),
  629. ComputeExpr(LightnessVars),
  630. ComputeExpr(AlphaVars)) else
  631. if gammaCorr then
  632. pdest^ := THSLAPixel.New(
  633. ComputeExpr(HueVars, 65536),
  634. ComputeExpr(SaturationVars),
  635. ComputeExpr(LightnessVars),
  636. ComputeExpr(AlphaVars))
  637. else
  638. pdest^ := TStdHSLA.New(
  639. ComputeExpr(HueVars, 65536)*(360/65536),
  640. ComputeExpr(SaturationVars)*oneOver65535,
  641. ComputeExpr(LightnessVars)*oneOver65535,
  642. ComputeExpr(AlphaVars)*oneOver65535);
  643. inc(pdest);
  644. inc(psrc);
  645. end else
  646. begin
  647. pdest^ := TLabA.New(
  648. ComputeExprF(LVars)*100,
  649. ComputeExprF(aVars)*127,
  650. ComputeExprF(bVars)*127,
  651. ComputeExprF(AlphaVars));
  652. inc(pdest);
  653. inc(psrc);
  654. end;
  655. end;
  656. except
  657. on ex: exception do
  658. begin
  659. break;
  660. end;
  661. end;
  662. Inc(FComputedLines);
  663. if Now-PrevDate > TimeGrain then break;
  664. end;
  665. except
  666. on ex: exception do
  667. begin
  668. end;
  669. end;
  670. if CheckBox_Preview.Checked then
  671. FFilterConnector.PutImage(FComputedImage, rect(0,prevComputedLines,FComputedImage.Width,FComputedLines), True,False);
  672. if FComputedLines = FFilterConnector.WorkArea.Bottom then
  673. begin
  674. FComputing := false;
  675. Button_OK.Enabled := true;
  676. CheckBox_Preview.Enabled := true;
  677. end;
  678. Timer1.Interval := 15;
  679. Timer1.Enabled := True;
  680. end;
  681. end;
  682. procedure TFFilterFunction.Timer_AdjustVerticalSizeTimer(Sender: TObject);
  683. begin
  684. PanelLabelRGB.ChildSizing.TopBottomSpacing:= Edit_Red.Top + (Edit_Red.Height - Label_RedEquals.Height) div 2;
  685. PanelLabelRGB.ChildSizing.VerticalSpacing:= (Edit_Green.Top - Edit_Red.Top) - Label_RedEquals.Height;
  686. PanelLabelHSL.ChildSizing.TopBottomSpacing := PanelLabelRGB.ChildSizing.TopBottomSpacing;
  687. PanelLabelHSL.ChildSizing.VerticalSpacing := PanelLabelRGB.ChildSizing.VerticalSpacing;
  688. PanelLabelLab.ChildSizing.TopBottomSpacing := PanelLabelRGB.ChildSizing.TopBottomSpacing;
  689. PanelLabelLab.ChildSizing.VerticalSpacing := PanelLabelRGB.ChildSizing.VerticalSpacing;
  690. PageControl_Color.Height := PanelRGB.Top + Edit_Blue.Top + Edit_Blue.Height +
  691. TabSheet_RGB.ChildSizing.VerticalSpacing +
  692. CheckBox_Gamma.Height + TabSheet_RGB.ChildSizing.TopBottomSpacing +
  693. (PageControl_Color.Height - TabSheet_RGB.Height);
  694. ClientHeight := PageControl_Color.Top + PageControl_Color.Height +
  695. Label_Variables.Top + (ClientHeight - Edit_Alpha.Top);
  696. Timer_AdjustVerticalSize.Enabled := false;
  697. end;
  698. procedure TFFilterFunction.UpdateExpr(AExpr: TFPExpressionParser; AEdit: TEdit; var AError: boolean);
  699. begin
  700. if AExpr.Expression = Trim(AEdit.Text) then exit;
  701. try
  702. AExpr.Expression := ReplaceStats(Trim(AEdit.Text));
  703. AEdit.Color := clWindow;
  704. AEdit.Font.Color := clWindowText;
  705. AEdit.Hint := '';
  706. AEdit.ShowHint:= false;
  707. AError:= length(AExpr.Expression) = 0;
  708. except
  709. on ex:exception do
  710. begin
  711. AEdit.Color := clRed;
  712. AEdit.Font.Color := clWhite;
  713. AEdit.Hint := ex.Message;
  714. AEdit.ShowHint:= true;
  715. AError:= true;
  716. end;
  717. end;
  718. if not FInitializing then PreviewNeeded;
  719. end;
  720. procedure TFFilterFunction.PreviewNeeded;
  721. begin
  722. Timer1.Enabled := False;
  723. FComputing := false;
  724. Button_OK.Enabled := false;
  725. CheckBox_Preview.Enabled := false;
  726. FComputationRestarted := false;
  727. if not FAlphaError and not FGreenError and not FBlueError and not FRedError
  728. and not FHueError and not FSaturationError and not FLightnessError then
  729. begin
  730. FComputing := True;
  731. FComputedLines := 0;
  732. FComputationRestarted := true;
  733. Timer1.Interval := 200;
  734. Timer1.Enabled := True;
  735. end;
  736. end;
  737. procedure TFFilterFunction.InitParams;
  738. begin
  739. FInitializing:= true;
  740. Edit_Red.Text := 'red';
  741. Edit_Green.Text := 'green';
  742. Edit_Blue.Text := 'blue';
  743. Edit_Alpha.Text := 'alpha';
  744. Edit_Hue.Text := 'hue';
  745. Edit_Saturation.Text := 'saturation';
  746. Edit_Lightness.Text := 'lightness';
  747. Edit_L.Text := 'L';
  748. Edit_a.Text := 'a';
  749. Edit_b.Text := 'b';
  750. CheckBox_Gamma.Checked := true;
  751. if Assigned(FFilterConnector.Parameters) then
  752. with FFilterConnector.Parameters do
  753. begin
  754. if IsDefined('Red') then Edit_Red.Text := Strings['Red'];
  755. if IsDefined('Green') then Edit_Green.Text := Strings['Green'];
  756. if IsDefined('Blue') then Edit_Blue.Text := Strings['Blue'];
  757. if IsDefined('Alpha') then Edit_Alpha.Text := Strings['Alpha'];
  758. if IsDefined('Hue') then Edit_Hue.Text := Strings['Hue'];
  759. if IsDefined('Saturation') then Edit_Saturation.Text := Strings['Saturation'];
  760. if IsDefined('Lightness') then Edit_Lightness.Text := Strings['Lightness'];
  761. if IsDefined('L') then Edit_Hue.Text := Strings['L'];
  762. if IsDefined('a') then Edit_Saturation.Text := Strings['a'];
  763. if IsDefined('b') then Edit_Lightness.Text := Strings['b'];
  764. if IsDefined('GammaCorrection') then CheckBox_Gamma.Checked:= Booleans['GammaCorrection'];
  765. if IsDefined('CorrectedHue') then CheckBox_GSBA.Checked:= Booleans['CorrectedHue'];
  766. if IsDefined('Red') or IsDefined('Green') or IsDefined('Blue') then
  767. PageControl_Color.ActivePage := TabSheet_RGB else
  768. if IsDefined('Hue') or IsDefined('Saturation') or IsDefined('Lightness') then
  769. PageControl_Color.ActivePage := TabSheet_HSL else
  770. if IsDefined('L') or IsDefined('a') or IsDefined('b') then
  771. PageControl_Color.ActivePage := TabSheet_Lab;
  772. end;
  773. Edit_RedChange(nil);
  774. Edit_GreenChange(nil);
  775. Edit_BlueChange(nil);
  776. Edit_AlphaChange(nil);
  777. Edit_HueChange(nil);
  778. Edit_SaturationChange(nil);
  779. Edit_LightnessChange(nil);
  780. Edit_LChange(nil);
  781. Edit_aChange(nil);
  782. Edit_bChange(nil);
  783. CheckBox_Preview.Checked := True;
  784. CheckBox_Preview.Caption := rsPreview;
  785. Button_OK.Caption := rsOk;
  786. Button_Cancel.Caption := rsCancel;
  787. FInitializing:= false;
  788. end;
  789. function TFFilterFunction.CreateExpr: TFPExpressionParser;
  790. var
  791. i: Integer;
  792. begin
  793. result := TFPExpressionParser.Create(nil);
  794. result.BuiltIns := AllBuiltIns - [bcAggregate];
  795. result.Identifiers.AddFloatVariable('x',0);
  796. result.Identifiers.AddFloatVariable('y',0);
  797. result.Identifiers.AddIntegerVariable('width',1);
  798. result.Identifiers.AddIntegerVariable('height',1);
  799. result.Identifiers.AddFloatVariable('red',0);
  800. result.Identifiers.AddFloatVariable('green',0);
  801. result.Identifiers.AddFloatVariable('blue',0);
  802. result.Identifiers.AddFloatVariable('alpha',0);
  803. result.Identifiers.AddFloatVariable('hue',0);
  804. result.Identifiers.AddFloatVariable('saturation',0);
  805. result.Identifiers.AddFloatVariable('lightness',0);
  806. result.Identifiers.AddFloatVariable('L',0);
  807. result.Identifiers.AddFloatVariable('a',0);
  808. result.Identifiers.AddFloatVariable('b',0);
  809. result.Identifiers.AddFloatVariable('random',0);
  810. result.Identifiers.AddFunction('min', 'F', 'FF', @ExprFunctionMin_Call);
  811. result.Identifiers.AddFunction('max', 'F', 'FF', @ExprFunctionMax_Call);
  812. for i := low(StatsName) to high(StatsName) do
  813. begin
  814. result.Identifiers.AddFloatVariable('min_'+StatsName[i],0);
  815. result.Identifiers.AddFloatVariable('max_'+StatsName[i],0);
  816. result.Identifiers.AddFloatVariable('avg_'+StatsName[i],0);
  817. end;
  818. end;
  819. function TFFilterFunction.ExprResultToFloat(const AResult: TFPExpressionResult): single;
  820. var {%H-}code: integer;
  821. begin
  822. case AResult.ResultType of
  823. rtFloat: result := AResult.ResFloat;
  824. rtInteger: result := AResult.ResInteger;
  825. rtBoolean: if AResult.ResBoolean then result := 1 else result := 0;
  826. rtDateTime: result := 0;
  827. rtString: val(AResult.ResString, result, code);
  828. else result := 0;
  829. end;
  830. end;
  831. procedure TFFilterFunction.ExprFunctionMin_Call(
  832. var Result: TFPExpressionResult; const Args: TExprParameterArray);
  833. begin
  834. result.ResultType:= rtFloat;
  835. result.ResFloat := min(ExprResultToFloat(Args[0]), ExprResultToFloat(Args[1]));
  836. end;
  837. procedure TFFilterFunction.ExprFunctionMax_Call(
  838. var Result: TFPExpressionResult; const Args: TExprParameterArray);
  839. begin
  840. result.ResultType:= rtFloat;
  841. result.ResFloat := max(ExprResultToFloat(Args[0]), ExprResultToFloat(Args[1]));
  842. end;
  843. procedure TFFilterFunction.StatsNotComputed(AFrom,ATo: integer);
  844. var i: integer;
  845. begin
  846. for i := AFrom to ATo do
  847. FStats[i].computed := false;
  848. end;
  849. procedure TFFilterFunction.NeedStats(AStatIndex: integer);
  850. const
  851. oneOver255 = 1/255;
  852. oneOver65535 = 1/65535;
  853. oneOver65536 = 1/65536;
  854. procedure AggregateStat(AIndex: integer; AValue: single);
  855. begin
  856. FStats[AIndex].min := min(FStats[AIndex].min, AValue);
  857. FStats[AIndex].max := max(FStats[AIndex].max, AValue);
  858. FStats[AIndex].sum += AValue;
  859. inc(FStats[AIndex].count);
  860. end;
  861. procedure StartCompute(AFrom,ATo: integer);
  862. var
  863. i: Integer;
  864. begin
  865. for i := AFrom to ATo do
  866. begin
  867. FStats[i].min := 1;
  868. FStats[i].max := 0;
  869. FStats[i].sum := 0;
  870. FStats[i].avg := 0;
  871. FStats[i].count := 0;
  872. end;
  873. end;
  874. procedure EndCompute(AFrom,ATo: integer);
  875. var
  876. i: Integer;
  877. begin
  878. for i := AFrom to ATo do
  879. begin
  880. if FStats[i].count > 0 then
  881. FStats[i].avg := FStats[i].sum / FStats[i].count;
  882. FStats[i].computed:= true;
  883. end;
  884. end;
  885. var
  886. y, x: LongInt;
  887. p: PBGRAPixel;
  888. gammaCorr, gsba: Boolean;
  889. ec: TExpandedPixel;
  890. r,g,b,h,s,l: single;
  891. begin
  892. if not FStats[AStatIndex].computed then
  893. begin
  894. gammaCorr := CheckBox_Gamma.Checked;
  895. gsba := CheckBox_GSBA.Checked;
  896. if (AStatIndex >= 1) and (AStatIndex <= 4) then
  897. begin
  898. StartCompute(1,4);
  899. for y := FFilterConnector.WorkArea.Top to FFilterConnector.WorkArea.Bottom-1 do
  900. begin
  901. p := FFilterConnector.BackupLayer.ScanLine[y] + FFilterConnector.WorkArea.Left;
  902. for x := FFilterConnector.WorkArea.Left to FFilterConnector.WorkArea.Right-1 do
  903. begin
  904. if p^.alpha > 0 then
  905. begin
  906. if gammaCorr then
  907. begin
  908. ec := p^.ToExpanded;
  909. r := ec.red*oneOver65535;
  910. g := ec.green*oneOver65535;
  911. b := ec.blue*oneOver65535;
  912. end else
  913. begin
  914. r := p^.red*oneOver255;
  915. g := p^.green*oneOver255;
  916. b := p^.blue*oneOver255;
  917. end;
  918. AggregateStat(1, r);
  919. AggregateStat(2, g);
  920. AggregateStat(3, b);
  921. end;
  922. AggregateStat(4, p^.blue*oneOver255);
  923. inc(p);
  924. end;
  925. end;
  926. EndCompute(1,4);
  927. end else
  928. if (AStatIndex >= 5) and (AStatIndex <= 7) then
  929. begin
  930. StartCompute(5,7);
  931. for y := FFilterConnector.WorkArea.Top to FFilterConnector.WorkArea.Bottom-1 do
  932. begin
  933. p := FFilterConnector.BackupLayer.ScanLine[y] + FFilterConnector.WorkArea.Left;
  934. for x := FFilterConnector.WorkArea.Left to FFilterConnector.WorkArea.Right-1 do
  935. begin
  936. if p^.alpha > 0 then
  937. begin
  938. if gsba then
  939. with p^.ToGSBAPixel do
  940. begin
  941. h := hue*oneOver65536;
  942. s := saturation*oneOver65535;
  943. l := lightness*oneOver65535;
  944. end else
  945. if gammaCorr then
  946. with p^.ToHSLAPixel do
  947. begin
  948. h := hue*oneOver65536;
  949. s := saturation*oneOver65535;
  950. l := lightness*oneOver65535;
  951. end else
  952. with p^.ToStdHSLA do
  953. begin
  954. h := hue/360;
  955. s := saturation;
  956. l := lightness;
  957. end;
  958. AggregateStat(5, h);
  959. AggregateStat(6, s);
  960. AggregateStat(7, l);
  961. end;
  962. inc(p);
  963. end;
  964. end;
  965. EndCompute(5,7);
  966. end else
  967. if (AStatIndex >= 8) and (AStatIndex <= 10) then
  968. begin
  969. StartCompute(8,10);
  970. for y := FFilterConnector.WorkArea.Top to FFilterConnector.WorkArea.Bottom-1 do
  971. begin
  972. p := FFilterConnector.BackupLayer.ScanLine[y] + FFilterConnector.WorkArea.Left;
  973. for x := FFilterConnector.WorkArea.Left to FFilterConnector.WorkArea.Right-1 do
  974. begin
  975. if p^.alpha > 0 then
  976. with p^.ToLabA do
  977. begin
  978. AggregateStat(8, L/100);
  979. AggregateStat(9, a/127);
  980. AggregateStat(10, b/127);
  981. end;
  982. inc(p);
  983. end;
  984. end;
  985. EndCompute(8,10);
  986. end;
  987. end;
  988. end;
  989. function TFFilterFunction.ReplaceStats(AExpr: string): string;
  990. var
  991. i: Integer;
  992. begin
  993. result := AExpr;
  994. for i := low(StatsName) to high(StatsName) do
  995. begin
  996. result := ReplaceIdentifier(result, 'min('+StatsName[i]+')','min_'+StatsName[i]);
  997. result := ReplaceIdentifier(result, 'max('+StatsName[i]+')','max_'+StatsName[i]);
  998. result := ReplaceIdentifier(result, 'avg('+StatsName[i]+')','avg_'+StatsName[i]);
  999. end;
  1000. end;
  1001. procedure TFFilterFunction.Button_OKClick(Sender: TObject);
  1002. begin
  1003. if not CheckBox_Preview.Checked then
  1004. DisplayComputedImage;
  1005. FFilterConnector.ValidateAction;
  1006. ModalResult := mrOK;
  1007. end;
  1008. procedure TFFilterFunction.CheckBox_GammaChange(Sender: TObject);
  1009. begin
  1010. if not FInitializing then
  1011. begin
  1012. StatsNotComputed(1,3);
  1013. if not CheckBox_GSBA.Checked then StatsNotComputed(5,7);
  1014. PreviewNeeded;
  1015. end;
  1016. end;
  1017. procedure TFFilterFunction.CheckBox_GSBAChange(Sender: TObject);
  1018. begin
  1019. if not FInitializing then
  1020. begin
  1021. StatsNotComputed(5,7);
  1022. PreviewNeeded;
  1023. end;
  1024. end;
  1025. procedure TFFilterFunction.DisplayComputedImage;
  1026. begin
  1027. if FComputedImage <> nil then
  1028. FFilterConnector.PutImage(FComputedImage,True,False);
  1029. end;
  1030. procedure TFFilterFunction.CheckBox_PreviewChange(Sender: TObject);
  1031. begin
  1032. if FInitializing then exit;
  1033. if CheckBox_Preview.Checked then
  1034. DisplayComputedImage
  1035. else
  1036. FFilterConnector.RestoreBackup;
  1037. end;
  1038. procedure TFFilterFunction.Edit_aChange(Sender: TObject);
  1039. begin
  1040. UpdateExpr(FaExpr,Edit_a,FaError);
  1041. end;
  1042. procedure TFFilterFunction.Button_CancelClick(Sender: TObject);
  1043. begin
  1044. Timer1.Enabled:= false;
  1045. end;
  1046. procedure TFFilterFunction.Edit_AlphaChange(Sender: TObject);
  1047. begin
  1048. UpdateExpr(FAlphaExpr,Edit_Alpha,FAlphaError);
  1049. end;
  1050. procedure TFFilterFunction.Edit_bChange(Sender: TObject);
  1051. begin
  1052. UpdateExpr(FbExpr,Edit_b,FbError);
  1053. end;
  1054. procedure TFFilterFunction.Edit_BlueChange(Sender: TObject);
  1055. begin
  1056. UpdateExpr(FBlueExpr,Edit_Blue,FBlueError);
  1057. end;
  1058. procedure TFFilterFunction.Edit_GreenChange(Sender: TObject);
  1059. begin
  1060. UpdateExpr(FGreenExpr,Edit_Green,FGreenError);
  1061. end;
  1062. procedure TFFilterFunction.Edit_HueChange(Sender: TObject);
  1063. begin
  1064. UpdateExpr(FHueExpr,Edit_Hue,FHueError);
  1065. end;
  1066. procedure TFFilterFunction.Edit_LChange(Sender: TObject);
  1067. begin
  1068. UpdateExpr(FLExpr,Edit_L,FLError);
  1069. end;
  1070. procedure TFFilterFunction.Edit_LightnessChange(Sender: TObject);
  1071. begin
  1072. UpdateExpr(FLightnessExpr,Edit_Lightness,FLightnessError);
  1073. end;
  1074. procedure TFFilterFunction.Edit_RedChange(Sender: TObject);
  1075. begin
  1076. UpdateExpr(FRedExpr,Edit_Red,FRedError);
  1077. end;
  1078. procedure TFFilterFunction.Edit_SaturationChange(Sender: TObject);
  1079. begin
  1080. UpdateExpr(FSaturationExpr,Edit_Saturation,FSaturationError);
  1081. end;
  1082. {$R *.lfm}
  1083. initialization
  1084. randomize;
  1085. end.