Ignore:
Timestamp:
Apr 9, 2015, 9:58:36 PM (9 years ago)
Author:
chronos
Message:
  • Fixed: Use csOpaque control style also to Image, PaintBox and OpenGLControl.
  • Modified: Change size of test frame with SpinEdits as delayed using timer.
  • Updated: BRGABitmap package to version 8.1.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/Packages/bgrabitmap/bgrafilters.pas

    r452 r472  
    1111uses
    1212  Classes, BGRABitmapTypes;
     13
     14type
     15  TCheckShouldStopFunc = function(ACurrentY: integer) : boolean of object;
     16
     17  { TFilterTask }
     18
     19  TFilterTask = class
     20  private
     21    FCheckShouldStop: TCheckShouldStopFunc;
     22    procedure SetDestination(AValue: TBGRACustomBitmap);
     23  protected
     24    FDestination: TBGRACustomBitmap;
     25    FSource: TBGRACustomBitmap;
     26    FCurrentY: integer;
     27    function GetShouldStop(ACurrentY: integer): boolean;
     28    procedure DoExecute; virtual; abstract;
     29  public
     30    function Execute: TBGRACustomBitmap;
     31    property Destination: TBGRACustomBitmap read FDestination write SetDestination;
     32    property CheckShouldStop: TCheckShouldStopFunc read FCheckShouldStop write FCheckShouldStop;
     33    property CurrentY: integer read FCurrentY;
     34  end;
    1335
    1436{ The median filter consist in calculating the median value of pixels. Here
     
    2547
    2648{ Sharpen filter add more contrast between pixels }
    27 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     49function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap;
     50function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    2851
    2952{ A radial blur applies a blur with a circular influence, i.e, each pixel
     
    3255function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
    3356  blurType: TRadialBlurType): TBGRACustomBitmap;
     57function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
     58  ABlurType: TRadialBlurType): TFilterTask;
    3459
    3560{ The precise blur allow to specify the blur radius with subpixel accuracy }
    36 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
    37   radius: single): TBGRACustomBitmap;
     61function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap;
     62function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TFilterTask;
    3863
    3964{ Motion blur merge pixels in a direction. The oriented parameter specifies
     
    4166function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    4267  angle: single; oriented: boolean): TBGRACustomBitmap;
    43 
    44 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
     68function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TFilterTask;
    4569
    4670{ General purpose blur filter, with a blur mask as parameter to describe
    4771  how pixels influence each other }
    48 function FilterBlur(bmp: TBGRACustomBitmap;
    49   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     72function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     73function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask;
     74
     75function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
    5076
    5177{ Emboss filter compute a color difference in the angle direction }
    5278function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     79function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    5380
    5481{ Emboss highlight computes a sort of emboss with 45 degrees angle and
     
    6390function FilterNormalize(bmp: TBGRACustomBitmap;
    6491  eachChannel: boolean = True): TBGRACustomBitmap;
     92function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
     93  eachChannel: boolean = True): TBGRACustomBitmap;
    6594
    6695{ Rotate filter rotate the image and clip it in the bounding rectangle }
    6796function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    68   angle: single): TBGRACustomBitmap;
     97  angle: single; correctBlur: boolean = false): TBGRACustomBitmap;
    6998
    7099{ Grayscale converts colored pixel into grayscale with same luminosity }
    71100function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     101function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     102function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask;
    72103
    73104{ Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil }
     
    79110{ Twirl distortion, i.e. a progressive rotation }
    80111function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     112function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    81113
    82114{ Distort the image as if it were on a vertical cylinder }
     
    88120implementation
    89121
    90 uses Math, GraphType, Dialogs, BGRATransform;
     122uses Math, GraphType, Dialogs, BGRATransform, Types, SysUtils;
     123
     124type
     125  { TGrayscaleTask }
     126
     127  TGrayscaleTask = class(TFilterTask)
     128  private
     129    FBounds: TRect;
     130  public
     131    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect);
     132  protected
     133    procedure DoExecute; override;
     134  end;
     135
     136  { TBoxBlurTask }
     137
     138  TBoxBlurTask = class(TFilterTask)
     139  private
     140    FBounds: TRect;
     141    FRadius: integer;
     142  public
     143    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer);
     144  protected
     145    procedure DoExecute; override;
     146  end;
     147
     148  { TRadialBlurTask }
     149
     150  TRadialBlurTask = class(TFilterTask)
     151  private
     152    FBounds: TRect;
     153    FRadius: integer;
     154    FBlurType: TRadialBlurType;
     155  public
     156    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     157                       blurType: TRadialBlurType);
     158  protected
     159    procedure DoExecute; override;
     160  end;
     161
     162  { TCustomBlurTask }
     163
     164  TCustomBlurTask = class(TFilterTask)
     165  private
     166    FBounds: TRect;
     167    FMask: TBGRACustomBitmap;
     168    FMaskOwned: boolean;
     169  public
     170    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false);
     171    destructor Destroy; override;
     172  protected
     173    procedure DoExecute; override;
     174  end;
     175
     176  { TRadialPreciseBlurTask }
     177
     178  TRadialPreciseBlurTask = class(TFilterTask)
     179  private
     180    FBounds: TRect;
     181    FRadius: Single;
     182  public
     183    constructor Create(bmp: TBGRACustomBitmap; ABounds: TRect; radius: single);
     184  protected
     185    procedure DoExecute; override;
     186  end;
     187
     188  { TMotionBlurTask }
     189
     190  TMotionBlurTask = class(TFilterTask)
     191  private
     192    FBounds: TRect;
     193    FDistance,FAngle: single;
     194    FOriented: boolean;
     195  public
     196    constructor Create(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance, AAngle: single; AOriented: boolean);
     197  protected
     198    procedure DoExecute; override;
     199  end;
     200
     201procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     202  blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     203procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; ABounds: TRect;
     204  radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     205procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
     206  angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     207procedure FilterBlur(bmp: TBGRACustomBitmap; ABounds: TRect;
     208   blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    91209
    92210function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     
    98216
    99217var
    100   xb, yb: integer;
     218  xb, yb: Int32or64;
    101219  diag1, diag2, h1, h2, v1, v2: TSmartDiff;
    102220  c,c1,c2:      TBGRAPixel;
     
    105223  function ColorDiff(c1, c2: TBGRAPixel): single;
    106224  var
    107     max1, max2: integer;
     225    max1, max2: Int32or64;
    108226  begin
    109227    if (c1.alpha = 0) and (c2.alpha = 0) then
     
    156274  end;
    157275
    158   function smartDiff(x1, y1, x2, y2: integer): TSmartDiff;
     276  function smartDiff(x1, y1, x2, y2: Int32or64): TSmartDiff;
    159277  var
    160278    c1, c2, c1m, c2m: TBGRAPixel;
     
    209327        begin
    210328          c1 := bmp.GetPixel(xb, yb);
    211           c2 := bmp.GetPixel(integer(xb + 1), integer(yb + 1));
     329          c2 := bmp.GetPixel(xb + 1, yb + 1);
    212330          c := MergeBGRA(c1, c2);
    213331          //restore
    214332          Result.SetPixel(xb * 3 + 2, yb * 3 + 2, bmp.GetPixel(xb, yb));
    215           Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(integer(xb + 1), integer(yb + 1)));
     333          Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(xb + 1, yb + 1));
    216334
    217335          if (diag1.sd < h1.sd) and (diag1.sd < v2.sd) then
     
    250368  of the square. Finally the difference is added to the new pixel, exagerating
    251369  its difference with its neighbours. }
    252 function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    253 const
    254   nbpix = 8;
     370function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    255371var
    256   yb, xb:   integer;
    257   dx, dy, n, j: integer;
    258   a_pixels: array[0..nbpix - 1] of TBGRAPixel;
    259   sumR, sumG, sumB, sumA, RGBdiv, nbA: cardinal;
    260   tempPixel, refPixel: TBGRAPixel;
    261   pdest:    PBGRAPixel;
     372  yb, xcount: Int32or64;
     373  dx, dy: Int32or64;
     374  a_pixels: array[-2..1,-2..1] of PBGRAPixel;
     375  sumR, sumG, sumB, sumA, {RGBdiv, }nbA: UInt32or64;
     376  refPixel: TBGRAPixel;
     377  pdest,ptempPixel:    PBGRAPixel;
    262378  bounds:   TRect;
    263 begin
     379  Amount256: boolean;
     380  lastXincluded: boolean;
     381  alpha,rgbDivShr1: uint32or64;
     382begin
     383  if IsRectEmpty(ABounds) then exit;
     384  Amount256 := AAmount = 256;
    264385  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    265386
    266387  //determine where pixels are in the bitmap
    267388  bounds := bmp.GetImageBounds;
    268   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    269     exit;
     389  if not IntersectRect(bounds, bounds,ABounds) then exit;
    270390  bounds.Left   := max(0, bounds.Left - 1);
    271391  bounds.Top    := max(0, bounds.Top - 1);
    272392  bounds.Right  := min(bmp.Width, bounds.Right + 1);
    273393  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     394  lastXincluded:= bounds.Right < bmp.Width;
    274395
    275396  //loop through the destination bitmap
     
    277398  begin
    278399    pdest := Result.scanline[yb] + bounds.Left;
    279     for xb := bounds.Left to bounds.Right - 1 do
    280     begin
     400    fillchar({%H-}a_pixels,sizeof(a_pixels),0);
     401    for dy := -1 to 1 do
     402      if (yb+dy >= bounds.Top) and (yb+dy < bounds.Bottom) then
     403        a_pixels[dy,1] := bmp.ScanLine[yb+dy]+bounds.Left else
     404          a_pixels[dy,1] := nil;
     405    xcount := bounds.right-bounds.left;
     406    while xcount > 0 do
     407    begin
     408      dec(xcount);
     409
    281410      //for each pixel, read eight surrounding pixels in the source bitmap
    282       n := 0;
    283411      for dy := -1 to 1 do
    284         for dx := -1 to 1 do
    285           if (dx <> 0) or (dy <> 0) then
    286           begin
    287             a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
    288             Inc(n);
    289           end;
     412        for dx := -1 to 0 do
     413          a_pixels[dy,dx] := a_pixels[dy,dx+1];
     414      if (xcount > 0) or lastXincluded then
     415      begin
     416        for dy := -1 to 1 do
     417          if a_pixels[dy,0] <> nil then a_pixels[dy,1] := a_pixels[dy,0]+1;
     418      end;
    290419
    291420      //compute sum
     
    294423      sumB   := 0;
    295424      sumA   := 0;
    296       RGBdiv := 0;
     425      //RGBdiv := 0;
    297426      nbA    := 0;
    298427
    299428       {$hints off}
    300       for j := 0 to n - 1 do
    301       begin
    302         tempPixel := a_pixels[j];
    303         sumR      += tempPixel.red * tempPixel.alpha;
    304         sumG      += tempPixel.green * tempPixel.alpha;
    305         sumB      += tempPixel.blue * tempPixel.alpha;
    306         RGBdiv    += tempPixel.alpha;
    307         sumA      += tempPixel.alpha;
    308         Inc(nbA);
    309       end;
     429      for dy := -1 to 1 do
     430        for dx := -1 to 1 do
     431        if (dx<>0) or (dy<>0) then
     432        begin
     433          ptempPixel := a_pixels[dy,dx];
     434          if ptempPixel <> nil then
     435          begin
     436            alpha := ptempPixel^.alpha;
     437            sumR      += ptempPixel^.red * alpha;
     438            sumG      += ptempPixel^.green * alpha;
     439            sumB      += ptempPixel^.blue * alpha;
     440            //RGBdiv    += alpha;
     441            sumA      += alpha;
     442            Inc(nbA);
     443          end;
     444        end;
    310445       {$hints on}
    311446
    312447      //we finally have an average pixel
    313       if (RGBdiv = 0) then
     448      if ({RGBdiv}sumA = 0) then
    314449        refPixel := BGRAPixelTransparent
    315450      else
    316451      begin
    317         refPixel.red   := (sumR + RGBdiv shr 1) div RGBdiv;
    318         refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv;
    319         refPixel.blue  := (sumB + RGBdiv shr 1) div RGBdiv;
     452        rgbDivShr1:= {RGBDiv}sumA shr 1;
     453        refPixel.red   := (sumR + rgbDivShr1) div {RGBdiv}sumA;
     454        refPixel.green := (sumG + rgbDivShr1) div {RGBdiv}sumA;
     455        refPixel.blue  := (sumB + rgbDivShr1) div {RGBdiv}sumA;
    320456        refPixel.alpha := (sumA + nbA shr 1) div nbA;
    321457      end;
    322458
    323459      //read the pixel at the center of the square
    324       tempPixel := bmp.GetPixel(xb, yb);
     460      ptempPixel := a_pixels[0,0];
    325461      if refPixel <> BGRAPixelTransparent then
    326462      begin
    327463        //compute sharpened pixel by adding the difference
    328         tempPixel.red   := max(0, min(255, tempPixel.red +
    329           integer(tempPixel.red - refPixel.red)));
    330         tempPixel.green := max(0, min(255, tempPixel.green +
    331           integer(tempPixel.green - refPixel.green)));
    332         tempPixel.blue  := max(0, min(255, tempPixel.blue +
    333           integer(tempPixel.blue - refPixel.blue)));
    334         tempPixel.alpha := max(0, min(255, tempPixel.alpha +
    335           integer(tempPixel.alpha - refPixel.alpha)));
    336       end;
    337       pdest^ := tempPixel;
     464        if not Amount256 then
     465          pdest^ := BGRA( max(0, min($FFFF, Int32or64(ptempPixel^.red shl 8) +
     466            AAmount*(ptempPixel^.red - refPixel.red))) shr 8,
     467              max(0, min($FFFF, Int32or64(ptempPixel^.green shl 8) +
     468            AAmount*(ptempPixel^.green - refPixel.green))) shr 8,
     469             max(0, min($FFFF, Int32or64(ptempPixel^.blue shl 8) +
     470            AAmount*(ptempPixel^.blue - refPixel.blue))) shr 8,
     471             max(0, min($FFFF, Int32or64(ptempPixel^.alpha shl 8) +
     472            AAmount*(ptempPixel^.alpha - refPixel.alpha))) shr 8 )
     473        else
     474          pdest^ := BGRA( max(0, min(255, (ptempPixel^.red shl 1) - refPixel.red)),
     475             max(0, min(255, (ptempPixel^.green shl 1) - refPixel.green)),
     476             max(0, min(255, (ptempPixel^.blue shl 1) - refPixel.blue)),
     477             max(0, min(255, (ptempPixel^.alpha shl 1) - refPixel.alpha)));
     478      end else
     479        pdest^ := ptempPixel^;
    338480      Inc(pdest);
    339481    end;
    340482  end;
    341483  Result.InvalidateBitmap;
     484end;
     485
     486function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer
     487  ): TBGRACustomBitmap;
     488begin
     489  result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount);
    342490end;
    343491
    344492{ Precise blur builds a blur mask with a gradient fill and use
    345493  general purpose blur }
    346 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
    347   radius: single): TBGRACustomBitmap;
     494procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
     495  ABounds: TRect; radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    348496var
    349497  blurShape: TBGRACustomBitmap;
     
    352500  if radius = 0 then
    353501  begin
    354     result := bmp.Duplicate;
     502    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    355503    exit;
    356504  end;
     
    360508    BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF(
    361509    intRadius - radius - 1, intRadius), dmSet);
    362   Result := FilterBlur(bmp, blurShape);
     510  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    363511  blurShape.Free;
     512end;
     513
     514function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single
     515  ): TBGRACustomBitmap;
     516begin
     517  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     518  FilterBlurRadialPrecise(bmp, rect(0,0,bmp.Width,bmp.Height), radius, result, nil);
     519end;
     520
     521function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     522  ARadius: single): TFilterTask;
     523begin
     524  result := TRadialPreciseBlurTask.Create(ABmp,ABounds,ARadius);
    364525end;
    365526
     
    369530  the vertical sums are kept except for the last column of
    370531  the square }
    371 function FilterBlurFast(bmp: TBGRACustomBitmap;
    372   radius: integer): TBGRACustomBitmap;
    373 
     532procedure FilterBlurFast(bmp: TBGRACustomBitmap; ABounds: TRect;
     533  radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     534 {$IFDEF CPU64}{$DEFINE FASTBLUR_DOUBLE}{$ENDIF}
    374535  type
    375536    TRowSum = record
    376       sumR,sumG,sumB,rgbDiv,sumA,aDiv: cardinal;
    377     end;
    378 
    379   function ComputeAverage(sum: TRowSum): TBGRAPixel;
    380   begin
     537      sumR,sumG,sumB,rgbDiv,sumA,aDiv: uint32or64;
     538    end;
     539    TExtendedRowValue = {$IFDEF FASTBLUR_DOUBLE}double{$ELSE}uint64{$ENDIF};
     540    TExtendedRowSum = record
     541      sumR,sumG,sumB,rgbDiv,sumA,aDiv: TExtendedRowValue;
     542    end;
     543
     544  function ComputeExtendedAverage(sum: TExtendedRowSum): TBGRAPixel;
     545  {$IFDEF FASTBLUR_DOUBLE}
     546  var v: uint32or64;
     547  {$ENDIF}
     548  begin
     549    {$IFDEF FASTBLUR_DOUBLE}
     550    v := round(sum.sumA/sum.aDiv);
     551    if v > 255 then result.alpha := 255 else result.alpha := v;
     552    v := round(sum.sumR/sum.rgbDiv);
     553    if v > 255 then result.red := 255 else result.red := v;
     554    v := round(sum.sumG/sum.rgbDiv);
     555    if v > 255 then result.green := 255 else result.green := v;
     556    v := round(sum.sumB/sum.rgbDiv);
     557    if v > 255 then result.blue := 255 else result.blue := v;
     558    {$ELSE}
    381559    result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
    382560    result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
    383561    result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
    384562    result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
     563    {$ENDIF}
     564  end;
     565
     566  function ComputeClampedAverage(sum: TRowSum): TBGRAPixel;
     567  var v: UInt32or64;
     568  begin
     569    v := (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
     570    if v > 255 then result.alpha := 255 else result.alpha := v;
     571    v := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
     572    if v > 255 then result.red := 255 else result.red := v;
     573    v := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
     574    if v > 255 then result.green := 255 else result.green := v;
     575    v := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
     576    if v > 255 then result.blue := 255 else result.blue := v;
     577  end;
     578
     579  function ComputeAverage(sum: TRowSum): TBGRAPixel;
     580  begin
     581    result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
     582    result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
     583    result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
     584    result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
    385585  end;
    386586
     
    389589{ Normal radial blur compute a blur mask with a GradientFill and
    390590  then posterize to optimize general purpose blur }
    391 function FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
    392   radius: integer): TBGRACustomBitmap;
     591procedure FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
     592  ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    393593var
    394594  blurShape: TBGRACustomBitmap;
    395   n: Integer;
     595  n: Int32or64;
    396596  p: PBGRAPixel;
    397597begin
     598  if radius = 0 then
     599  begin
     600    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     601    exit;
     602  end;
    398603  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    399604  blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
     
    407612    inc(p);
    408613  end;
    409   Result := FilterBlur(bmp, blurShape);
     614  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    410615  blurShape.Free;
    411616end;
    412617
    413618{ Blur disk creates a disk mask with a FillEllipse }
    414 function FilterBlurDisk(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     619procedure FilterBlurDisk(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    415620var
    416621  blurShape: TBGRACustomBitmap;
    417622begin
     623  if radius = 0 then
     624  begin
     625    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     626    exit;
     627  end;
    418628  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    419629  blurShape.Fill(BGRABlack);
    420630  blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite);
    421   Result := FilterBlur(bmp, blurShape);
     631  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    422632  blurShape.Free;
    423633end;
    424634
    425635{ Corona blur use a circle as mask }
    426 function FilterBlurCorona(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     636procedure FilterBlurCorona(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    427637var
    428638  blurShape: TBGRACustomBitmap;
    429639begin
     640  if radius = 0 then
     641  begin
     642    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     643    exit;
     644  end;
    430645  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    431646  blurShape.Fill(BGRABlack);
    432647  blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1);
    433   Result := FilterBlur(bmp, blurShape);
     648  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    434649  blurShape.Free;
     650end;
     651
     652function FilterBlurBox(bmp: TBGRACustomBitmap; radius: integer; ADestination: TBGRACustomBitmap): TBGRACustomBitmap;
     653var task: TBoxBlurTask;
     654begin
     655  task := TBoxBlurTask.Create(bmp, rect(0,0,bmp.Width,bmp.Height), radius);
     656  task.Destination := ADestination;
     657  result := task.Execute;
     658  task.Free;
     659end;
     660
     661procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
     662  blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     663begin
     664  if radius = 0 then
     665  begin
     666    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
     667    exit;
     668  end;
     669  case blurType of
     670    rbCorona:  FilterBlurCorona(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     671    rbDisk:    FilterBlurDisk(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     672    rbNormal:  FilterBlurRadialNormal(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     673    rbFast:    FilterBlurFast(bmp, ABounds, radius, ADestination, ACheckShouldStop);
     674    rbPrecise: FilterBlurRadialPrecise(bmp, ABounds, radius / 10, ADestination, ACheckShouldStop);
     675    rbBox:     FilterBlurBox(bmp, radius, ADestination);
     676  end;
    435677end;
    436678
     
    438680  blurType: TRadialBlurType): TBGRACustomBitmap;
    439681begin
    440   if radius = 0 then
    441   begin
    442     result := bmp.Duplicate;
    443     exit;
    444   end;
    445   case blurType of
    446     rbCorona: Result  := FilterBlurCorona(bmp, radius);
    447     rbDisk: Result    := FilterBlurDisk(bmp, radius);
    448     rbNormal: Result  := FilterBlurRadialNormal(bmp, radius);
    449     rbFast: Result  := FilterBlurFast(bmp, radius);
    450     rbPrecise: Result := FilterBlurRadialPrecise(bmp, radius / 10);
    451     else
    452       Result := nil;
    453   end;
     682  if blurType = rbBox then
     683  begin
     684    result := FilterBlurBox(bmp,radius,nil);
     685  end else
     686  begin
     687    result := bmp.NewBitmap(bmp.width,bmp.Height);
     688    FilterBlurRadial(bmp, rect(0,0,bmp.Width,bmp.height), radius, blurType,result,nil);
     689  end;
     690end;
     691
     692function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
     693  ABlurType: TRadialBlurType): TFilterTask;
     694begin
     695  if ABlurType = rbBox then
     696    result := TBoxBlurTask.Create(ABmp,ABounds,ARadius)
     697  else
     698    result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType);
    454699end;
    455700
    456701{ This filter draws an antialiased line to make the mask, and
    457702  if the motion blur is oriented, does a GradientFill to orient it }
    458 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    459   angle: single; oriented: boolean): TBGRACustomBitmap;
     703procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
     704  angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    460705var
    461706  blurShape: TBGRACustomBitmap;
     
    463708  dx, dy, d: single;
    464709begin
    465   if distance = 0 then
    466   begin
    467     result := bmp.Duplicate;
     710  if distance < 1e-6 then
     711  begin
     712    ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    468713    exit;
    469714  end;
     
    482727      pointF(intRadius + dx * (d + 0.5), intRadius + dy * (d + 0.5)),
    483728      dmFastBlend, False);
    484   Result := FilterBlur(bmp, blurShape);
     729  FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    485730  blurShape.Free;
     731end;
     732
     733function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
     734  angle: single; oriented: boolean): TBGRACustomBitmap;
     735begin
     736  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     737  FilterBlurMotion(bmp,rect(0,0,bmp.Width,bmp.Height),distance,angle,oriented,result,nil);
     738end;
     739
     740function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     741  ADistance, AAngle: single; AOriented: boolean): TFilterTask;
     742begin
     743  result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented);
    486744end;
    487745
    488746{ General purpose blur : compute pixel sum according to the mask and then
    489747  compute only difference while scanning from the left to the right }
    490 function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    491   blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
    492 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    493   blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap; forward;
    494 function FilterBlurBigMask(bmp: TBGRACustomBitmap;
    495   blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
     748procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     749  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     750procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     751  blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     752procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
     753  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     754procedure FilterBlurMask64(bmp: TBGRACustomBitmap;
     755  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    496756
    497757//make sure value is in the range 0..255
    498 function clampByte(value: integer): byte; inline;
     758function clampByte(value: Int32or64): byte; inline;
    499759begin
    500760  if value < 0 then result := 0 else
     
    505765function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer;
    506766  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
    507 var yb,xb, xs,ys, tx,ty: integer;
     767var yb,xb, xs,ys, tx,ty: Int32or64;
    508768    psrc,pdest: PBGRAPixel;
    509769    temp,stretched: TBGRACustomBitmap;
     
    531791      psrc := bmp.scanline[ys]+xs;
    532792      inc(ys,pixelSize);
    533       for xb := 0 to temp.width-1 do
     793      for xb := temp.width-1 downto 0 do
    534794      begin
    535795        pdest^ := psrc^;
     
    560820end;
    561821
    562 function FilterBlur(bmp: TBGRACustomBitmap;
    563   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     822function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     823begin
     824  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     825  FilterBlur(bmp,rect(0,0,bmp.Width,bmp.Height),blurMask,result,nil);
     826end;
     827
     828function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     829  AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean): TFilterTask;
     830begin
     831  result := TCustomBlurTask.Create(ABmp,ABounds,AMask,AMaskIsThreadSafe);
     832end;
     833
     834procedure FilterBlur(bmp: TBGRACustomBitmap;
     835  ABounds: TRect; blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     836{$IFDEF CPU64}
     837begin
     838    FilterBlurMask64(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
     839end;
     840{$ELSE}
    564841var
    565842  maskSum: int64;
    566   i: Integer;
     843  i: Int32or64;
    567844  p: PBGRAPixel;
    568845  maskShift: integer;
     
    583860  //check if sum can be stored in a 32-bit signed integer
    584861  if maskShift = 0 then
    585     result := FilterBlurSmallMask(bmp,blurMask) else
     862    FilterBlurSmallMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop) else
     863  {$IFDEF CPU32}
    586864  if maskShift < 8 then
    587     result := FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift) else
    588     result := FilterBlurBigMask(bmp,blurMask);
    589 end;
     865    FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift,ABounds,ADestination,ACheckShouldStop) else
     866  {$ENDIF}
     867    FilterBlurBigMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
     868end;
     869{$ENDIF}
    590870
    591871//32-bit blur with shift
    592 function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    593   blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap;
     872procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     873  blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    594874
    595875  var
     
    613893
    614894//32-bit blur
    615 function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    616   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     895procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     896  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    617897
    618898  var
     
    634914  {$I blurnormal.inc}
    635915
     916//64-bit blur
     917procedure FilterBlurMask64(bmp: TBGRACustomBitmap;
     918  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
     919
     920  var
     921    sumR, sumG, sumB, sumA, Adiv : int64;
     922
     923  function ComputeAverage: TBGRAPixel; inline;
     924  begin
     925    result.alpha := (sumA + Adiv shr 1) div Adiv;
     926    if result.alpha = 0 then
     927      result := BGRAPixelTransparent
     928    else
     929    begin
     930      result.red   := clampByte((sumR + sumA shr 1) div sumA);
     931      result.green := clampByte((sumG + sumA shr 1) div sumA);
     932      result.blue  := clampByte((sumB + sumA shr 1) div sumA);
     933    end;
     934  end;
     935
     936  {$I blurnormal.inc}
     937
    636938//floating point blur
    637 function FilterBlurBigMask(bmp: TBGRACustomBitmap;
    638   blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     939procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
     940  blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    639941
    640942  var
     
    655957
    656958  {$I blurnormal.inc}
     959function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     960begin
     961  result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height));
     962end;
    657963
    658964{ Emboss filter computes the difference between each pixel and the surrounding pixels
    659965  in the specified direction. }
    660 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     966function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    661967var
    662   yb, xb: integer;
     968  yb, xb: Int32or64;
    663969  dx, dy: single;
    664   idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: integer;
     970  idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: Int32or64;
    665971  w:      array[1..4] of single;
    666   iw:     cardinal;
     972  iw:     uint32or64;
    667973  c:      array[0..4] of TBGRAPixel;
    668974
    669   i:     integer;
    670   sumR, sumG, sumB, sumA, RGBdiv, Adiv: cardinal;
     975  i:     Int32or64;
     976  sumR, sumG, sumB, sumA, RGBdiv, Adiv: UInt32or64;
    671977  tempPixel, refPixel: TBGRAPixel;
    672978  pdest: PBGRAPixel;
    673979
    674980  bounds: TRect;
    675 begin
     981  onHorizBorder: boolean;
     982  psrc: array[-1..1] of PBGRAPixel;
     983begin
     984  if IsRectEmpty(ABounds) then exit;
    676985  //compute pixel position and weight
    677986  dx   := cos(angle * Pi / 180);
     
    6961005
    6971006  bounds := bmp.GetImageBounds;
    698   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    699     exit;
     1007  if not IntersectRect(bounds, bounds, ABounds) then exit;
    7001008  bounds.Left   := max(0, bounds.Left - 1);
    7011009  bounds.Top    := max(0, bounds.Top - 1);
     
    7071015  begin
    7081016    pdest := Result.scanline[yb] + bounds.Left;
     1017    onHorizBorder:= (yb=0) or (yb=bmp.Height-1);
     1018    psrc[0] := bmp.ScanLine[yb]+bounds.Left;
     1019    if (yb>0) then psrc[-1] := bmp.ScanLine[yb-1]+bounds.Left else psrc[-1] := nil;
     1020    if (yb<bmp.Height-1) then psrc[1] := bmp.ScanLine[yb+1]+bounds.Left else psrc[1] := nil;
    7091021    for xb := bounds.Left to bounds.Right - 1 do
    7101022    begin
    711       c[0] := bmp.getPixel(xb, yb);
    712       c[1] := bmp.getPixel(integer(xb + idx1), integer(yb + idy1));
    713       c[2] := bmp.getPixel(integer(xb + idx2), integer(yb + idy2));
    714       c[3] := bmp.getPixel(integer(xb + idx3), integer(yb + idy3));
    715       c[4] := bmp.getPixel(integer(xb + idx4), integer(yb + idy4));
     1023      c[0] := psrc[0]^;
     1024      if onHorizBorder or (xb=0) or (xb=bmp.Width-1) then
     1025      begin
     1026        c[1] := bmp.getPixel(xb + idx1, yb + idy1);
     1027        c[2] := bmp.getPixel(xb + idx2, yb + idy2);
     1028        c[3] := bmp.getPixel(xb + idx3, yb + idy3);
     1029        c[4] := bmp.getPixel(xb + idx4, yb + idy4);
     1030      end else
     1031      begin
     1032        c[1] := (psrc[idy1]+idx1)^;
     1033        c[2] := (psrc[idy2]+idx2)^;
     1034        c[3] := (psrc[idy3]+idx3)^;
     1035        c[4] := (psrc[idy4]+idx4)^;
     1036      end;
    7161037
    7171038      sumR   := 0;
     
    7621083      pdest^ := tempPixel;
    7631084      Inc(pdest);
     1085      inc(psrc[0]);
     1086      if psrc[-1] <> nil then inc(psrc[-1]);
     1087      if psrc[1] <> nil then inc(psrc[1]);
    7641088    end;
    7651089  end;
     
    7711095  FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
    7721096var
    773   yb, xb: integer;
    774   c0,c1,c2,c3,c4,c5,c6: integer;
    775 
    776   bmpWidth, bmpHeight: integer;
     1097  yb, xb: Int32or64;
     1098  c0,c1,c2,c3,c4,c5,c6: Int32or64;
     1099
     1100  bmpWidth, bmpHeight: Int32or64;
    7771101  slope, h: byte;
    778   sum:      integer;
     1102  sum:      Int32or64;
    7791103  tempPixel, highlight: TBGRAPixel;
    7801104  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    7821106  bounds: TRect;
    7831107  borderColorOverride: boolean;
    784   borderColorLevel: integer;
    785 
    786   currentBorderColor: integer;
     1108  borderColorLevel: Int32or64;
     1109
     1110  currentBorderColor: Int32or64;
    7871111begin
    7881112  borderColorOverride := DefineBorderColor.alpha <> 0;
     
    9051229  FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
    9061230var
    907   yb, xb: integer;
    908   c0,c1,c2,c3,c4,c5,c6: integer;
    909 
    910   bmpWidth, bmpHeight: integer;
     1231  yb, xb: int32or64;
     1232  c0,c1,c2,c3,c4,c5,c6: int32or64;
     1233
     1234  bmpWidth, bmpHeight: int32or64;
    9111235  slope, h: byte;
    912   sum:      integer;
     1236  sum:      int32or64;
    9131237  tempPixel, highlight: TBGRAPixel;
    9141238  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    9161240  bounds: TRect;
    9171241  borderColorOverride: boolean;
    918   borderColorLevel: integer;
    919 
    920   currentBorderColor: integer;
     1242  borderColorLevel: int32or64;
     1243
     1244  currentBorderColor: int32or64;
    9211245begin
    9221246  borderColorOverride := DefineBorderColor.alpha <> 0;
     
    10421366end;
    10431367
     1368function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean
     1369  ): TBGRACustomBitmap;
     1370begin
     1371  result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel);
     1372end;
     1373
    10441374{ Normalize compute min-max of specified channel and apply an affine transformation
    10451375  to make it use the full range of values }
    1046 function FilterNormalize(bmp: TBGRACustomBitmap;
     1376function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
    10471377  eachChannel: boolean = True): TBGRACustomBitmap;
    10481378var
    10491379  psrc, pdest: PBGRAPixel;
    10501380  c: TExpandedPixel;
    1051   n: integer;
     1381  xcount,xb,yb: int32or64;
    10521382  minValRed, maxValRed, minValGreen, maxValGreen, minValBlue, maxValBlue,
    10531383  minAlpha, maxAlpha, addValRed, addValGreen, addValBlue, addAlpha: word;
    1054   factorValRed, factorValGreen, factorValBlue, factorAlpha: integer;
    1055 begin
     1384  factorValRed, factorValGreen, factorValBlue, factorAlpha: int32or64;
     1385begin
     1386  if not IntersectRect(ABounds,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit;
    10561387  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    10571388  bmp.LoadFromBitmapIfNeeded;
    1058   psrc      := bmp.Data;
    10591389  maxValRed := 0;
    10601390  minValRed := 65535;
     
    10651395  maxAlpha  := 0;
    10661396  minAlpha  := 65535;
    1067   for n := bmp.Width * bmp.Height - 1 downto 0 do
    1068   begin
    1069     c := GammaExpansion(psrc^);
    1070     Inc(psrc);
    1071     if c.red > maxValRed then
    1072       maxValRed := c.red;
    1073     if c.green > maxValGreen then
    1074       maxValGreen := c.green;
    1075     if c.blue > maxValBlue then
    1076       maxValBlue := c.blue;
    1077     if c.red < minValRed then
    1078       minValRed := c.red;
    1079     if c.green < minValGreen then
    1080       minValGreen := c.green;
    1081     if c.blue < minValBlue then
    1082       minValBlue := c.blue;
    1083 
    1084     if c.alpha > maxAlpha then
    1085       maxAlpha := c.alpha;
    1086     if c.alpha < minAlpha then
    1087       minAlpha := c.alpha;
     1397  xcount := ABounds.Right-ABounds.Left;
     1398  for yb := ABounds.Top to ABounds.Bottom-1 do
     1399  begin
     1400    psrc := bmp.ScanLine[yb]+ABounds.Left;
     1401    for xb := xcount-1 downto 0 do
     1402    begin
     1403      c := GammaExpansion(psrc^);
     1404      Inc(psrc);
     1405      if c.red > maxValRed then
     1406        maxValRed := c.red;
     1407      if c.green > maxValGreen then
     1408        maxValGreen := c.green;
     1409      if c.blue > maxValBlue then
     1410        maxValBlue := c.blue;
     1411      if c.red < minValRed then
     1412        minValRed := c.red;
     1413      if c.green < minValGreen then
     1414        minValGreen := c.green;
     1415      if c.blue < minValBlue then
     1416        minValBlue := c.blue;
     1417
     1418      if c.alpha > maxAlpha then
     1419        maxAlpha := c.alpha;
     1420      if c.alpha < minAlpha then
     1421        minAlpha := c.alpha;
     1422    end;
    10881423  end;
    10891424  if not eachChannel then
     
    11491484  end;
    11501485
    1151   psrc  := bmp.Data;
    1152   pdest := Result.Data;
    1153   for n := bmp.Width * bmp.Height - 1 downto 0 do
    1154   begin
    1155     c := GammaExpansion(psrc^);
    1156     Inc(psrc);
    1157     c.red   := ((c.red - minValRed) * factorValRed + 2047) shr 12 + addValRed;
    1158     c.green := ((c.green - minValGreen) * factorValGreen + 2047) shr 12 + addValGreen;
    1159     c.blue  := ((c.blue - minValBlue) * factorValBlue + 2047) shr 12 + addValBlue;
    1160     c.alpha := ((c.alpha - minAlpha) * factorAlpha + 2047) shr 12 + addAlpha;
    1161     pdest^  := GammaCompression(c);
    1162     Inc(pdest);
     1486  for yb := ABounds.Top to ABounds.Bottom-1 do
     1487  begin
     1488    psrc := bmp.ScanLine[yb]+ABounds.Left;
     1489    pdest := Result.ScanLine[yb]+ABounds.Left;
     1490    for xb := xcount-1 downto 0 do
     1491    begin
     1492      c := GammaExpansion(psrc^);
     1493      Inc(psrc);
     1494      c.red   := ((c.red - minValRed) * factorValRed + 2047) shr 12 + addValRed;
     1495      c.green := ((c.green - minValGreen) * factorValGreen + 2047) shr 12 + addValGreen;
     1496      c.blue  := ((c.blue - minValBlue) * factorValBlue + 2047) shr 12 + addValBlue;
     1497      c.alpha := ((c.alpha - minAlpha) * factorAlpha + 2047) shr 12 + addAlpha;
     1498      pdest^  := GammaCompression(c);
     1499      Inc(pdest);
     1500    end;
    11631501  end;
    11641502  Result.InvalidateBitmap;
     
    11681506  calculates the position in the source bitmap with an affine transformation }
    11691507function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    1170   angle: single): TBGRACustomBitmap;
     1508  angle: single; correctBlur: boolean): TBGRACustomBitmap;
    11711509var
    11721510  bounds:     TRect;
     
    11751513  savexysrc, pt: TPointF;
    11761514  dx, dy:     single;
    1177   xb, yb:     integer;
     1515  xb, yb:     int32or64;
    11781516  minx, miny, maxx, maxy: single;
     1517  rf : TResampleFilter;
    11791518
    11801519  function RotatePos(x, y: single): TPointF;
     
    11881527
    11891528begin
    1190   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    11911529  bounds := bmp.GetImageBounds;
    11921530  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     1531  begin
     1532    Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    11931533    exit;
     1534  end;
     1535
     1536  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     1537  if correctBlur then rf := rfHalfCosine else rf := rfLinear;
    11941538
    11951539  //compute new bounding rectangle
     
    12511595    for xb := bounds.left to bounds.right - 1 do
    12521596    begin
    1253       pdest^ := bmp.GetPixel(xsrc, ysrc);
     1597      pdest^ := bmp.GetPixel(xsrc, ysrc, rf);
    12541598      Inc(pdest);
    12551599      xsrc += dx;
     
    12631607
    12641608{ Filter grayscale applies BGRAToGrayscale function to all pixels }
    1265 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     1609procedure FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    12661610var
    1267   bounds:      TRect;
    12681611  pdest, psrc: PBGRAPixel;
    1269   xb, yb:      integer;
    1270 
    1271 begin
    1272   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1273   bounds := bmp.GetImageBounds;
    1274   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1275     exit;
    1276 
    1277   for yb := bounds.Top to bounds.bottom - 1 do
    1278   begin
    1279     pdest := Result.scanline[yb] + bounds.left;
    1280     psrc  := bmp.scanline[yb] + bounds.left;
    1281     for xb := bounds.left to bounds.right - 1 do
     1612  xb, yb:      int32or64;
     1613
     1614begin
     1615  if IsRectEmpty(ABounds) then exit;
     1616
     1617  for yb := ABounds.Top to ABounds.bottom - 1 do
     1618  begin
     1619    if Assigned(ACheckShouldStop) and ACheckShouldStop(yb) then break;
     1620    pdest := ADestination.scanline[yb] + ABounds.left;
     1621    psrc  := bmp.scanline[yb] + ABounds.left;
     1622    for xb := ABounds.left to ABounds.right - 1 do
    12821623    begin
    12831624      pdest^ := BGRAToGrayscale(psrc^);
     
    12861627    end;
    12871628  end;
    1288   Result.InvalidateBitmap;
     1629  ADestination.InvalidateBitmap;
     1630end;
     1631
     1632function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     1633begin
     1634  result := FilterGrayscale(bmp, rect(0,0,bmp.width,bmp.Height));
     1635end;
     1636
     1637function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     1638begin
     1639  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     1640  FilterGrayscale(bmp,ABounds,result,nil);
     1641end;
     1642
     1643function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect
     1644  ): TFilterTask;
     1645begin
     1646  result := TGrayscaleTask.Create(bmp,ABounds);
    12891647end;
    12901648
     
    12941652function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    12951653var
    1296   yb, xb: integer;
     1654  yb, xb: int32or64;
    12971655  c:      array[0..8] of TBGRAPixel;
    12981656
    1299   i, bmpWidth, bmpHeight: integer;
     1657  i, bmpWidth, bmpHeight: int32or64;
    13001658  slope: byte;
    1301   sum:   integer;
     1659  sum:   int32or64;
    13021660  tempPixel: TBGRAPixel;
    13031661  pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
     
    14121770var
    14131771  cx, cy, x, y, len, fact: single;
    1414   xb, yb: integer;
     1772  xb, yb: int32or64;
    14151773  mask:   TBGRACustomBitmap;
    14161774begin
     
    14431801
    14441802{ Applies twirl scanner. See TBGRATwirlScanner }
    1445 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     1803function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    14461804var twirl: TBGRATwirlScanner;
    14471805begin
    14481806  twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);
    14491807  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1450   result.Fill(twirl);
     1808  result.FillRect(ABounds, twirl, dmSet);
    14511809  twirl.free;
     1810end;
     1811
     1812function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint;
     1813  ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;
     1814begin
     1815  result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent);
    14521816end;
    14531817
     
    14581822var
    14591823  cx, cy, x, y, len, fact: single;
    1460   xb, yb: integer;
     1824  xb, yb: int32or64;
    14611825begin
    14621826  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     
    14851849var
    14861850  cy, x1, x2, y1, y2, z1, z2, h: single;
    1487   yb: integer;
     1851  yb: int32or64;
    14881852  resampledBmp: TBGRACustomBitmap;
    1489   resampledBmpWidth: integer;
     1853  resampledBmpWidth: int32or64;
    14901854  resampledFactor,newResampleFactor: single;
    14911855  sub,resampledSub: TBGRACustomBitmap;
    14921856  partRect: TRect;
    1493   resampleSizeY : integer;
     1857  resampleSizeY : int32or64;
    14941858begin
    14951859  resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch);
     
    15541918  begin
    15551919    if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then
    1556       Result := (integer(p1.red) shl 8) + (integer(p1.green) shl 16) +
    1557         integer(p1.blue) < (integer(p2.red) shl 8) + (integer(p2.green) shl 16) +
    1558         integer(p2.blue)
     1920      Result := (int32or64(p1.red) shl 8) + (int32or64(p1.green) shl 16) +
     1921        int32or64(p1.blue) < (int32or64(p2.red) shl 8) + (int32or64(p2.green) shl 16) +
     1922        int32or64(p2.blue)
    15591923    else
    15601924      Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue);
     
    15641928  nbpix = 9;
    15651929var
    1566   yb, xb:    integer;
    1567   dx, dy, n, i, j, k: integer;
     1930  yb, xb:    int32or64;
     1931  dx, dy, n, i, j, k: int32or64;
    15681932  a_pixels:  array[0..nbpix - 1] of TBGRAPixel;
    15691933  tempPixel, refPixel: TBGRAPixel;
    15701934  tempValue: byte;
    1571   sumR, sumG, sumB, sumA, BGRAdiv, nbA: cardinal;
     1935  sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64;
    15721936  tempAlpha: word;
    15731937  bounds:    TRect;
     
    15931957        for dx := -1 to 1 do
    15941958        begin
    1595           a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
     1959          a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
    15961960          if a_pixels[n].alpha = 0 then
    15971961            a_pixels[n] := BGRAPixelTransparent;
     
    16952059end;
    16962060
     2061constructor TBoxBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2062  radius: integer);
     2063begin
     2064  FSource := bmp;
     2065  FBounds := ABounds;
     2066  FRadius := radius;
     2067end;
     2068
     2069procedure TBoxBlurTask.DoExecute;
     2070type
     2071  TVertical = record red,green,blue,alpha,count: NativeUint; end;
     2072  PVertical = ^TVertical;
     2073var
     2074  verticals: PVertical;
     2075  left,right,width,height: NativeInt;
     2076  delta: PtrInt;
     2077
     2078  procedure PrepareVerticals;
     2079  var
     2080    xb,yb: NativeInt;
     2081    psrc,p: PBGRAPixel;
     2082    pvert : PVertical;
     2083  begin
     2084    fillchar(verticals^, width*sizeof(TVertical), 0);
     2085    psrc := FSource.ScanLine[FBounds.Top];
     2086    pvert := verticals;
     2087    for xb := left to right-1 do
     2088    begin
     2089      p := psrc+xb;
     2090      for yb := 0 to FRadius-1 do
     2091      begin
     2092        if yb = height then break;
     2093        if p^.alpha <> 0 then
     2094        begin
     2095          pvert^.red += p^.red * p^.alpha;
     2096          pvert^.green += p^.green * p^.alpha;
     2097          pvert^.blue += p^.blue * p^.alpha;
     2098          pvert^.alpha += p^.alpha;
     2099        end;
     2100        inc(pvert^.count);
     2101        PByte(p) += delta;
     2102      end;
     2103      inc(pvert);
     2104    end;
     2105  end;
     2106
     2107  procedure NextVerticals(y: integer);
     2108  var
     2109    psrc1,psrc2: PBGRAPixel;
     2110    pvert : PVertical;
     2111    xb: NativeInt;
     2112  begin
     2113    pvert := verticals;
     2114    if y-FRadius-1 >= 0 then
     2115      psrc1 := FSource.ScanLine[y-FRadius-1]
     2116    else
     2117      psrc1 := nil;
     2118    if y+FRadius < FSource.Height then
     2119      psrc2 := FSource.ScanLine[y+FRadius]
     2120    else
     2121      psrc2 := nil;
     2122    for xb := width-1 downto 0 do
     2123    begin
     2124      if psrc1 <> nil then
     2125      begin
     2126        if psrc1^.alpha <> 0 then
     2127        begin
     2128          {$HINTS OFF}
     2129          pvert^.red -= psrc1^.red * psrc1^.alpha;
     2130          pvert^.green -= psrc1^.green * psrc1^.alpha;
     2131          pvert^.blue -= psrc1^.blue * psrc1^.alpha;
     2132          pvert^.alpha -= psrc1^.alpha;
     2133          {$HINTS ON}
     2134        end;
     2135        dec(pvert^.count);
     2136        inc(psrc1);
     2137      end;
     2138      if psrc2 <> nil then
     2139      begin
     2140        if psrc2^.alpha <> 0 then
     2141        begin
     2142          pvert^.red += psrc2^.red * psrc2^.alpha;
     2143          pvert^.green += psrc2^.green * psrc2^.alpha;
     2144          pvert^.blue += psrc2^.blue * psrc2^.alpha;
     2145          pvert^.alpha += psrc2^.alpha;
     2146        end;
     2147        inc(pvert^.count);
     2148        inc(psrc2);
     2149      end;
     2150      inc(pvert);
     2151    end;
     2152  end;
     2153
     2154  procedure MainLoop;
     2155  var
     2156    xb,yb,xdest: NativeInt;
     2157    pdest: PBGRAPixel;
     2158    pvert : PVertical;
     2159    sumRed,sumGreen,sumBlue,sumAlpha,sumCount: NativeUInt;
     2160  begin
     2161    for yb := FBounds.Top to FBounds.Bottom-1 do
     2162    begin
     2163      NextVerticals(yb);
     2164      if GetShouldStop(yb) then exit;
     2165      pdest := Destination.ScanLine[yb]+left;
     2166      sumRed := 0;
     2167      sumGreen := 0;
     2168      sumBlue := 0;
     2169      sumAlpha := 0;
     2170      sumCount := 0;
     2171      pvert := verticals;
     2172      for xb := 0 to FRadius-1 do
     2173      begin
     2174        if xb = width then break;
     2175        sumRed += pvert^.red;
     2176        sumGreen += pvert^.green;
     2177        sumBlue += pvert^.blue;
     2178        sumAlpha += pvert^.alpha;
     2179        sumCount += pvert^.count;
     2180        inc(pvert);
     2181      end;
     2182      for xdest := 0 to width-1 do
     2183      begin
     2184        if xdest-FRadius-1 >= 0 then
     2185        begin
     2186          pvert := verticals+(xdest-FRadius-1);
     2187          sumRed -= pvert^.red;
     2188          sumGreen -= pvert^.green;
     2189          sumBlue -= pvert^.blue;
     2190          sumAlpha -= pvert^.alpha;
     2191          sumCount -= pvert^.count;
     2192        end;
     2193        if xdest+FRadius < width then
     2194        begin
     2195          pvert := verticals+(xdest+FRadius);
     2196          sumRed += pvert^.red;
     2197          sumGreen += pvert^.green;
     2198          sumBlue += pvert^.blue;
     2199          sumAlpha += pvert^.alpha;
     2200          sumCount += pvert^.count;
     2201        end;
     2202        if (sumCount > 0) and (sumAlpha >= (sumCount+1) shr 1) then
     2203        begin
     2204          pdest^.red := (sumRed+(sumAlpha shr 1)) div sumAlpha;
     2205          pdest^.green := (sumGreen+(sumAlpha shr 1)) div sumAlpha;
     2206          pdest^.blue := (sumBlue+(sumAlpha shr 1)) div sumAlpha;
     2207          pdest^.alpha := (sumAlpha+(sumCount shr 1)) div sumCount;
     2208        end else
     2209          pdest^ := BGRAPixelTransparent;
     2210        inc(pdest);
     2211      end;
     2212    end;
     2213  end;
     2214
     2215begin
     2216  if (FBounds.Right <= FBounds.Left) or (FBounds.Bottom <= FBounds.Top) or (FRadius <= 0) then exit;
     2217  left := FBounds.left;
     2218  right := FBounds.right;
     2219  width := right-left;
     2220  height := FBounds.bottom-FBounds.top;
     2221  delta := FSource.Width*SizeOf(TBGRAPixel);
     2222  if FSource.LineOrder = riloBottomToTop then delta := -delta;
     2223
     2224  getmem(verticals, width*sizeof(TVertical));
     2225  try
     2226    PrepareVerticals;
     2227    MainLoop;
     2228  finally
     2229    freemem(verticals);
     2230  end;
     2231end;
     2232
     2233constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect);
     2234begin
     2235  FSource := bmp;
     2236  FBounds := ABounds;
     2237end;
     2238
     2239procedure TGrayscaleTask.DoExecute;
     2240begin
     2241  FilterGrayscale(FSource,FBounds,Destination,@GetShouldStop);
     2242end;
     2243
     2244{ TCustomBlurTask }
     2245
     2246constructor TCustomBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2247  AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean);
     2248begin
     2249  FSource := bmp;
     2250  FBounds := ABounds;
     2251  if AMaskIsThreadSafe then
     2252  begin
     2253    FMask := AMask;
     2254    FMaskOwned := false;
     2255  end else
     2256  begin
     2257    FMask := AMask.Duplicate;
     2258    FMaskOwned := true;
     2259  end;
     2260end;
     2261
     2262destructor TCustomBlurTask.Destroy;
     2263begin
     2264  If FMaskOwned then FreeAndNil(FMask);
     2265  inherited Destroy;
     2266end;
     2267
     2268procedure TCustomBlurTask.DoExecute;
     2269begin
     2270  FilterBlur(FSource,FBounds,FMask,Destination,@GetShouldStop);
     2271end;
     2272
     2273constructor TMotionBlurTask.Create(ABmp: TBGRACustomBitmap; ABounds: TRect;
     2274  ADistance, AAngle: single; AOriented: boolean);
     2275begin
     2276  FSource := ABmp;
     2277  FBounds := ABounds;
     2278  FDistance := ADistance;
     2279  FAngle := AAngle;
     2280  FOriented:= AOriented;
     2281end;
     2282
     2283procedure TMotionBlurTask.DoExecute;
     2284begin
     2285  FilterBlurMotion(FSource,FBounds,FDistance,FAngle,FOriented,Destination,@GetShouldStop);
     2286end;
     2287
     2288constructor TRadialPreciseBlurTask.Create(bmp: TBGRACustomBitmap;
     2289  ABounds: TRect; radius: single);
     2290begin
     2291  FSource := bmp;
     2292  FBounds := ABounds;
     2293  FRadius := radius;
     2294end;
     2295
     2296procedure TRadialPreciseBlurTask.DoExecute;
     2297begin
     2298  FilterBlurRadialPrecise(FSource,FBounds,FRadius,Destination,@GetShouldStop);
     2299end;
     2300
     2301{ TRadialBlurTask }
     2302
     2303constructor TRadialBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
     2304  radius: integer; blurType: TRadialBlurType);
     2305begin
     2306  FSource := bmp;
     2307  FBounds := ABounds;
     2308  FRadius := radius;
     2309  FBlurType:= blurType;
     2310end;
     2311
     2312procedure TRadialBlurTask.DoExecute;
     2313begin
     2314  FilterBlurRadial(FSource,FBounds,FRadius,FBlurType,Destination,@GetShouldStop);
     2315end;
     2316
     2317{ TFilterTask }
     2318
     2319function TFilterTask.GetShouldStop(ACurrentY: integer): boolean;
     2320begin
     2321  FCurrentY:= ACurrentY;
     2322  if Assigned(FCheckShouldStop) then
     2323    result := FCheckShouldStop(ACurrentY)
     2324  else
     2325    result := false;
     2326end;
     2327
     2328function TFilterTask.Execute: TBGRACustomBitmap;
     2329var DestinationOwned: boolean;
     2330begin
     2331  FCurrentY := 0;
     2332  if Destination = nil then
     2333  begin
     2334    FDestination := FSource.NewBitmap(FSource.Width,FSource.Height);
     2335    DestinationOwned:= true;
     2336  end else
     2337    DestinationOwned:= false;
     2338  try
     2339    DoExecute;
     2340    result := Destination;
     2341    FDestination := nil;
     2342  except
     2343    on ex: exception do
     2344    begin
     2345      if DestinationOwned then FreeAndNil(FDestination);
     2346      raise ex;
     2347    end;
     2348  end;
     2349end;
     2350
     2351procedure TFilterTask.SetDestination(AValue: TBGRACustomBitmap);
     2352begin
     2353  if FDestination <> nil then
     2354    raise exception.Create('Destination is already defined');
     2355  FDestination := AValue;
     2356end;
     2357
    16972358end.
    16982359
Note: See TracChangeset for help on using the changeset viewer.