ufilterthread.pas 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UFilterThread;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, BGRABitmap, BGRAFilters, UFilterConnector;
  7. type
  8. TFilterThread = class;
  9. TThreadManagerEvent = (tmeStartingNewTask, tmeCompletedTask, tmeAbortedTask);
  10. TThreadManagerEventHandler = procedure(ASender:TObject; AEvent: TThreadManagerEvent) of object;
  11. { TFilterThreadManager }
  12. TFilterThreadManager = class
  13. private
  14. FThread: TFilterThread;
  15. FFilterConnector: TFilterConnector;
  16. FQuitting,FCancellingPreview: boolean;
  17. FNextTask: TFilterTask;
  18. FOnEvent: TThreadManagerEventHandler;
  19. FLastUpdatedY: integer;
  20. function GetReadyToClose: boolean;
  21. procedure StartNextTask;
  22. procedure RaiseEvent(AEvent: TThreadManagerEvent);
  23. protected
  24. procedure OnFilterDone({%H-}ASender: TThread; {%H-}AFilteredLayer: TBGRABitmap);
  25. procedure OnFilterTerminate({%H-}ASender: TObject);
  26. public
  27. constructor Create(AFilterConnector: TFilterConnector);
  28. destructor Destroy; override;
  29. procedure WantPreview(ATask: TFilterTask);
  30. procedure Quit;
  31. function RegularCheck: boolean;
  32. property Quitting: boolean read FQuitting;
  33. property ReadyToClose: boolean read GetReadyToClose;
  34. property CancellingPreview: boolean read FCancellingPreview;
  35. property OnEvent: TThreadManagerEventHandler read FOnEvent write FOnEvent;
  36. end;
  37. TFilterThreadOnDoneHandler = procedure(ASender: TThread; AFilteredLayer: TBGRABitmap) of object;
  38. { TFilterThread }
  39. TFilterThread = class(TThread)
  40. strict private
  41. FConnector: TFilterConnector;
  42. FOnDone: TFilterThreadOnDoneHandler;
  43. FTask: TFilterTask;
  44. FFilteredLayer: TBGRABitmap;
  45. FCurrentY: integer;
  46. protected
  47. procedure SynchronizedOnDone;
  48. procedure CallOnDone;
  49. function CheckShouldStop(ACurrentY: integer): boolean;
  50. function CreateFilterTask: TFilterTask; virtual; abstract;
  51. public
  52. constructor Create(AConnector: TFilterConnector; ASuspended: boolean);
  53. procedure Execute; override;
  54. destructor Destroy; override;
  55. property OnFilterDone: TFilterThreadOnDoneHandler read FOnDone write FOnDone;
  56. property FilteredLayer: TBGRABitmap read FFilteredLayer;
  57. property FilterConnector: TFilterConnector read FConnector;
  58. property CurrentY: integer read FCurrentY;
  59. end;
  60. { TSingleTaskFilterThread }
  61. TSingleTaskFilterThread = class(TFilterThread)
  62. private
  63. FTask: TFilterTask;
  64. protected
  65. function CreateFilterTask: TFilterTask; override;
  66. public
  67. constructor Create(AFilterConnector: TFilterConnector; ATask: TFilterTask; ASuspended: boolean);
  68. destructor Destroy; override;
  69. end;
  70. implementation
  71. { TSingleTaskFilterThread }
  72. function TSingleTaskFilterThread.CreateFilterTask: TFilterTask;
  73. begin
  74. result := FTask;
  75. FTask := nil;
  76. end;
  77. constructor TSingleTaskFilterThread.Create(AFilterConnector: TFilterConnector;
  78. ATask: TFilterTask; ASuspended: boolean);
  79. begin
  80. FTask := ATask;
  81. inherited Create(AFilterConnector,ASuspended);
  82. end;
  83. destructor TSingleTaskFilterThread.Destroy;
  84. begin
  85. FTask.Free;
  86. inherited Destroy;
  87. end;
  88. { TFilterThreadManager }
  89. procedure TFilterThreadManager.StartNextTask;
  90. begin
  91. if not Assigned(FNextTask) then exit;
  92. FThread := TSingleTaskFilterThread.Create(FFilterConnector, FNextTask, True);
  93. FNextTask := nil;
  94. FThread.OnTerminate:= @OnFilterTerminate;
  95. FThread.OnFilterDone := @OnFilterDone;
  96. FThread.Start;
  97. FLastUpdatedY:= 0;
  98. RaiseEvent(tmeStartingNewTask);
  99. end;
  100. function TFilterThreadManager.GetReadyToClose: boolean;
  101. begin
  102. result := FQuitting and not FCancellingPreview;
  103. end;
  104. procedure TFilterThreadManager.RaiseEvent(AEvent: TThreadManagerEvent);
  105. begin
  106. if Assigned(FOnEvent) then FOnEvent(self,AEvent);
  107. end;
  108. procedure TFilterThreadManager.OnFilterDone(ASender: TThread;
  109. AFilteredLayer: TBGRABitmap);
  110. var changedBounds: TRect;
  111. begin
  112. if FLastUpdatedY < FFilterConnector.WorkArea.Bottom then
  113. begin
  114. changedBounds := rect(FFilterConnector.WorkArea.Left,FLastUpdatedY,FFilterConnector.WorkArea.Right,FFilterConnector.WorkArea.Bottom);
  115. If Assigned(AFilteredLayer) then
  116. FFilterConnector.PutImage(AFilteredLayer,changedBounds,False,False)
  117. else
  118. FFilterConnector.InvalidateActiveLayer(changedBounds);
  119. FLastUpdatedY := FFilterConnector.WorkArea.Bottom;
  120. end;
  121. FThread := nil; //it will free itself, set it now to nil so that it cannot be cancelled
  122. RaiseEvent(tmeCompletedTask);
  123. end;
  124. procedure TFilterThreadManager.OnFilterTerminate(ASender: TObject);
  125. begin
  126. FThread := nil; //it will free itself
  127. if FCancellingPreview then
  128. begin
  129. if Quitting or not Assigned(FNextTask) then FFilterConnector.RestoreBackup;
  130. FCancellingPreview := false;
  131. RaiseEvent(tmeAbortedTask);
  132. end;
  133. if not Quitting then StartNextTask;
  134. end;
  135. constructor TFilterThreadManager.Create(AFilterConnector: TFilterConnector);
  136. begin
  137. FFilterConnector := AFilterConnector;
  138. end;
  139. destructor TFilterThreadManager.Destroy;
  140. begin
  141. if Assigned(FThread) then
  142. raise exception.Create('Current task is not terminated');
  143. inherited Destroy;
  144. end;
  145. procedure TFilterThreadManager.WantPreview(ATask: TFilterTask);
  146. begin
  147. if FQuitting then
  148. begin
  149. FreeAndNil(ATask);
  150. exit;
  151. end;
  152. FreeAndNil(FNextTask);
  153. FNextTask := ATask;
  154. if Assigned(FThread) then
  155. begin
  156. FCancellingPreview:= true;
  157. FThread.Terminate;
  158. end else
  159. StartNextTask;
  160. end;
  161. procedure TFilterThreadManager.Quit;
  162. begin
  163. FQuitting:= true;
  164. if Assigned(FThread) then
  165. begin
  166. FCancellingPreview:= true;
  167. FThread.Terminate;
  168. end;
  169. FreeAndNil(FNextTask);
  170. end;
  171. function TFilterThreadManager.RegularCheck: boolean;
  172. var filteredLayer: TBGRABitmap;
  173. currentY: integer;
  174. changedBounds: TRect;
  175. begin
  176. if Assigned(FThread) and not FQuitting and not FCancellingPreview then
  177. begin
  178. filteredLayer := (FThread as TFilterThread).FilteredLayer;
  179. currentY := FThread.CurrentY;
  180. if currentY >= FLastUpdatedY then
  181. begin
  182. changedBounds := rect(FFilterConnector.WorkArea.Left,FLastUpdatedY,FFilterConnector.WorkArea.Right,currentY);
  183. if (currentY < FFilterConnector.WorkArea.Bottom) and (currentY=FLastUpdatedY) then currentY+=1;
  184. if filteredLayer <> nil then
  185. FFilterConnector.PutImage(filteredLayer,changedBounds,False,False)
  186. else
  187. FFilterConnector.InvalidateActiveLayer(changedBounds);
  188. end;
  189. FLastUpdatedY := currentY;
  190. end else
  191. if Assigned(FNextTask) then
  192. begin
  193. if not FCancellingPreview then StartNextTask;
  194. end;
  195. result := Assigned(FThread);
  196. end;
  197. { TFilterThread }
  198. procedure TFilterThread.SynchronizedOnDone;
  199. begin
  200. if Assigned(FOnDone) then FOnDone(self, FFilteredLayer);
  201. end;
  202. procedure TFilterThread.CallOnDone;
  203. begin
  204. Synchronize(@SynchronizedOnDone);
  205. end;
  206. function TFilterThread.CheckShouldStop(ACurrentY: integer): boolean;
  207. begin
  208. FCurrentY:= ACurrentY;
  209. result := Terminated;
  210. end;
  211. constructor TFilterThread.Create(AConnector: TFilterConnector;
  212. ASuspended: boolean);
  213. begin
  214. inherited Create(True);
  215. FConnector := AConnector;
  216. FreeOnTerminate := True;
  217. FFilteredLayer := nil;
  218. if not ASuspended then Start;
  219. end;
  220. procedure TFilterThread.Execute;
  221. begin
  222. FCurrentY:= 0;
  223. FreeAndNil(FFilteredLayer);
  224. FTask := CreateFilterTask;
  225. If FTask.Destination = nil then
  226. begin
  227. FFilteredLayer := FConnector.BackupLayer.Duplicate() as TBGRABitmap;
  228. FTask.Destination := FFilteredLayer;
  229. end;
  230. FTask.CheckShouldStop := @CheckShouldStop;
  231. try
  232. FTask.Execute;
  233. if not Terminated then CallOnDone;
  234. finally
  235. FreeAndNil(FTask);
  236. end;
  237. end;
  238. destructor TFilterThread.Destroy;
  239. begin
  240. FreeAndNil(FFilteredLayer);
  241. inherited Destroy;
  242. end;
  243. end.