| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // SPDX-License-Identifier: GPL-3.0-only
- unit UFilterThread;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, BGRABitmap, BGRAFilters, UFilterConnector;
- type
- TFilterThread = class;
- TThreadManagerEvent = (tmeStartingNewTask, tmeCompletedTask, tmeAbortedTask);
- TThreadManagerEventHandler = procedure(ASender:TObject; AEvent: TThreadManagerEvent) of object;
- { TFilterThreadManager }
- TFilterThreadManager = class
- private
- FThread: TFilterThread;
- FFilterConnector: TFilterConnector;
- FQuitting,FCancellingPreview: boolean;
- FNextTask: TFilterTask;
- FOnEvent: TThreadManagerEventHandler;
- FLastUpdatedY: integer;
- function GetReadyToClose: boolean;
- procedure StartNextTask;
- procedure RaiseEvent(AEvent: TThreadManagerEvent);
- protected
- procedure OnFilterDone({%H-}ASender: TThread; {%H-}AFilteredLayer: TBGRABitmap);
- procedure OnFilterTerminate({%H-}ASender: TObject);
- public
- constructor Create(AFilterConnector: TFilterConnector);
- destructor Destroy; override;
- procedure WantPreview(ATask: TFilterTask);
- procedure Quit;
- function RegularCheck: boolean;
- property Quitting: boolean read FQuitting;
- property ReadyToClose: boolean read GetReadyToClose;
- property CancellingPreview: boolean read FCancellingPreview;
- property OnEvent: TThreadManagerEventHandler read FOnEvent write FOnEvent;
- end;
- TFilterThreadOnDoneHandler = procedure(ASender: TThread; AFilteredLayer: TBGRABitmap) of object;
- { TFilterThread }
- TFilterThread = class(TThread)
- strict private
- FConnector: TFilterConnector;
- FOnDone: TFilterThreadOnDoneHandler;
- FTask: TFilterTask;
- FFilteredLayer: TBGRABitmap;
- FCurrentY: integer;
- protected
- procedure SynchronizedOnDone;
- procedure CallOnDone;
- function CheckShouldStop(ACurrentY: integer): boolean;
- function CreateFilterTask: TFilterTask; virtual; abstract;
- public
- constructor Create(AConnector: TFilterConnector; ASuspended: boolean);
- procedure Execute; override;
- destructor Destroy; override;
- property OnFilterDone: TFilterThreadOnDoneHandler read FOnDone write FOnDone;
- property FilteredLayer: TBGRABitmap read FFilteredLayer;
- property FilterConnector: TFilterConnector read FConnector;
- property CurrentY: integer read FCurrentY;
- end;
- { TSingleTaskFilterThread }
- TSingleTaskFilterThread = class(TFilterThread)
- private
- FTask: TFilterTask;
- protected
- function CreateFilterTask: TFilterTask; override;
- public
- constructor Create(AFilterConnector: TFilterConnector; ATask: TFilterTask; ASuspended: boolean);
- destructor Destroy; override;
- end;
- implementation
- { TSingleTaskFilterThread }
- function TSingleTaskFilterThread.CreateFilterTask: TFilterTask;
- begin
- result := FTask;
- FTask := nil;
- end;
- constructor TSingleTaskFilterThread.Create(AFilterConnector: TFilterConnector;
- ATask: TFilterTask; ASuspended: boolean);
- begin
- FTask := ATask;
- inherited Create(AFilterConnector,ASuspended);
- end;
- destructor TSingleTaskFilterThread.Destroy;
- begin
- FTask.Free;
- inherited Destroy;
- end;
- { TFilterThreadManager }
- procedure TFilterThreadManager.StartNextTask;
- begin
- if not Assigned(FNextTask) then exit;
- FThread := TSingleTaskFilterThread.Create(FFilterConnector, FNextTask, True);
- FNextTask := nil;
- FThread.OnTerminate:= @OnFilterTerminate;
- FThread.OnFilterDone := @OnFilterDone;
- FThread.Start;
- FLastUpdatedY:= 0;
- RaiseEvent(tmeStartingNewTask);
- end;
- function TFilterThreadManager.GetReadyToClose: boolean;
- begin
- result := FQuitting and not FCancellingPreview;
- end;
- procedure TFilterThreadManager.RaiseEvent(AEvent: TThreadManagerEvent);
- begin
- if Assigned(FOnEvent) then FOnEvent(self,AEvent);
- end;
- procedure TFilterThreadManager.OnFilterDone(ASender: TThread;
- AFilteredLayer: TBGRABitmap);
- var changedBounds: TRect;
- begin
- if FLastUpdatedY < FFilterConnector.WorkArea.Bottom then
- begin
- changedBounds := rect(FFilterConnector.WorkArea.Left,FLastUpdatedY,FFilterConnector.WorkArea.Right,FFilterConnector.WorkArea.Bottom);
- If Assigned(AFilteredLayer) then
- FFilterConnector.PutImage(AFilteredLayer,changedBounds,False,False)
- else
- FFilterConnector.InvalidateActiveLayer(changedBounds);
- FLastUpdatedY := FFilterConnector.WorkArea.Bottom;
- end;
- FThread := nil; //it will free itself, set it now to nil so that it cannot be cancelled
- RaiseEvent(tmeCompletedTask);
- end;
- procedure TFilterThreadManager.OnFilterTerminate(ASender: TObject);
- begin
- FThread := nil; //it will free itself
- if FCancellingPreview then
- begin
- if Quitting or not Assigned(FNextTask) then FFilterConnector.RestoreBackup;
- FCancellingPreview := false;
- RaiseEvent(tmeAbortedTask);
- end;
- if not Quitting then StartNextTask;
- end;
- constructor TFilterThreadManager.Create(AFilterConnector: TFilterConnector);
- begin
- FFilterConnector := AFilterConnector;
- end;
- destructor TFilterThreadManager.Destroy;
- begin
- if Assigned(FThread) then
- raise exception.Create('Current task is not terminated');
- inherited Destroy;
- end;
- procedure TFilterThreadManager.WantPreview(ATask: TFilterTask);
- begin
- if FQuitting then
- begin
- FreeAndNil(ATask);
- exit;
- end;
- FreeAndNil(FNextTask);
- FNextTask := ATask;
- if Assigned(FThread) then
- begin
- FCancellingPreview:= true;
- FThread.Terminate;
- end else
- StartNextTask;
- end;
- procedure TFilterThreadManager.Quit;
- begin
- FQuitting:= true;
- if Assigned(FThread) then
- begin
- FCancellingPreview:= true;
- FThread.Terminate;
- end;
- FreeAndNil(FNextTask);
- end;
- function TFilterThreadManager.RegularCheck: boolean;
- var filteredLayer: TBGRABitmap;
- currentY: integer;
- changedBounds: TRect;
- begin
- if Assigned(FThread) and not FQuitting and not FCancellingPreview then
- begin
- filteredLayer := (FThread as TFilterThread).FilteredLayer;
- currentY := FThread.CurrentY;
- if currentY >= FLastUpdatedY then
- begin
- changedBounds := rect(FFilterConnector.WorkArea.Left,FLastUpdatedY,FFilterConnector.WorkArea.Right,currentY);
- if (currentY < FFilterConnector.WorkArea.Bottom) and (currentY=FLastUpdatedY) then currentY+=1;
- if filteredLayer <> nil then
- FFilterConnector.PutImage(filteredLayer,changedBounds,False,False)
- else
- FFilterConnector.InvalidateActiveLayer(changedBounds);
- end;
- FLastUpdatedY := currentY;
- end else
- if Assigned(FNextTask) then
- begin
- if not FCancellingPreview then StartNextTask;
- end;
- result := Assigned(FThread);
- end;
- { TFilterThread }
- procedure TFilterThread.SynchronizedOnDone;
- begin
- if Assigned(FOnDone) then FOnDone(self, FFilteredLayer);
- end;
- procedure TFilterThread.CallOnDone;
- begin
- Synchronize(@SynchronizedOnDone);
- end;
- function TFilterThread.CheckShouldStop(ACurrentY: integer): boolean;
- begin
- FCurrentY:= ACurrentY;
- result := Terminated;
- end;
- constructor TFilterThread.Create(AConnector: TFilterConnector;
- ASuspended: boolean);
- begin
- inherited Create(True);
- FConnector := AConnector;
- FreeOnTerminate := True;
- FFilteredLayer := nil;
- if not ASuspended then Start;
- end;
- procedure TFilterThread.Execute;
- begin
- FCurrentY:= 0;
- FreeAndNil(FFilteredLayer);
- FTask := CreateFilterTask;
- If FTask.Destination = nil then
- begin
- FFilteredLayer := FConnector.BackupLayer.Duplicate() as TBGRABitmap;
- FTask.Destination := FFilteredLayer;
- end;
- FTask.CheckShouldStop := @CheckShouldStop;
- try
- FTask.Execute;
- if not Terminated then CallOnDone;
- finally
- FreeAndNil(FTask);
- end;
- end;
- destructor TFilterThread.Destroy;
- begin
- FreeAndNil(FFilteredLayer);
- inherited Destroy;
- end;
- end.
|