Ignore:
Timestamp:
Feb 1, 2012, 3:02:33 PM (12 years ago)
Author:
chronos
Message:
  • Modified: Updated BGRABitmap package to version 5.5.
  • Modified: Removed draw method ComboBox and reorganized method list to single listview with using ownerdraw facility.
  • Added: New draw method TBitmap.RawImage.Data Move which use fast Move operation. It requires same pixel format.
  • Added: New draw method Dummy for comparion of empty method and to determine possibily max frame rate limit.
Location:
GraphicTest/BGRABitmap
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/BGRABitmap

    • Property svn:ignore set to
      lib
  • GraphicTest/BGRABitmap/bgrafilters.pas

    r210 r317  
    55interface
    66
     7{ Here are some filters that can be applied to a bitmap. The filters
     8  take a source image as a parameter and gives a filtered image as
     9  a result. }
     10
    711uses
    8   Classes, SysUtils, BGRADefaultBitmap, BGRABitmapTypes;
    9 
    10 function FilterMedian(bmp: TBGRADefaultBitmap;
    11   Option: TMedianOption): TBGRADefaultBitmap;
    12 function FilterSmartZoom3(bmp: TBGRADefaultBitmap;
    13   Option: TMedianOption): TBGRADefaultBitmap;
    14 function FilterSharpen(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
    15 function FilterBlurRadialPrecise(bmp: TBGRADefaultBitmap;
    16   radius: single): TBGRADefaultBitmap;
    17 function FilterBlurRadial(bmp: TBGRADefaultBitmap; radius: integer;
    18   blurType: TRadialBlurType): TBGRADefaultBitmap;
    19 function FilterBlurMotion(bmp: TBGRADefaultBitmap; distance: single;
    20   angle: single; oriented: boolean): TBGRADefaultBitmap;
    21 function FilterBlur(bmp: TBGRADefaultBitmap;
    22   blurMask: TBGRADefaultBitmap): TBGRADefaultBitmap;
    23 function FilterEmboss(bmp: TBGRADefaultBitmap; angle: single): TBGRADefaultBitmap;
    24 function FilterEmbossHighlight(bmp: TBGRADefaultBitmap;
    25   FillSelection: boolean): TBGRADefaultBitmap;
    26 function FilterNormalize(bmp: TBGRADefaultBitmap;
    27   eachChannel: boolean = True): TBGRADefaultBitmap;
    28 function FilterRotate(bmp: TBGRADefaultBitmap; origin: TPointF;
    29   angle: single): TBGRADefaultBitmap;
    30 function FilterGrayscale(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
    31 function FilterContour(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
    32 function FilterSphere(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
    33 function FilterCylinder(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
    34 function FilterPlane(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     12  Classes, SysUtils, BGRABitmapTypes;
     13
     14{ The median filter consist in calculating the median value of pixels. Here
     15  a square of 9x9 pixel is considered. The median allow to select the most
     16  representative colors. The option parameter allow to choose to smooth the
     17  result or not. }
     18function FilterMedian(bmp: TBGRACustomBitmap;
     19  Option: TMedianOption): TBGRACustomBitmap;
     20
     21{ SmartZoom x3 is a filter that upsizes 3 times the picture and add
     22  pixels that could be logically expected (horizontal, vertical, diagonal lines) }
     23function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     24  Option: TMedianOption): TBGRACustomBitmap;
     25
     26{ Sharpen filter add more contrast between pixels }
     27function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     28
     29{ A radial blur applies a blur with a circular influence, i.e, each pixel
     30  is merged with pixels within the specified radius. There is an exception
     31  with rbFast blur, the optimization entails an hyperbolic shape. }
     32function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
     33  blurType: TRadialBlurType): TBGRACustomBitmap;
     34
     35{ The precise blur allow to specify the blur radius with subpixel accuracy }
     36function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
     37  radius: single): TBGRACustomBitmap;
     38
     39{ Motion blur merge pixels in a direction. The oriented parameter specifies
     40  if the weights of the pixels are the same along the line or not. }
     41function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
     42  angle: single; oriented: boolean): TBGRACustomBitmap;
     43
     44function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
     45
     46{ General purpose blur filter, with a blur mask as parameter to describe
     47  how pixels influence each other }
     48function FilterBlur(bmp: TBGRACustomBitmap;
     49  blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     50
     51{ Emboss filter compute a color difference in the angle direction }
     52function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
     53
     54{ Emboss highlight computes a sort of emboss with 45 degrees angle and
     55  with standard selection color (white/black and filled with blue) }
     56function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
     57  FillSelection: boolean): TBGRACustomBitmap;
     58
     59{ Normalize use the whole available range of values, making dark colors darkest possible
     60  and light colors lightest possible }
     61function FilterNormalize(bmp: TBGRACustomBitmap;
     62  eachChannel: boolean = True): TBGRACustomBitmap;
     63
     64{ Rotate filter rotate the image and clip it in the bounding rectangle }
     65function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
     66  angle: single): TBGRACustomBitmap;
     67
     68{ Grayscale converts colored pixel into grayscale with same luminosity }
     69function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     70
     71{ Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil }
     72function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     73
     74{ Distort the image as if it were on a sphere }
     75function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     76
     77{ Twirl distortion, i.e. a progressive rotation }
     78function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     79
     80{ Distort the image as if it were on a vertical cylinder }
     81function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     82
     83{ Compute a plane projection towards infinity (SLOW) }
     84function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    3585
    3686implementation
    3787
    38 uses Math;
    39 
    40 function FilterSmartZoom3(bmp: TBGRADefaultBitmap;
    41   Option: TMedianOption): TBGRADefaultBitmap;
     88uses Math, GraphType, Dialogs, BGRATransform;
     89
     90function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     91  Option: TMedianOption): TBGRACustomBitmap;
    4292type
    4393  TSmartDiff = record
     
    4898  xb, yb: integer;
    4999  diag1, diag2, h1, h2, v1, v2: TSmartDiff;
    50   c:      TBGRAPixel;
    51   temp, median: TBGRADefaultBitmap;
     100  c,c1,c2:      TBGRAPixel;
     101  temp, median: TBGRACustomBitmap;
    52102
    53103  function ColorDiff(c1, c2: TBGRAPixel): single;
     
    156206        if diag1.cd < 0.3 then
    157207        begin
    158           c := MergeBGRA(bmp.GetPixel(xb, yb), bmp.GetPixel(xb + 1, yb + 1));
     208          c1 := bmp.GetPixel(xb, yb);
     209          c2 := bmp.GetPixel(integer(xb + 1), integer(yb + 1));
     210          c := MergeBGRA(c1, c2);
    159211          //restore
    160212          Result.SetPixel(xb * 3 + 2, yb * 3 + 2, bmp.GetPixel(xb, yb));
    161           Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(xb + 1, yb + 1));
     213          Result.SetPixel(xb * 3 + 3, yb * 3 + 3, bmp.GetPixel(integer(xb + 1), integer(yb + 1)));
    162214
    163215          if (diag1.sd < h1.sd) and (diag1.sd < v2.sd) then
     
    173225        if diag2.cd < 0.3 then
    174226        begin
    175           c := MergeBGRA(bmp.GetPixel(xb, yb + 1), bmp.GetPixel(xb + 1, yb));
     227          c1 := bmp.GetPixel(xb, yb + 1);
     228          c2 := bmp.GetPixel(xb + 1, yb);
     229          c := MergeBGRA(c1, c2);
    176230          //restore
    177231          Result.SetPixel(xb * 3 + 3, yb * 3 + 2, bmp.GetPixel(xb + 1, yb));
     
    190244end;
    191245
    192 function FilterSharpen(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     246{ This filter compute for each pixel the mean of the eight surrounding pixels,
     247  then the difference between this average pixel and the pixel at the center
     248  of the square. Finally the difference is added to the new pixel, exagerating
     249  its difference with its neighbours. }
     250function FilterSharpen(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    193251const
    194252  nbpix = 8;
     
    204262  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    205263
     264  //determine where pixels are in the bitmap
    206265  bounds := bmp.GetImageBounds;
    207266  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     
    212271  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
    213272
     273  //loop through the destination bitmap
    214274  for yb := bounds.Top to bounds.Bottom - 1 do
    215275  begin
     
    217277    for xb := bounds.Left to bounds.Right - 1 do
    218278    begin
     279      //for each pixel, read eight surrounding pixels in the source bitmap
    219280      n := 0;
    220281      for dy := -1 to 1 do
     
    222283          if (dx <> 0) or (dy <> 0) then
    223284          begin
    224             a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
     285            a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
    225286            Inc(n);
    226287          end;
    227288
     289      //compute sum
    228290      sumR   := 0;
    229291      sumG   := 0;
     
    246308       {$hints on}
    247309
     310      //we finally have an average pixel
    248311      if (RGBdiv = 0) then
    249312        refPixel := BGRAPixelTransparent
     
    256319      end;
    257320
     321      //read the pixel at the center of the square
    258322      tempPixel := bmp.GetPixel(xb, yb);
    259323      if refPixel <> BGRAPixelTransparent then
    260324      begin
     325        //compute sharpened pixel by adding the difference
    261326        tempPixel.red   := max(0, min(255, tempPixel.red +
    262327          integer(tempPixel.red - refPixel.red)));
     
    275340end;
    276341
    277 function FilterBlurRadialPrecise(bmp: TBGRADefaultBitmap;
    278   radius: single): TBGRADefaultBitmap;
    279 var
    280   blurShape: TBGRADefaultBitmap;
     342{ Precise blur builds a blur mask with a gradient fill and use
     343  general purpose blur }
     344function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
     345  radius: single): TBGRACustomBitmap;
     346var
     347  blurShape: TBGRACustomBitmap;
    281348  intRadius: integer;
    282349begin
     350  if radius = 0 then
     351  begin
     352    result := bmp.Duplicate;
     353    exit;
     354  end;
    283355  intRadius := ceil(radius);
    284   blurShape := TBGRADefaultBitmap.Create(2 * intRadius + 1, 2 * intRadius + 1);
     356  blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);
    285357  blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
    286358    BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF(
     
    290362end;
    291363
    292 function FilterBlurRadialNormal(bmp: TBGRADefaultBitmap;
    293   radius: integer): TBGRADefaultBitmap;
    294 var
    295   blurShape: TBGRADefaultBitmap;
    296 begin
    297   blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1);
     364{ This is a clever solution for fast computing of the blur
     365  effect : it stores an array of vertical sums forming a square
     366  around the pixel which moves with it. For each new pixel,
     367  the vertical sums are kept except for the last column of
     368  the square }
     369function FilterBlurFast(bmp: TBGRACustomBitmap;
     370  radius: integer): TBGRACustomBitmap;
     371
     372  type
     373    TRowSum = record
     374      sumR,sumG,sumB,rgbDiv,sumA,aDiv: cardinal;
     375    end;
     376
     377  function ComputeAverage(sum: TRowSum): TBGRAPixel;
     378  begin
     379    result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
     380    result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
     381    result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
     382    result.blue := (sum.sumB+sum.rgbDiv shr 1) div sum.rgbDiv;
     383  end;
     384
     385  {$I blurfast.inc}
     386
     387{ Normal radial blur compute a blur mask with a GradientFill and
     388  then posterize to optimize general purpose blur }
     389function FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
     390  radius: integer): TBGRACustomBitmap;
     391var
     392  blurShape: TBGRACustomBitmap;
     393  n: Integer;
     394  p: PBGRAPixel;
     395begin
     396  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    298397  blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
    299398    BGRABlack, gtRadial, pointF(radius, radius), pointF(-0.5, radius), dmSet);
     399  p := blurShape.Data;
     400  for n := 0 to blurShape.NbPixels-1 do
     401  begin
     402    p^.red := p^.red and $F0;
     403    p^.green := p^.red;
     404    p^.blue := p^.red;
     405    inc(p);
     406  end;
    300407  Result := FilterBlur(bmp, blurShape);
    301408  blurShape.Free;
    302409end;
    303410
    304 function FilterBlurDisk(bmp: TBGRADefaultBitmap; radius: integer): TBGRADefaultBitmap;
    305 var
    306   blurShape: TBGRADefaultBitmap;
    307 begin
    308   blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1);
     411{ Blur disk creates a disk mask with a FillEllipse }
     412function FilterBlurDisk(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     413var
     414  blurShape: TBGRACustomBitmap;
     415begin
     416  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    309417  blurShape.Fill(BGRABlack);
    310418  blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite);
     
    313421end;
    314422
    315 function FilterBlurCorona(bmp: TBGRADefaultBitmap; radius: integer): TBGRADefaultBitmap;
    316 var
    317   blurShape: TBGRADefaultBitmap;
    318 begin
    319   blurShape := TBGRADefaultBitmap.Create(2 * radius + 1, 2 * radius + 1);
     423{ Corona blur use a circle as mask }
     424function FilterBlurCorona(bmp: TBGRACustomBitmap; radius: integer): TBGRACustomBitmap;
     425var
     426  blurShape: TBGRACustomBitmap;
     427begin
     428  blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    320429  blurShape.Fill(BGRABlack);
    321430  blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1);
     
    324433end;
    325434
    326 function FilterBlurRadial(bmp: TBGRADefaultBitmap; radius: integer;
    327   blurType: TRadialBlurType): TBGRADefaultBitmap;
    328 begin
     435function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
     436  blurType: TRadialBlurType): TBGRACustomBitmap;
     437begin
     438  if radius = 0 then
     439  begin
     440    result := bmp.Duplicate;
     441    exit;
     442  end;
    329443  case blurType of
    330444    rbCorona: Result  := FilterBlurCorona(bmp, radius);
    331445    rbDisk: Result    := FilterBlurDisk(bmp, radius);
    332446    rbNormal: Result  := FilterBlurRadialNormal(bmp, radius);
     447    rbFast: Result  := FilterBlurFast(bmp, radius);
    333448    rbPrecise: Result := FilterBlurRadialPrecise(bmp, radius / 10);
    334449    else
     
    337452end;
    338453
    339 function FilterBlurMotion(bmp: TBGRADefaultBitmap; distance: single;
    340   angle: single; oriented: boolean): TBGRADefaultBitmap;
    341 var
    342   blurShape: TBGRADefaultBitmap;
     454{ This filter draws an antialiased line to make the mask, and
     455  if the motion blur is oriented, does a GradientFill to orient it }
     456function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
     457  angle: single; oriented: boolean): TBGRACustomBitmap;
     458var
     459  blurShape: TBGRACustomBitmap;
    343460  intRadius: integer;
    344461  dx, dy, d: single;
    345462begin
     463  if distance = 0 then
     464  begin
     465    result := bmp.Duplicate;
     466    exit;
     467  end;
    346468  intRadius := ceil(distance / 2);
    347   blurShape := TBGRADefaultBitmap.Create(2 * intRadius + 1, 2 * intRadius + 1);
     469  blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);
    348470  d  := distance / 2;
    349471  dx := cos(angle * Pi / 180);
     
    362484end;
    363485
    364 function FilterBlur(bmp: TBGRADefaultBitmap;
    365   blurMask: TBGRADefaultBitmap): TBGRADefaultBitmap;
    366 var
    367   yb, xb:      integer;
    368   dx, dy, mindx, maxdx, mindy, maxdy, n, j: integer;
    369   a_pixels:    array of TBGRAPixel;
    370   weights:     array of integer;
    371   sumR, sumG, sumB, sumA, Adiv, RGBdiv: cardinal;
    372   RGBweight:   byte;
    373   tempPixel, refPixel: TBGRAPixel;
    374   shapeMatrix: array of array of byte;
    375   pdest, psrc: PBGRAPixel;
    376   blurOfs:     TPoint;
    377   bounds:      TRect;
    378 begin
    379   blurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1);
    380 
    381   setlength(shapeMatrix, blurMask.Width, blurMask.Height);
    382   n := 0;
    383   for yb := 0 to blurMask.Height - 1 do
    384     for xb := 0 to blurMask.Width - 1 do
    385     begin
    386       shapeMatrix[yb, xb] := blurMask.GetPixel(xb, yb).red;
    387       if shapeMatrix[yb, xb] <> 0 then
    388         Inc(n);
    389     end;
    390 
    391   setlength(a_pixels, n);
    392   setlength(weights, n);
    393 
    394   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    395   bounds := bmp.GetImageBounds;
    396   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     486{ General purpose blur : compute pixel sum according to the mask and then
     487  compute only difference while scanning from the left to the right }
     488function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     489  blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
     490function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     491  blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap; forward;
     492function FilterBlurBigMask(bmp: TBGRACustomBitmap;
     493  blurMask: TBGRACustomBitmap): TBGRACustomBitmap; forward;
     494
     495//make sure value is in the range 0..255
     496function clampByte(value: integer): byte; inline;
     497begin
     498  if value < 0 then result := 0 else
     499  if value > 255 then result := 255 else
     500    result := value;
     501end;
     502
     503function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer;
     504  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
     505var yb,xb, xs,ys, tx,ty: integer;
     506    psrc,pdest: PBGRAPixel;
     507    temp,stretched: TBGRACustomBitmap;
     508    oldfilter: TResampleFilter;
     509begin
     510  if pixelSize < 1 then
     511  begin
     512    result := bmp.Duplicate;
    397513    exit;
    398   bounds.Left   := max(0, bounds.Left - blurOfs.X);
    399   bounds.Top    := max(0, bounds.Top - blurOfs.Y);
    400   bounds.Right  := min(bmp.Width, bounds.Right + blurMask.Width - 1 - blurOfs.X);
    401   bounds.Bottom := min(bmp.Height, bounds.Bottom + blurMask.Height - 1 - blurOfs.Y);
    402 
    403   for yb := bounds.Top to bounds.Bottom - 1 do
    404   begin
    405     pdest := Result.ScanLine[yb] + bounds.Left;
    406     for xb := bounds.Left to Bounds.Right - 1 do
    407     begin
    408       n     := 0;
    409       mindx := max(-blurOfs.X, -xb);
    410       mindy := max(-blurOfs.Y, -yb);
    411       maxdx := min(blurMask.Width - 1 - blurOfs.X, bmp.Width - 1 - xb);
    412       maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmp.Height - 1 - yb);
    413       for dy := mindy to maxdy do
    414       begin
    415         psrc := bmp.scanline[yb + dy] + (xb + mindx);
    416         for dx := mindx to maxdx do
    417         begin
    418           j := shapeMatrix[dy + blurOfs.Y, dx + blurOfs.X];
    419           if j <> 0 then
    420           begin
    421             a_pixels[n] := psrc^;
    422             weights[n]  := (a_pixels[n].alpha * j + 127) shr 8;
    423             Inc(n);
    424           end;
    425           Inc(psrc);
    426         end;
     514  end;
     515  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     516
     517  tx := (bmp.Width+pixelSize-1) div pixelSize;
     518  ty := (bmp.Height+pixelSize-1) div pixelSize;
     519  if not useResample then
     520  begin
     521    temp := bmp.NewBitmap(tx,ty);
     522
     523    xs := (bmp.Width mod pixelSize) div 2;
     524    ys := (bmp.Height mod pixelSize) div 2;
     525
     526    for yb := 0 to temp.height-1 do
     527    begin
     528      pdest := temp.ScanLine[yb];
     529      psrc := bmp.scanline[ys]+xs;
     530      inc(ys,pixelSize);
     531      for xb := 0 to temp.width-1 do
     532      begin
     533        pdest^ := psrc^;
     534        inc(pdest);
     535        inc(psrc,pixelSize);
    427536      end;
    428       sumR   := 0;
    429       sumG   := 0;
    430       sumB   := 0;
    431       sumA   := 0;
    432       Adiv   := 0;
    433       RGBdiv := 0;
    434 
    435        {$hints off}
    436       for j := 0 to n - 1 do
    437       begin
    438         tempPixel := a_pixels[j];
    439         RGBweight := (weights[j] * tempPixel.alpha + 128) div 255;
    440         sumR      += tempPixel.red * RGBweight;
    441         sumG      += tempPixel.green * RGBweight;
    442         sumB      += tempPixel.blue * RGBweight;
    443         RGBdiv    += RGBweight;
    444         sumA      += tempPixel.alpha;
    445         Adiv      += 1;
    446       end;
    447        {$hints on}
    448 
    449       if (Adiv = 0) or (RGBdiv = 0) then
    450         refPixel := BGRAPixelTransparent
    451       else
    452       begin
    453         refPixel.alpha := (sumA + Adiv shr 1) div Adiv;
    454         if refPixel.alpha = 0 then
    455           refPixel := BGRAPixelTransparent
    456         else
    457         begin
    458           refPixel.red   := (sumR + RGBdiv shr 1) div RGBdiv;
    459           refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv;
    460           refPixel.blue  := (sumB + RGBdiv shr 1) div RGBdiv;
    461         end;
    462       end;
    463 
    464       pdest^ := refPixel;
    465       Inc(pdest);
    466     end;
    467   end;
    468   Result.InvalidateBitmap;
    469 end;
    470 
    471 function FilterEmboss(bmp: TBGRADefaultBitmap; angle: single): TBGRADefaultBitmap;
     537    end;
     538    temp.InvalidateBitmap;
     539  end else
     540  begin
     541    oldfilter := bmp.ResampleFilter;
     542    bmp.ResampleFilter := filter;
     543    temp := bmp.Resample(tx,ty,rmFineResample);
     544    bmp.ResampleFilter := oldfilter;
     545  end;
     546  stretched := temp.Resample(temp.Width*pixelSize,temp.Height*pixelSize,rmSimpleStretch);
     547  temp.free;
     548  if bmp.Width mod pixelSize = 0 then
     549    xs := 0
     550  else
     551    xs := (-pixelSize+(bmp.Width mod pixelSize)) div 2;
     552  if bmp.Height mod pixelSize = 0 then
     553    ys := 0
     554  else
     555    ys := (-pixelSize+(bmp.Height mod pixelSize)) div 2;
     556  result.PutImage(xs,ys,stretched,dmSet);
     557  stretched.Free;
     558end;
     559
     560function FilterBlur(bmp: TBGRACustomBitmap;
     561  blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     562var
     563  maskSum: int64;
     564  i: Integer;
     565  p: PBGRAPixel;
     566  maskShift: integer;
     567begin
     568  maskSum := 0;
     569  p := blurMask.data;
     570  for i := 0 to blurMask.NbPixels-1 do
     571  begin
     572    inc(maskSum,p^.red);
     573    inc(p);
     574  end;
     575  maskShift := 0;
     576  while maskSum > 32768 do
     577  begin
     578    inc(maskShift);
     579    maskSum := maskSum shr 1;
     580  end;
     581  //check if sum can be stored in a 32-bit signed integer
     582  if maskShift = 0 then
     583    result := FilterBlurSmallMask(bmp,blurMask) else
     584  if maskShift < 8 then
     585    result := FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift) else
     586    result := FilterBlurBigMask(bmp,blurMask);
     587end;
     588
     589//32-bit blur with shift
     590function FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
     591  blurMask: TBGRACustomBitmap; maskShift: integer): TBGRACustomBitmap;
     592
     593  var
     594    sumR, sumG, sumB, sumA, Adiv, RGBdiv : integer;
     595
     596  function ComputeAverage: TBGRAPixel; inline;
     597  begin
     598    result.alpha := (sumA + Adiv shr 1) div Adiv;
     599    if result.alpha = 0 then
     600      result := BGRAPixelTransparent
     601    else
     602    begin
     603      result.red   := clampByte((sumR + RGBdiv shr 1) div RGBdiv);
     604      result.green := clampByte((sumG + RGBdiv shr 1) div RGBdiv);
     605      result.blue  := clampByte((sumB + RGBdiv shr 1) div RGBdiv);
     606    end;
     607  end;
     608
     609  {$define PARAM_MASKSHIFT}
     610  {$I blurnormal.inc}
     611
     612//32-bit blur
     613function FilterBlurSmallMask(bmp: TBGRACustomBitmap;
     614  blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     615
     616  var
     617    sumR, sumG, sumB, sumA, Adiv : integer;
     618
     619  function ComputeAverage: TBGRAPixel; inline;
     620  begin
     621    result.alpha := (sumA + Adiv shr 1) div Adiv;
     622    if result.alpha = 0 then
     623      result := BGRAPixelTransparent
     624    else
     625    begin
     626      result.red   := clampByte((sumR + sumA shr 1) div sumA);
     627      result.green := clampByte((sumG + sumA shr 1) div sumA);
     628      result.blue  := clampByte((sumB + sumA shr 1) div sumA);
     629    end;
     630  end;
     631
     632  {$I blurnormal.inc}
     633
     634//floating point blur
     635function FilterBlurBigMask(bmp: TBGRACustomBitmap;
     636  blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
     637
     638  var
     639    sumR, sumG, sumB, sumA, Adiv : single;
     640
     641  function ComputeAverage: TBGRAPixel; inline;
     642  begin
     643    result.alpha := round(sumA/Adiv);
     644    if result.alpha = 0 then
     645      result := BGRAPixelTransparent
     646    else
     647    begin
     648      result.red   := clampByte(round(sumR/sumA));
     649      result.green := clampByte(round(sumG/sumA));
     650      result.blue  := clampByte(round(sumB/sumA));
     651    end;
     652  end;
     653
     654  {$I blurnormal.inc}
     655
     656{ Emboss filter computes the difference between each pixel and the surrounding pixels
     657  in the specified direction. }
     658function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
    472659var
    473660  yb, xb: integer;
     
    475662  idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: integer;
    476663  w:      array[1..4] of single;
    477   iw:     integer;
     664  iw:     cardinal;
    478665  c:      array[0..4] of TBGRAPixel;
    479666
     
    485672  bounds: TRect;
    486673begin
     674  //compute pixel position and weight
    487675  dx   := cos(angle * Pi / 180);
    488676  dy   := sin(angle * Pi / 180);
     
    501689  w[4] := (1 - abs(idx4 - dx)) * (1 - abs(idy4 - dy));
    502690
     691  //fill with gray
    503692  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    504693  Result.Fill(BGRA(128, 128, 128, 255));
     
    512701  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
    513702
     703  //loop through destination
    514704  for yb := bounds.Top to bounds.bottom - 1 do
    515705  begin
     
    518708    begin
    519709      c[0] := bmp.getPixel(xb, yb);
    520       c[1] := bmp.getPixel(xb + idx1, yb + idy1);
    521       c[2] := bmp.getPixel(xb + idx2, yb + idy2);
    522       c[3] := bmp.getPixel(xb + idx3, yb + idy3);
    523       c[4] := bmp.getPixel(xb + idx4, yb + idy4);
     710      c[1] := bmp.getPixel(integer(xb + idx1), integer(yb + idy1));
     711      c[2] := bmp.getPixel(integer(xb + idx2), integer(yb + idy2));
     712      c[3] := bmp.getPixel(integer(xb + idx3), integer(yb + idy3));
     713      c[4] := bmp.getPixel(integer(xb + idx4), integer(yb + idy4));
    524714
    525715      sumR   := 0;
     
    530720      RGBdiv := 0;
    531721
     722      //compute sum
    532723       {$hints off}
    533724      for i := 1 to 4 do
     
    546737       {$hints on}
    547738
     739      //average
    548740      if (Adiv = 0) or (RGBdiv = 0) then
    549741        refPixel := c[0]
     
    555747        refPixel.alpha := (sumA * 255 + Adiv shr 1) div Adiv;
    556748      end;
     749
     750      //difference with center pixel
    557751       {$hints off}
    558752      tempPixel.red := max(0, min(512 * 255, 65536 + refPixel.red *
     
    571765end;
    572766
    573 function FilterEmbossHighlight(bmp: TBGRADefaultBitmap;
    574   FillSelection: boolean): TBGRADefaultBitmap;
     767{ Like general emboss, but with fixed direction and automatic color with transparency }
     768function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
     769  FillSelection: boolean): TBGRACustomBitmap;
    575770var
    576771  yb, xb: integer;
     
    710905end;
    711906
    712 function FilterNormalize(bmp: TBGRADefaultBitmap;
    713   eachChannel: boolean = True): TBGRADefaultBitmap;
     907{ Normalize compute min-max of specified channel and apply an affine transformation
     908  to make it use the full range of values }
     909function FilterNormalize(bmp: TBGRACustomBitmap;
     910  eachChannel: boolean = True): TBGRACustomBitmap;
    714911var
    715912  psrc, pdest: PBGRAPixel;
     
    8311028end;
    8321029
    833 function FilterRotate(bmp: TBGRADefaultBitmap; origin: TPointF;
    834   angle: single): TBGRADefaultBitmap;
     1030{ Rotates the image. To do this, loop through the destination and
     1031  calculates the position in the source bitmap with an affine transformation }
     1032function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
     1033  angle: single): TBGRACustomBitmap;
    8351034var
    8361035  bounds:     TRect;
     
    9261125end;
    9271126
    928 function FilterGrayscale(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     1127{ Filter grayscale applies BGRAToGrayscale function to all pixels }
     1128function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    9291129var
    9301130  bounds:      TRect;
     
    9521152end;
    9531153
    954 function FilterContour(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     1154{ Filter contour compute a grayscale image, then for each pixel
     1155  calculates the difference with surrounding pixels (in intensity and alpha)
     1156  and draw black pixels when there is a difference }
     1157function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    9551158var
    9561159  yb, xb: integer;
     
    9641167
    9651168  bounds: TRect;
    966   gray:   TBGRADefaultBitmap;
     1169  gray:   TBGRACustomBitmap;
    9671170begin
    9681171  bmpWidth  := bmp.Width;
     
    10661269end;
    10671270
    1068 function FilterSphere(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     1271{ Compute the distance for each pixel to the center of the bitmap,
     1272  calculate the corresponding angle with arcsin, use this angle
     1273  to determine a distance from the center in the source bitmap }
     1274function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    10691275var
    10701276  cx, cy, x, y, len, fact: single;
    10711277  xb, yb: integer;
    1072   mask:   TBGRADefaultBitmap;
     1278  mask:   TBGRACustomBitmap;
    10731279begin
    10741280  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     
    10991305end;
    11001306
    1101 function FilterCylinder(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     1307{ Applies twirl scanner. See TBGRATwirlScanner }
     1308function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     1309var twirl: TBGRATwirlScanner;
     1310begin
     1311  twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);
     1312  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     1313  result.Fill(twirl);
     1314  twirl.free;
     1315end;
     1316
     1317{ Compute the distance for each pixel to the vertical axis of the bitmap,
     1318  calculate the corresponding angle with arcsin, use this angle
     1319  to determine a distance from the vertical axis in the source bitmap }
     1320function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    11021321var
    11031322  cx, cy, x, y, len, fact: single;
     
    11251344end;
    11261345
    1127 function FilterPlane(bmp: TBGRADefaultBitmap): TBGRADefaultBitmap;
     1346function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    11281347const resampleGap=0.6;
    11291348var
    11301349  cy, x1, x2, y1, y2, z1, z2, h: single;
    11311350  yb: integer;
    1132   resampledBmp: TBGRADefaultBitmap;
     1351  resampledBmp: TBGRACustomBitmap;
    11331352  resampledBmpWidth: integer;
    11341353  resampledFactor,newResampleFactor: single;
    1135   sub,resampledSub: TBGRADefaultBitmap;
     1354  sub,resampledSub: TBGRACustomBitmap;
    11361355  partRect: TRect;
    11371356  resampleSizeY : integer;
     
    11911410end;
    11921411
    1193 function FilterMedian(bmp: TBGRADefaultBitmap;
    1194   Option: TMedianOption): TBGRADefaultBitmap;
     1412{ For each component, sort values to get the median }
     1413function FilterMedian(bmp: TBGRACustomBitmap;
     1414  Option: TMedianOption): TBGRACustomBitmap;
    11951415
    11961416  function ComparePixLt(p1, p2: TBGRAPixel): boolean;
     
    12361456        for dx := -1 to 1 do
    12371457        begin
    1238           a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
     1458          a_pixels[n] := bmp.GetPixel(integer(xb + dx), integer(yb + dy));
    12391459          if a_pixels[n].alpha = 0 then
    12401460            a_pixels[n] := BGRAPixelTransparent;
Note: See TracChangeset for help on using the changeset viewer.