Changeset 494


Ignore:
Timestamp:
Dec 22, 2016, 8:49:19 PM (7 years ago)
Author:
chronos
Message:
  • Modified: Updated BGRABitmap package.
Location:
GraphicTest
Files:
43 added
7 deleted
78 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/GraphicTest.lpi

    r472 r494  
    9191        <HasResources Value="True"/>
    9292        <ResourceBaseClass Value="Form"/>
    93         <UnitName Value="UMainForm"/>
    9493      </Unit1>
    9594      <Unit2>
    9695        <Filename Value="UPlatform.pas"/>
    9796        <IsPartOfProject Value="True"/>
    98         <UnitName Value="UPlatform"/>
    9997      </Unit2>
    10098      <Unit3>
    10199        <Filename Value="UDrawMethod.pas"/>
    102100        <IsPartOfProject Value="True"/>
    103         <UnitName Value="UDrawMethod"/>
    104101      </Unit3>
    105102      <Unit4>
    106103        <Filename Value="UFastBitmap.pas"/>
    107104        <IsPartOfProject Value="True"/>
    108         <UnitName Value="UFastBitmap"/>
    109105      </Unit4>
    110106      <Unit5>
     
    114110        <HasResources Value="True"/>
    115111        <ResourceBaseClass Value="Form"/>
    116         <UnitName Value="UDrawForm"/>
    117112      </Unit5>
    118113      <Unit6>
    119114        <Filename Value="Methods/UCanvasPixels.pas"/>
    120115        <IsPartOfProject Value="True"/>
    121         <UnitName Value="UCanvasPixels"/>
    122116      </Unit6>
    123117      <Unit7>
     
    128122        <Filename Value="Methods/ULazIntfImageColorsCopy.pas"/>
    129123        <IsPartOfProject Value="True"/>
    130         <UnitName Value="ULazIntfImageColorsCopy"/>
    131124      </Unit8>
    132125      <Unit9>
     
    137130        <Filename Value="Methods/UBGRABitmapPaintBox.pas"/>
    138131        <IsPartOfProject Value="True"/>
    139         <UnitName Value="UBGRABitmapPaintBox"/>
    140132      </Unit10>
    141133      <Unit11>
    142134        <Filename Value="Methods/UBitmapRawImageDataPaintBox.pas"/>
    143135        <IsPartOfProject Value="True"/>
    144         <UnitName Value="UBitmapRawImageDataPaintBox"/>
    145136      </Unit11>
    146137      <Unit12>
     
    159150        <Filename Value="Methods/UOpenGLMethod.pas"/>
    160151        <IsPartOfProject Value="True"/>
    161         <UnitName Value="UOpenGLMethod"/>
    162152      </Unit15>
    163153      <Unit16>
    164154        <Filename Value="Methods/UOpenGLPBOMethod.pas"/>
    165155        <IsPartOfProject Value="True"/>
    166         <UnitName Value="UOpenGLPBOMethod"/>
    167156      </Unit16>
    168157      <Unit17>
    169158        <Filename Value="Methods/UGraphics32Method.pas"/>
    170159        <IsPartOfProject Value="True"/>
    171         <UnitName Value="UGraphics32Method"/>
    172160      </Unit17>
    173161    </Units>
  • GraphicTest/GraphicTest.lpr

    r471 r494  
    88  {$ENDIF}{$ENDIF}
    99  Interfaces, // this includes the LCL widgetset
    10   Forms, lazopenglcontext, UMainForm, UPlatform, UDrawMethod, UFastBitmap,
     10  Forms, openglcontext, UMainForm, UPlatform, UDrawMethod, UFastBitmap,
    1111  UDrawForm, bgrabitmappack,
    1212  {$IFDEF GRAPHICS32}GR32_L,{$ENDIF}
  • GraphicTest/Packages/bgrabitmap/bgraanimatedgif.pas

    r472 r494  
    22
    33{$mode objfpc}{$H+}
     4{$i bgrabitmap.inc}
    45
    56interface
    67
    78uses
    8   Classes, SysUtils, Graphics, FPImage, BGRABitmap, BGRABitmapTypes;
     9  Classes, SysUtils, BGRAGraphics, FPImage, BGRABitmap, BGRABitmapTypes,
     10  BGRAPalette, BGRAGifFormat;
    911
    1012type
    11   TDisposeMode = (dmNone, dmKeep, dmErase, dmRestore);
    12 
    13   TGifSubImage = record
    14     Image:    TBGRABitmap;
    15     Position: TPoint;
    16     Delay:    integer;
    17     disposeMode: TDisposeMode;
    18     TransparentColor: TBGRAPixel;
    19   end;
    20   TGifSubImageArray = array of TGifSubImage;
    21 
     13  TDisposeMode = BGRAGifFormat.TDisposeMode;
     14  TGifSubImage = BGRAGifFormat.TGifSubImage;
     15  TGifSubImageArray = BGRAGifFormat.TGifSubImageArray;
     16
     17  //how to deal with the background under the GIF animation
    2218  TGifBackgroundMode = (gbmSimplePaint, gbmEraseBackground,
    2319    gbmSaveBackgroundOnce, gbmUpdateBackgroundContinuously);
     
    2723  TBGRAAnimatedGif = class(TGraphic)
    2824  private
     25    FAspectRatio: single;
    2926    FWidth, FHeight:  integer;
    3027    FBackgroundColor: TColor;
     
    3431    FTimeAccumulator: double;
    3532    FCurrentImage, FWantedImage: integer;
    36     FFullAnimationTime: double;
     33    FTotalAnimationTime: int64;
    3734    FPreviousDisposeMode: TDisposeMode;
    3835
     
    4138    FImageChanged: boolean;
    4239
     40    procedure CheckFrameIndex(AIndex: integer);
    4341    function GetCount: integer;
     42    function GetFrameDelayMs(AIndex: integer): integer;
     43    function GetFrameDisposeMode(AIndex: integer): TDisposeMode;
     44    function GetFrameHasLocalPalette(AIndex: integer): boolean;
     45    function GetFrameImage(AIndex: integer): TBGRABitmap;
     46    function GetFrameImagePos(AIndex: integer): TPoint;
    4447    function GetTimeUntilNextImage: integer;
    4548    procedure Render(StretchWidth, StretchHeight: integer);
     49    procedure SetAspectRatio(AValue: single);
     50    procedure SetBackgroundColor(AValue: TColor);
     51    procedure SetFrameDelayMs(AIndex: integer; AValue: integer);
     52    procedure SetFrameDisposeMode(AIndex: integer; AValue: TDisposeMode);
     53    procedure SetFrameHasLocalPalette(AIndex: integer; AValue: boolean);
     54    procedure SetFrameImage(AIndex: integer; AValue: TBGRABitmap);
     55    procedure SetFrameImagePos(AIndex: integer; AValue: TPoint);
    4656    procedure UpdateSimple(Canvas: TCanvas; ARect: TRect;
    4757      DrawOnlyIfChanged: boolean = True);
     
    5666  protected
    5767    FImages: TGifSubImageArray;
    58     procedure LoadImages(stream: TStream);
    5968
    6069    {TGraphic}
     
    6473    function GetTransparent: boolean; override;
    6574    function GetWidth: integer; override;
    66     procedure SetHeight(Value: integer); override;
    67     procedure SetTransparent(Value: boolean); override;
    68     procedure SetWidth(Value: integer); override;
     75    procedure SetHeight({%H-}Value: integer); override;
     76    procedure SetTransparent({%H-}Value: boolean); override;
     77    procedure SetWidth({%H-}Value: integer); override;
     78    procedure ClearViewer; virtual;
    6979
    7080  public
     
    7686    constructor Create; override;
    7787    function Duplicate: TBGRAAnimatedGif;
     88    function AddFrame(AImage: TFPCustomImage; X,Y: integer; ADelayMs: integer;
     89      ADisposeMode: TDisposeMode = dmErase; AHasLocalPalette: boolean = false) : integer;
     90    procedure InsertFrame(AIndex: integer; AImage: TBGRABitmap; X,Y: integer; ADelayMs: integer;
     91      ADisposeMode: TDisposeMode = dmErase; AHasLocalPalette: boolean = false);
    7892
    7993    {TGraphic}
     
    8498    class function GetFileExtensions: string; override;
    8599
     100    procedure SetSize(AWidth,AHeight: integer); virtual;
     101    procedure SaveToStream(Stream: TStream; AQuantizer: TBGRAColorQuantizerAny;
     102      ADitheringAlgorithm: TDitheringAlgorithm); virtual; overload;
    86103    procedure Clear; override;
    87104    destructor Destroy; override;
     
    93110    procedure Hide(Canvas: TCanvas; ARect: TRect); overload;
    94111
    95     property BackgroundColor: TColor Read FBackgroundColor;
     112    property BackgroundColor: TColor Read FBackgroundColor write SetBackgroundColor;
    96113    property Count: integer Read GetCount;
    97114    property Width: integer Read FWidth;
     
    102119    property CurrentImage: integer Read FCurrentImage Write SetCurrentImage;
    103120    property TimeUntilNextImageMs: integer read GetTimeUntilNextImage;
    104   end;
    105 
    106   { TFPReaderGIF }
    107 
    108   TFPReaderGIF = class(TFPCustomImageReader)
     121    property FrameImage[AIndex: integer]: TBGRABitmap read GetFrameImage write SetFrameImage;
     122    property FrameHasLocalPalette[AIndex: integer]: boolean read GetFrameHasLocalPalette write SetFrameHasLocalPalette;
     123    property FrameImagePos[AIndex: integer]: TPoint read GetFrameImagePos write SetFrameImagePos;
     124    property FrameDelayMs[AIndex: integer]: integer read GetFrameDelayMs write SetFrameDelayMs;
     125    property FrameDisposeMode[AIndex: integer]: TDisposeMode read GetFrameDisposeMode write SetFrameDisposeMode;
     126    property AspectRatio: single read FAspectRatio write SetAspectRatio;
     127    property TotalAnimationTimeMs: Int64 read FTotalAnimationTime;
     128  end;
     129
     130  { TBGRAReaderGIF }
     131
     132  TBGRAReaderGIF = class(TFPCustomImageReader)
    109133  protected
    110134    procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
    111135    function InternalCheck(Str: TStream): boolean; override;
     136  end;
     137
     138  { TBGRAWriterGIF }
     139
     140  TBGRAWriterGIF = class(TFPCustomImageWriter)
     141  protected
     142    procedure InternalWrite(Str: TStream; Img: TFPCustomImage); override;
    112143  end;
    113144
     
    119150implementation
    120151
    121 uses BGRABlend, lazutf8classes;
     152uses BGRABlend, BGRAUTF8{$IFDEF BGRABITMAP_USE_LCL}, Graphics{$ENDIF};
    122153
    123154const
     
    128159  {$ENDIF}
    129160
    130 type
    131   TGIFSignature = packed array[1..6] of char;
    132 
    133   TGIFScreenDescriptor = packed record
    134     Width, Height: word;
    135     flags, background, map: byte;
    136   end;
    137 
    138   TGIFImageDescriptor = packed record
    139     x, y, Width, Height: word;
    140     flags: byte;
    141   end;
    142 
    143   TGIFExtensionBlock = packed record
    144     functioncode: byte;
    145   end;
    146 
    147   TGIFGraphicControlExtension = packed record
    148     flags:      byte;
    149     delaytime:  word;
    150     transcolor: byte;
    151   end;
    152161
    153162{ TBGRAAnimatedGif }
     
    156165begin
    157166  Result := 'gif';
     167end;
     168
     169procedure TBGRAAnimatedGif.SetSize(AWidth, AHeight: integer);
     170begin
     171  ClearViewer;
     172  FWidth := AWidth;
     173  FHeight := AHeight;
     174end;
     175
     176procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream;
     177      AQuantizer: TBGRAColorQuantizerAny;
     178      ADitheringAlgorithm: TDitheringAlgorithm);
     179var data: TGIFData;
     180begin
     181  data.Height:= Height;
     182  data.Width := Width;
     183  data.AspectRatio := 1;
     184  data.BackgroundColor := BackgroundColor;
     185  data.Images := FImages;
     186  GIFSaveToStream(data, Stream, AQuantizer, ADitheringAlgorithm);
    158187end;
    159188
     
    167196  begin
    168197    FInternalVirtualScreen := TBGRABitmap.Create(FWidth, FHeight);
    169     if Count = 0 then
     198    if (Count = 0) and (BackgroundColor <> clNone) then
    170199      FInternalVirtualScreen.Fill(BackgroundColor)
    171200    else
     
    197226    if not FPaused then
    198227      FTimeAccumulator += (curDate - FPrevDate) * 24 * 60 * 60 * 1000;
    199     if FFullAnimationTime > 0 then FTimeAccumulator:= frac(FTimeAccumulator/FFullAnimationTime)*FFullAnimationTime;
     228    if FTotalAnimationTime > 0 then FTimeAccumulator:= frac(FTimeAccumulator/FTotalAnimationTime)*FTotalAnimationTime;
    200229    nextImage := FCurrentImage;
    201     while FTimeAccumulator > FImages[nextImage].Delay do
    202     begin
    203       FTimeAccumulator -= FImages[nextImage].Delay;
     230    while FTimeAccumulator > FImages[nextImage].DelayMs do
     231    begin
     232      FTimeAccumulator -= FImages[nextImage].DelayMs;
    204233      Inc(nextImage);
    205234      if nextImage >= Count then
     
    244273        FInternalVirtualScreen.PutImage(Position.X, Position.Y, Image,
    245274          dmSetExceptTransparent);
    246       FPreviousDisposeMode := disposeMode;
     275      FPreviousDisposeMode := DisposeMode;
    247276    end;
    248277
     
    262291end;
    263292
     293procedure TBGRAAnimatedGif.SetAspectRatio(AValue: single);
     294begin
     295  if AValue < 0.25 then AValue := 0.25;
     296  if AValue > 4 then AValue := 4;
     297  if FAspectRatio=AValue then Exit;
     298  FAspectRatio:=AValue;
     299end;
     300
     301procedure TBGRAAnimatedGif.SetBackgroundColor(AValue: TColor);
     302begin
     303  if FBackgroundColor=AValue then Exit;
     304  FBackgroundColor:=AValue;
     305end;
     306
     307procedure TBGRAAnimatedGif.SetFrameDelayMs(AIndex: integer; AValue: integer);
     308begin
     309  CheckFrameIndex(AIndex);
     310  if AValue < 0 then AValue := 0;
     311  FTotalAnimationTime := FTotalAnimationTime + AValue - FImages[AIndex].DelayMs;
     312  FImages[AIndex].DelayMs := AValue;
     313end;
     314
     315procedure TBGRAAnimatedGif.SetFrameDisposeMode(AIndex: integer;
     316  AValue: TDisposeMode);
     317begin
     318  CheckFrameIndex(AIndex);
     319  FImages[AIndex].DisposeMode := AValue;
     320end;
     321
     322procedure TBGRAAnimatedGif.SetFrameHasLocalPalette(AIndex: integer;
     323  AValue: boolean);
     324begin
     325  CheckFrameIndex(AIndex);
     326  FImages[AIndex].HasLocalPalette := AValue;
     327
     328end;
     329
     330procedure TBGRAAnimatedGif.SetFrameImage(AIndex: integer; AValue: TBGRABitmap);
     331var ACopy: TBGRABitmap;
     332begin
     333  CheckFrameIndex(AIndex);
     334  ACopy := AValue.Duplicate as TBGRABitmap;
     335  FImages[AIndex].Image.FreeReference;
     336  FImages[AIndex].Image := ACopy;
     337end;
     338
     339procedure TBGRAAnimatedGif.SetFrameImagePos(AIndex: integer; AValue: TPoint);
     340begin
     341  CheckFrameIndex(AIndex);
     342  FImages[AIndex].Position := AValue;
     343end;
     344
    264345procedure TBGRAAnimatedGif.UpdateSimple(Canvas: TCanvas; ARect: TRect;
    265346  DrawOnlyIfChanged: boolean = True);
     
    284365end;
    285366
     367procedure TBGRAAnimatedGif.CheckFrameIndex(AIndex: integer);
     368begin
     369  if (AIndex < 0) or (AIndex >= Count) then Raise ERangeError.Create('Index out of bounds');
     370end;
     371
    286372function TBGRAAnimatedGif.GetCount: integer;
    287373begin
    288374  Result := length(FImages);
     375end;
     376
     377function TBGRAAnimatedGif.GetFrameDelayMs(AIndex: integer): integer;
     378begin
     379  CheckFrameIndex(AIndex);
     380  result := FImages[AIndex].DelayMs;
     381end;
     382
     383function TBGRAAnimatedGif.GetFrameDisposeMode(AIndex: integer): TDisposeMode;
     384begin
     385  CheckFrameIndex(AIndex);
     386  result := FImages[AIndex].DisposeMode;
     387end;
     388
     389function TBGRAAnimatedGif.GetFrameHasLocalPalette(AIndex: integer): boolean;
     390begin
     391  CheckFrameIndex(AIndex);
     392  result := FImages[AIndex].HasLocalPalette;
     393end;
     394
     395function TBGRAAnimatedGif.GetFrameImage(AIndex: integer): TBGRABitmap;
     396begin
     397  CheckFrameIndex(AIndex);
     398  result := FImages[AIndex].Image;
     399end;
     400
     401function TBGRAAnimatedGif.GetFrameImagePos(AIndex: integer): TPoint;
     402begin
     403  CheckFrameIndex(AIndex);
     404  result := FImages[AIndex].Position;
    289405end;
    290406
     
    300416    acc := FTimeAccumulator;
    301417    if not FPaused then acc += (Now- FPrevDate) * 24 * 60 * 60 * 1000;
    302     if acc >= FImages[FCurrentImage].Delay then
     418    if acc >= FImages[FCurrentImage].DelayMs then
    303419      result := 0
    304420    else
    305       result := round(FImages[FCurrentImage].Delay-FTimeAccumulator);
     421      result := round(FImages[FCurrentImage].DelayMs-FTimeAccumulator);
    306422  end;
    307423end;
    308424
    309425constructor TBGRAAnimatedGif.Create(filenameUTF8: string);
    310 var
    311   Stream: TFileStreamUTF8;
    312426begin
    313427  inherited Create;
    314428  Init;
    315   Stream := TFileStreamUTF8.Create(filenameUTF8, fmOpenRead or fmShareDenyWrite);
    316   LoadFromStream(Stream);
    317   Stream.Free;
     429  LoadFromFile(filenameUTF8);
    318430end;
    319431
     
    348460end;
    349461
     462function TBGRAAnimatedGif.AddFrame(AImage: TFPCustomImage; X, Y: integer;
     463  ADelayMs: integer; ADisposeMode: TDisposeMode; AHasLocalPalette: boolean
     464  ): integer;
     465begin
     466  result := length(FImages);
     467  setlength(FImages, length(FImages)+1);
     468  if ADelayMs < 0 then ADelayMs:= 0;
     469  with FImages[result] do
     470  begin
     471    Image := TBGRABitmap.Create(AImage);
     472    Position := Point(x,y);
     473    DelayMs := ADelayMs;
     474    HasLocalPalette := AHasLocalPalette;
     475    DisposeMode := ADisposeMode;
     476  end;
     477  inc(FTotalAnimationTime, ADelayMs);
     478end;
     479
     480procedure TBGRAAnimatedGif.InsertFrame(AIndex: integer; AImage: TBGRABitmap; X,
     481  Y: integer; ADelayMs: integer; ADisposeMode: TDisposeMode;
     482  AHasLocalPalette: boolean);
     483var i: integer;
     484begin
     485  if (AIndex < 0) or (AIndex > Count) then
     486    raise ERangeError.Create('Index out of bounds');
     487  setlength(FImages, length(FImages)+1);
     488  if ADelayMs < 0 then ADelayMs:= 0;
     489  for i := high(FImages) downto AIndex+1 do
     490    FImages[i] := FImages[i-1];
     491  with FImages[AIndex] do
     492  begin
     493    Image := AImage.Duplicate as TBGRABitmap;
     494    Position := Point(x,y);
     495    DelayMs := ADelayMs;
     496    HasLocalPalette := AHasLocalPalette;
     497    DisposeMode := ADisposeMode;
     498  end;
     499  inc(FTotalAnimationTime, ADelayMs);
     500end;
     501
    350502procedure TBGRAAnimatedGif.LoadFromStream(Stream: TStream);
     503var data: TGIFData;
     504  i: integer;
     505begin
     506  data := GIFLoadFromStream(Stream);
     507
     508  ClearViewer;
     509  Clear;
     510  FWidth  := data.Width;
     511  FHeight := data.Height;
     512  FBackgroundColor := data.BackgroundColor;
     513  FAspectRatio:= data.AspectRatio;
     514
     515  SetLength(FImages, length(data.Images));
     516  FTotalAnimationTime:= 0;
     517  for i := 0 to high(FImages) do
     518  begin
     519    FImages[i] := data.Images[i];
     520    FTotalAnimationTime += FImages[i].DelayMs;
     521  end;
     522end;
     523
     524procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream);
     525begin
     526  SaveToStream(Stream, BGRAColorQuantizerFactory, daFloydSteinberg);
     527end;
     528
     529procedure TBGRAAnimatedGif.LoadFromFile(const AFilenameUTF8: string);
     530var stream: TFileStreamUTF8;
     531begin
     532  stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
     533  try
     534    LoadFromStream(stream);
     535  finally
     536    Stream.Free;
     537  end;
     538end;
     539
     540procedure TBGRAAnimatedGif.SaveToFile(const AFilenameUTF8: string);
     541var
     542  Stream: TFileStreamUTF8;
     543begin
     544  Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);
     545  try
     546    SaveToStream(Stream);
     547  finally
     548    Stream.Free;
     549  end;
     550end;
     551
     552procedure TBGRAAnimatedGif.Draw(ACanvas: TCanvas; const Rect: TRect);
     553begin
     554  if FBackgroundImage <> nil then
     555    FreeAndNil(FBackgroundImage);
     556  SaveBackgroundOnce(ACanvas, Rect);
     557
     558  if FPreviousVirtualScreen <> nil then
     559  begin
     560    FPreviousVirtualScreen.FreeReference;
     561    FPreviousVirtualScreen := nil;
     562  end;
     563
     564  Render(Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
     565  FStretchedVirtualScreen.Draw(ACanvas, Rect.Left, Rect.Top, false);
     566  FImageChanged := False;
     567
     568  FPreviousVirtualScreen := TBGRABitmap(FStretchedVirtualScreen.Duplicate);
     569end;
     570
     571function TBGRAAnimatedGif.GetEmpty: boolean;
     572begin
     573  Result := (length(FImages) = 0);
     574end;
     575
     576function TBGRAAnimatedGif.GetHeight: integer;
     577begin
     578  Result := FHeight;
     579end;
     580
     581function TBGRAAnimatedGif.GetTransparent: boolean;
     582begin
     583  Result := True;
     584end;
     585
     586function TBGRAAnimatedGif.GetWidth: integer;
     587begin
     588  Result := FWidth;
     589end;
     590
     591procedure TBGRAAnimatedGif.SetHeight(Value: integer);
     592begin
     593  //not implemented
     594end;
     595
     596procedure TBGRAAnimatedGif.SetTransparent(Value: boolean);
     597begin
     598  //not implemented
     599end;
     600
     601procedure TBGRAAnimatedGif.SetWidth(Value: integer);
     602begin
     603  //not implemented
     604end;
     605
     606procedure TBGRAAnimatedGif.ClearViewer;
    351607begin
    352608  FCurrentImage    := -1;
     
    368624  FPreviousVirtualScreen := nil;
    369625
    370   EraseColor := clBlack;
    371626  FPreviousDisposeMode := dmNone;
    372 
    373   FWidth  := 0;
    374   FHeight := 0;
    375 
    376   if Stream <> nil then
    377     LoadImages(Stream);
    378 end;
    379 
    380 procedure TBGRAAnimatedGif.SaveToStream(Stream: TStream);
    381 begin
    382   //not implemented
    383 end;
    384 
    385 procedure TBGRAAnimatedGif.LoadFromFile(const AFilenameUTF8: string);
    386 var stream: TFileStreamUTF8;
    387 begin
    388   stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
    389   try
    390     LoadFromStream(Stream);
    391   finally
    392     Stream.Free;
    393   end;
    394 end;
    395 
    396 procedure TBGRAAnimatedGif.SaveToFile(const AFilenameUTF8: string);
    397 var
    398   Stream: TFileStreamUTF8;
    399 begin
    400   Stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);
    401   try
    402     SaveToStream(Stream);
    403   finally
    404     Stream.Free;
    405   end;
    406 end;
    407 
    408 {$HINTS OFF}
    409 procedure TBGRAAnimatedGif.LoadImages(stream: TStream);
    410 
    411   procedure DumpData;
    412   var
    413     Count: byte;
    414   begin
    415     repeat
    416       stream.Read(Count, 1);
    417       stream.position := stream.position + Count;
    418     until (Count = 0) or (stream.position >= stream.size);
    419   end;
    420 
    421 type
    422   TRGB = packed record
    423     r, g, b: byte;
    424   end;
    425 
    426   TPalette = array of TBGRAPixel;
    427 
    428   function rgbToColor(rgb: TRGB): TBGRAPixel;
    429   begin
    430     Result.red   := rgb.r;
    431     Result.green := rgb.g;
    432     Result.blue  := rgb.b;
    433     Result.alpha := 255;
    434   end;
    435 
    436 const
    437   GIFScreenDescriptor_GlobalColorTableFlag = $80;
    438   GIFImageDescriptor_LocalColorTableFlag = $80;
    439   GIFImageDescriptor_InterlacedFlag = $40;
    440   GIFGraphicControlExtension_TransparentFlag = $01;
    441 
    442 const
    443   ilstart: array[1..4] of longint = (0, 4, 2, 1);
    444   ilstep: array[1..4] of longint = (8, 8, 4, 2);
    445 
    446 var
    447   NewImages: array of TGifSubImage;
    448   NbImages:  integer;
    449 
    450   GIFSignature: TGIFSignature;
    451   GIFScreenDescriptor: TGIFScreenDescriptor;
    452   GIFBlockID:   char;
    453   GIFImageDescriptor: TGIFImageDescriptor;
    454 
    455   globalPalette: TPalette;
    456   localPalette:  TPalette;
    457 
    458   transcolorIndex: integer;
    459   delay: integer;
    460   disposeMode: TDisposeMode;
    461 
    462   procedure LoadGlobalPalette;
    463   var
    464     NbEntries, i: integer;
    465     rgb: TRGB;
    466   begin
    467     NbEntries := 1 shl (GIFScreenDescriptor.flags and $07 + 1);
    468     setlength(globalPalette, NbEntries);
    469     for i := 0 to NbEntries - 1 do
    470     begin
    471       stream.Read(rgb, 3);
    472       globalPalette[i] := rgbToColor(rgb);
    473     end;
    474   end;
    475 
    476   procedure LoadLocalPalette;
    477   var
    478     NbEntries, i: integer;
    479     rgb: TRGB;
    480   begin
    481     NbEntries := 1 shl (GIFImageDescriptor.flags and $07 + 1);
    482     setlength(localPalette, NbEntries);
    483     for i := 0 to NbEntries - 1 do
    484     begin
    485       stream.Read(rgb, 3);
    486       localPalette[i] := rgbToColor(rgb);
    487     end;
    488   end;
    489 
    490   procedure decodeGIFLZW(image: TBGRABitmap; const pal: TPalette; interlaced: boolean);
    491   var
    492     xd, yd: longint;
    493   const
    494     tablen = 4095;
    495   type
    496     Pstr = ^Tstr;
    497 
    498     Tstr = record
    499       prefix: Pstr;
    500       suffix: longint;
    501     end;
    502     Pstrtab = ^Tstrtab;
    503     Tstrtab = array[0..tablen] of Tstr;
    504 
    505   var
    506     strtab:   Pstrtab;
    507     oldcode, curcode, clearcode, endcode: longint;
    508     codesize, codelen, codemask: longint;
    509     stridx:   longint;
    510     bitbuf, bitsinbuf: longint;
    511     bytbuf:   packed array[0..255] of byte;
    512     bytinbuf, bytbufidx: byte;
    513     endofsrc: boolean;
    514     xcnt, ycnt, ystep, pass: longint;
    515 
    516     procedure InitStringTable;
    517     var
    518       i: longint;
    519     begin
    520       new(strtab);
    521       clearcode := 1 shl codesize;
    522       endcode   := clearcode + 1;
    523       stridx    := endcode + 1;
    524       codelen   := codesize + 1;
    525       codemask  := (1 shl codelen) - 1;
    526       for i := 0 to clearcode - 1 do
    527       begin
    528         strtab^[i].prefix := nil;
    529         strtab^[i].suffix := i;
    530       end;
    531       for i := clearcode to tablen do
    532       begin
    533         strtab^[i].prefix := nil;
    534         strtab^[i].suffix := 0;
    535       end;
    536     end;
    537 
    538     procedure ClearStringTable;
    539     var
    540       i: longint;
    541     begin
    542       clearcode := 1 shl codesize;
    543       endcode   := clearcode + 1;
    544       stridx    := endcode + 1;
    545       codelen   := codesize + 1;
    546       codemask  := (1 shl codelen) - 1;
    547       for i := clearcode to tablen do
    548       begin
    549         strtab^[i].prefix := nil;
    550         strtab^[i].suffix := 0;
    551       end;
    552     end;
    553 
    554     procedure DoneStringTable;
    555     begin
    556       dispose(strtab);
    557     end;
    558 
    559     function GetNextCode: longint;
    560     begin
    561       while (bitsinbuf < codelen) do
    562       begin
    563         if (bytinbuf = 0) then
    564         begin
    565           stream.Read(bytinbuf, 1);
    566           if (bytinbuf = 0) then
    567             endofsrc := True;
    568           stream.Read(bytbuf, bytinbuf);
    569           bytbufidx := 0;
    570         end;
    571         bitbuf := bitbuf or (longint(byte(bytbuf[bytbufidx])) shl bitsinbuf);
    572         Inc(bytbufidx);
    573         Dec(bytinbuf);
    574         Inc(bitsinbuf, 8);
    575       end;
    576       Result := bitbuf and codemask;
    577       {DBG(bitbuf AND codemask);}
    578       bitbuf := bitbuf shr codelen;
    579       Dec(bitsinbuf, codelen);
    580     end;
    581 
    582     procedure AddStr2Tab(prefix: Pstr; suffix: longint);
    583     begin
    584       strtab^[stridx].prefix := prefix;
    585       strtab^[stridx].suffix := suffix;
    586       Inc(stridx);
    587       case stridx of
    588         0..1: codelen      := 1;
    589         2..3: codelen      := 2;
    590         4..7: codelen      := 3;
    591         8..15: codelen     := 4;
    592         16..31: codelen    := 5;
    593         32..63: codelen    := 6;
    594         64..127: codelen   := 7;
    595         128..255: codelen  := 8;
    596         256..511: codelen  := 9;
    597         512..1023: codelen := 10;
    598         1024..2047: codelen := 11;
    599         2048..4096: codelen := 12;
    600       end;
    601       codemask := (1 shl codelen) - 1;
    602     end;
    603 
    604     function Code2Str(code: longint): Pstr;
    605     begin
    606       Result := addr(strtab^[code]);
    607     end;
    608 
    609     procedure WriteStr(s: Pstr);
    610     var
    611       colorIndex: integer;
    612     begin
    613       if (s^.prefix <> nil) then
    614         WriteStr(s^.prefix);
    615       if (ycnt >= yd) then
    616       begin
    617         if interlaced then
    618         begin
    619           while (ycnt >= yd) and (pass < 5) do
    620           begin
    621             Inc(pass);
    622             ycnt  := ilstart[pass];
    623             ystep := ilstep[pass];
    624           end;
    625         end;
    626       end;
    627 
    628       colorIndex := s^.suffix;
    629       if (colorIndex <> transcolorIndex) and (colorIndex >= 0) and
    630         (colorIndex < length(pal)) then
    631         image.setpixel(xcnt, ycnt, pal[colorIndex]);
    632 
    633       Inc(xcnt);
    634       if (xcnt >= xd) then
    635       begin
    636         xcnt := 0;
    637         Inc(ycnt, ystep);
    638 
    639         if not interlaced then
    640           if (ycnt >= yd) then
    641           begin
    642             Inc(pass);
    643           end;
    644 
    645       end;
    646     end;
    647 
    648     function firstchar(s: Pstr): byte;
    649     begin
    650       while (s^.prefix <> nil) do
    651         s    := s^.prefix;
    652       Result := s^.suffix;
    653     end;
    654 
    655   begin
    656     {DBG('lzw start');}
    657     endofsrc := False;
    658     xd   := image.Width;
    659     yd   := image.Height;
    660     xcnt := 0;
    661     if interlaced then
    662     begin
    663       pass  := 1;
    664       ycnt  := ilstart[pass];
    665       ystep := ilstep[pass];
    666     end
    667     else
    668     begin
    669       pass  := 4;
    670       ycnt  := 0;
    671       ystep := 1;
    672     end;
    673     oldcode   := 0;
    674     bitbuf    := 0;
    675     bitsinbuf := 0;
    676     bytinbuf  := 0;
    677     bytbufidx := 0;
    678     codesize  := 0;
    679     stream.Read(codesize, 1);
    680     {DBG(codesize);}
    681     InitStringTable;
    682     curcode := getnextcode;
    683     {DBG(curcode);}
    684     while (curcode <> endcode) and (pass < 5) and not endofsrc{ AND NOT finished} do
    685     begin
    686 {DBG('-----');
    687 DBG(curcode);
    688 DBGw(stridx);}
    689       if (curcode = clearcode) then
    690       begin
    691         ClearStringTable;
    692         repeat
    693           curcode := getnextcode;
    694           {DBG('lzw clear');}
    695         until (curcode <> clearcode);
    696         if (curcode = endcode) then
    697           break;
    698         WriteStr(code2str(curcode));
    699         oldcode := curcode;
    700       end
    701       else
    702       begin
    703         if (curcode < stridx) then
    704         begin
    705           WriteStr(Code2Str(curcode));
    706           AddStr2Tab(Code2Str(oldcode), firstchar(Code2Str(curcode)));
    707           oldcode := curcode;
    708         end
    709         else
    710         begin
    711           if (curcode > stridx) then
    712             break;
    713           AddStr2Tab(Code2Str(oldcode), firstchar(Code2Str(oldcode)));
    714           WriteStr(Code2Str(stridx - 1));
    715           oldcode := curcode;
    716         end;
    717       end;
    718       curcode := getnextcode;
    719     end;
    720     DoneStringTable;
    721     {putimage(0,0,image);}
    722 {DBG('lzw end');
    723 DBG(bytinbuf);}
    724     if not endofsrc then
    725       DumpData;
    726     {DBG('lzw finished');}
    727   end;
    728 
    729   procedure LoadImage;
    730   var
    731     imgWidth, imgHeight: integer;
    732     img:     TBGRABitmap;
    733     Interlaced: boolean;
    734     palette: TPalette;
    735   begin
    736     stream.Read(GIFImageDescriptor, sizeof(GIFImageDescriptor));
    737     GIFImageDescriptor.Width := LEtoN(GIFImageDescriptor.Width);
    738     GIFImageDescriptor.Height := LEtoN(GIFImageDescriptor.Height);
    739     GIFImageDescriptor.x := LEtoN(GIFImageDescriptor.x);
    740     GIFImageDescriptor.y := LEtoN(GIFImageDescriptor.y);
    741     if (GIFImageDescriptor.flags and GIFImageDescriptor_LocalColorTableFlag =
    742       GIFImageDescriptor_LocalColorTableFlag) then
    743       LoadLocalPalette
    744     else
    745       localPalette := nil;
    746 
    747     if localPalette <> nil then
    748       palette := localPalette
    749     else
    750       palette := globalPalette;
    751     imgWidth := GIFImageDescriptor.Width;
    752     imgHeight := GIFImageDescriptor.Height;
    753 
    754     if length(NewImages) <= NbImages then
    755       setlength(NewImages, length(NewImages) * 2 + 1);
    756     img := TBGRABitmap.Create(imgWidth, imgHeight);
    757     img.Fill(BGRAPixelTransparent);
    758     NewImages[NbImages].Image    := img;
    759     NewImages[NbImages].Position := point(GIFImageDescriptor.x, GIFImageDescriptor.y);
    760     NewImages[NbImages].Delay    := Delay;
    761     NewImages[NbImages].disposeMode := disposeMode;
    762 
    763     if (transcolorIndex >= 0) and (transcolorIndex < length(palette)) then
    764       NewImages[nbImages].TransparentColor := palette[transcolorIndex]
    765     else
    766       NewImages[nbImages].TransparentColor := BGRAPixelTransparent;
    767 
    768     Inc(NbImages);
    769 
    770     Interlaced := GIFImageDescriptor.flags and GIFImageDescriptor_InterlacedFlag =
    771       GIFImageDescriptor_InterlacedFlag;
    772     DecodeGIFLZW(img, palette, Interlaced);
    773   end;
    774 
    775   procedure ChangeImages;
    776   var
    777     i: integer;
    778   begin
    779     Clear;
    780     SetLength(FImages, NbImages);
    781     FFullAnimationTime:= 0;
    782     for i := 0 to Count - 1 do
    783     begin
    784       FImages[i] := NewImages[i];
    785       FFullAnimationTime += NewImages[i].Delay;
    786     end;
    787   end;
    788 
    789   procedure ReadExtension;
    790   var
    791     GIFExtensionBlock: TGIFExtensionBlock;
    792     GIFGraphicControlExtension: TGIFGraphicControlExtension;
    793     mincount, Count:   byte;
    794 
    795   begin
    796     stream.Read(GIFExtensionBlock, sizeof(GIFExtensionBlock));
    797     case GIFExtensionBlock.functioncode of
    798       $F9:
    799       begin
    800         stream.Read(Count, 1);
    801         if Count < sizeof(GIFGraphicControlExtension) then
    802           mincount := 0
    803         else
    804         begin
    805           mincount := sizeof(GIFGraphicControlExtension);
    806           stream.Read(GIFGraphicControlExtension, mincount);
    807           GIFGraphicControlExtension.delaytime := LEtoN(GIFGraphicControlExtension.delaytime);
    808 
    809           if GIFGraphicControlExtension.flags and
    810             GIFGraphicControlExtension_TransparentFlag =
    811             GIFGraphicControlExtension_TransparentFlag then
    812             transcolorIndex := GIFGraphicControlExtension.transcolor
    813           else
    814             transcolorIndex := -1;
    815           if GIFGraphicControlExtension.delaytime <> 0 then
    816             Delay     := GIFGraphicControlExtension.delaytime * 10;
    817           disposeMode := TDisposeMode((GIFGraphicControlExtension.flags shr 2) and 7);
    818         end;
    819         stream.Position := Stream.Position + Count - mincount;
    820         DumpData;
    821       end;
    822       else
    823       begin
    824         DumpData;
    825       end;
    826     end;
    827   end;
    828 
    829 begin
    830   NewImages := nil;
    831   NbImages  := 0;
    832   transcolorIndex := -1;
    833   Delay     := 100;
    834   FBackgroundColor := clBlack;
    835   FWidth    := 0;
    836   FHeight   := 0;
    837   disposeMode := dmErase;
    838 
    839   stream.Read(GIFSignature, sizeof(GIFSignature));
    840   if (GIFSignature[1] = 'G') and (GIFSignature[2] = 'I') and (GIFSignature[3] = 'F') then
    841   begin
    842     stream.Read(GIFScreenDescriptor, sizeof(GIFScreenDescriptor));
    843     GIFScreenDescriptor.Width := LEtoN(GIFScreenDescriptor.Width);
    844     GIFScreenDescriptor.Height := LEtoN(GIFScreenDescriptor.Height);
    845     FWidth  := GIFScreenDescriptor.Width;
    846     FHeight := GIFScreenDescriptor.Height;
    847     if (GIFScreenDescriptor.flags and GIFScreenDescriptor_GlobalColorTableFlag =
    848       GIFScreenDescriptor_GlobalColorTableFlag) then
    849     begin
    850       LoadGlobalPalette;
    851       if GIFScreenDescriptor.background < length(globalPalette) then
    852         FBackgroundColor :=
    853           BGRAToColor(globalPalette[GIFScreenDescriptor.background]);
    854     end;
    855     repeat
    856       stream.Read(GIFBlockID, sizeof(GIFBlockID));
    857       case GIFBlockID of
    858         ';': ;
    859         ',': LoadImage;
    860         '!': ReadExtension;
    861         else
    862         begin
    863           raise Exception.Create('TBGRAAnimatedGif: unexpected block type');
    864           break;
    865         end;
    866       end;
    867     until (GIFBlockID = ';') or (stream.Position >= stream.size);
    868   end
    869   else
    870     raise Exception.Create('TBGRAAnimatedGif: invalid header');
    871   ChangeImages;
    872 end;
    873 
    874 procedure TBGRAAnimatedGif.Draw(ACanvas: TCanvas; const Rect: TRect);
    875 begin
    876   if FBackgroundImage <> nil then
    877     FreeAndNil(FBackgroundImage);
    878   SaveBackgroundOnce(ACanvas, Rect);
    879 
    880   if FPreviousVirtualScreen <> nil then
    881   begin
    882     FPreviousVirtualScreen.FreeReference;
    883     FPreviousVirtualScreen := nil;
    884   end;
    885 
    886   Render(Rect.Right - Rect.Left, Rect.Bottom - Rect.Top);
    887   FStretchedVirtualScreen.Draw(ACanvas, Rect.Left, Rect.Top, false);
    888   FImageChanged := False;
    889 
    890   FPreviousVirtualScreen := TBGRABitmap(FStretchedVirtualScreen.Duplicate);
    891 end;
    892 
    893 function TBGRAAnimatedGif.GetEmpty: boolean;
    894 begin
    895   Result := (length(FImages) = 0);
    896 end;
    897 
    898 function TBGRAAnimatedGif.GetHeight: integer;
    899 begin
    900   Result := FHeight;
    901 end;
    902 
    903 function TBGRAAnimatedGif.GetTransparent: boolean;
    904 begin
    905   Result := True;
    906 end;
    907 
    908 function TBGRAAnimatedGif.GetWidth: integer;
    909 begin
    910   Result := FWidth;
    911 end;
    912 
    913 procedure TBGRAAnimatedGif.SetHeight(Value: integer);
    914 begin
    915   //not implemented
    916 end;
    917 
    918 procedure TBGRAAnimatedGif.SetTransparent(Value: boolean);
    919 begin
    920   //not implemented
    921 end;
    922 
    923 procedure TBGRAAnimatedGif.SetWidth(Value: integer);
    924 begin
    925   //not implemented
    926627end;
    927628
     
    947648    FWantedImage := Index;
    948649end;
    949 
    950 {$HINTS ON}
    951650
    952651procedure TBGRAAnimatedGif.Clear;
     
    1045744          end;
    1046745
    1047           PChangePix  := PLongWord(FPreviousVirtualScreen.ScanLine[0]);
    1048           PNewPix     := PLongWord(FStretchedVirtualScreen.ScanLine[0]);
    1049           PBackground := PLongWord(FBackgroundImage.ScanLine[0]);
    1050           PNewBackground := PLongWord(NewBackgroundImage.ScanLine[0]);
     746          PChangePix  := PLongWord(FPreviousVirtualScreen.Data);
     747          PNewPix     := PLongWord(FStretchedVirtualScreen.Data);
     748          PBackground := PLongWord(FBackgroundImage.Data);
     749          PNewBackground := PLongWord(NewBackgroundImage.Data);
    1051750          for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do
    1052751          begin
     
    1095794          else
    1096795          begin
    1097             PChangePix  := PLongWord(FPreviousVirtualScreen.ScanLine[0]);
    1098             PNewPix     := PLongWord(FStretchedVirtualScreen.ScanLine[0]);
    1099             PBackground := PLongWord(FBackgroundImage.ScanLine[0]);
     796            PChangePix  := PLongWord(FPreviousVirtualScreen.Data);
     797            PNewPix     := PLongWord(FStretchedVirtualScreen.Data);
     798            PBackground := PLongWord(FBackgroundImage.Data);
    1100799            for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do
    1101800            begin
     
    1154853      begin
    1155854        shape := TBGRABitmap(FPreviousVirtualScreen.Duplicate);
    1156         p     := shape.ScanLine[0];
     855        p     := shape.Data;
    1157856        for n := shape.NbPixels - 1 downto 0 do
    1158857        begin
     
    1172871      begin
    1173872        shape := TBGRABitmap(FPreviousVirtualScreen.Duplicate);
    1174         p     := shape.ScanLine[0];
    1175         pback := FBackgroundImage.ScanLine[0];
     873        p     := shape.Data;
     874        pback := FBackgroundImage.Data;
    1176875        for n := shape.NbPixels - 1 downto 0 do
    1177876        begin
     
    1227926    else
    1228927    begin
    1229       PChangePix := PLongWord(FPreviousVirtualScreen.ScanLine[0]);
    1230       PNewPix    := PLongWord(FStretchedVirtualScreen.ScanLine[0]);
     928      PChangePix := PLongWord(FPreviousVirtualScreen.Data);
     929      PNewPix    := PLongWord(FStretchedVirtualScreen.Data);
    1231930      for n := FStretchedVirtualScreen.NbPixels - 1 downto 0 do
    1232931      begin
     
    1274973end;
    1275974
    1276 { TFPReaderGIF }
    1277 
    1278 procedure TFPReaderGIF.InternalRead(Str: TStream; Img: TFPCustomImage);
     975{ TBGRAReaderGIF }
     976
     977procedure TBGRAReaderGIF.InternalRead(Str: TStream; Img: TFPCustomImage);
    1279978var
    1280979  gif:  TBGRAAnimatedGif;
     
    1300999end;
    13011000
    1302 {$HINTS OFF}
    1303 function TFPReaderGIF.InternalCheck(Str: TStream): boolean;
     1001function TBGRAReaderGIF.InternalCheck(Str: TStream): boolean;
    13041002var
    13051003  GIFSignature: TGIFSignature;
     
    13081006  savepos := str.Position;
    13091007  try
     1008    fillchar({%H-}GIFSignature, sizeof(GIFSignature), 0);
    13101009    str.Read(GIFSignature, sizeof(GIFSignature));
    13111010    if (GIFSignature[1] = 'G') and (GIFSignature[2] = 'I') and
     
    13231022end;
    13241023
    1325 {$HINTS ON}
     1024{ TBGRAWriterGIF }
     1025
     1026procedure TBGRAWriterGIF.InternalWrite(Str: TStream; Img: TFPCustomImage);
     1027var
     1028  gif: TBGRAAnimatedGif;
     1029begin
     1030  gif := TBGRAAnimatedGif.Create;
     1031  try
     1032    gif.SetSize(Img.Width,Img.Height);
     1033    gif.AddFrame(Img, 0,0,0);
     1034    gif.SaveToStream(Str, BGRAColorQuantizerFactory, daFloydSteinberg);
     1035  except
     1036    on ex: EColorQuantizerMissing do
     1037    begin
     1038      FreeAndNil(gif);
     1039      raise EColorQuantizerMissing.Create('Please define the color quantizer factory. You can do that with the following statements: Uses BGRAPalette, BGRAColorQuantization; BGRAColorQuantizerFactory:= TBGRAColorQuantizer;');
     1040    end;
     1041    on ex: Exception do
     1042    begin
     1043      FreeAndNil(gif);
     1044      raise ex;
     1045    end;
     1046  end;
     1047  FreeAndNil(gif);
     1048end;
    13261049
    13271050initialization
     1051
     1052  DefaultBGRAImageReader[ifGif] := TBGRAReaderGIF;
     1053  DefaultBGRAImageWriter[ifGif] := TBGRAWriterGIF;
    13281054
    13291055  //Free Pascal Image
    13301056  ImageHandlers.RegisterImageReader('Animated GIF', TBGRAAnimatedGif.GetFileExtensions,
    1331     TFPReaderGIF);
    1332 
     1057    TBGRAReaderGIF);
     1058  ImageHandlers.RegisterImageWriter('Animated GIF', TBGRAAnimatedGif.GetFileExtensions,
     1059    TBGRAWriterGIF);
     1060
     1061  {$IFDEF BGRABITMAP_USE_LCL}
    13331062  //Lazarus Picture
    13341063  TPicture.RegisterFileFormat(TBGRAAnimatedGif.GetFileExtensions, 'Animated GIF',
    13351064    TBGRAAnimatedGif);
    1336 
     1065  {$ENDIF}
    13371066end.
    13381067
  • GraphicTest/Packages/bgrabitmap/bgraarrow.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, BGRABitmapTypes, Graphics;
     8  Classes, SysUtils, BGRABitmapTypes, BGRAGraphics;
    99
    1010type
    11 
    1211  { TBGRAArrow }
    1312
    14   TBGRAArrow = class
     13  TBGRAArrow = class(TBGRACustomArrow)
    1514  private
    1615    FLineCap: TPenEndCap;
     
    3837        ATipStyle: TPenJoinStyle; ALineCap: TPenEndCap; const AWidth: single; AOffsetX: single;
    3938        ARepeatCount: integer; ARelativePenWidth: single; ATriangleBackOffset: single): ArrayOfTPointF;
    40     function GetIsEndDefined: boolean;
    41     function GetIsStartDefined: boolean;
    42     procedure SetEndOffsetX(AValue: single);
    43     procedure SetEndRepeatCount(AValue: integer);
    44     procedure SetEndSizeFactor(AValue: TPointF);
    45     procedure SetLineCap(AValue: TPenEndCap);
    46     procedure SetStartOffsetX(AValue: single);
    47     procedure SetStartRepeatCount(AValue: integer);
    48     procedure SetStartSizeFactor(AValue: TPointF);
    4939    procedure SetWidth(AValue: single);
     40  protected
     41    function GetEndRepeatCount: integer; override;
     42    function GetEndSizeFactor: TPointF; override;
     43    function GetIsEndDefined: boolean; override;
     44    function GetIsStartDefined: boolean; override;
     45    function GetEndOffsetX: single; override;
     46    function GetStartOffsetX: single; override;
     47    function GetStartRepeatCount: integer; override;
     48    function GetStartSizeFactor: TPointF; override;
     49    procedure SetEndOffsetX(AValue: single); override;
     50    procedure SetEndRepeatCount(AValue: integer); override;
     51    procedure SetEndSizeFactor(AValue: TPointF); override;
     52    procedure SetStartOffsetX(AValue: single); override;
     53    procedure SetStartRepeatCount(AValue: integer); override;
     54    procedure SetStartSizeFactor(AValue: TPointF); override;
     55    function GetLineCap: TPenEndCap; override;
     56    procedure SetLineCap(AValue: TPenEndCap); override;
     57    procedure SetStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0);
     58    procedure SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0);
    5059  public
    5160    constructor Create;
    52     procedure SetStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle;
    53         ARelativePenWidth: single; ATriangleBackOffset: single);
    54     procedure SetEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle;
    55         ARelativePenWidth: single; ATriangleBackOffset: single);
    56     function ComputeStartAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF;
    57     function ComputeEndAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF;
    58     property IsStartDefined: boolean read GetIsStartDefined;
    59     property IsEndDefined: boolean read GetIsEndDefined;
    60     property LineCap: TPenEndCap read FLineCap write SetLineCap;
    61     property StartSize: TPointF read FStartSizeFactor write SetStartSizeFactor;
    62     property EndSize: TPointF read FEndSizeFactor write SetEndSizeFactor;
    63     property StartOffsetX: single read FStartOffsetX write SetStartOffsetX;
    64     property EndOffsetX: single read FEndOffsetX write SetEndOffsetX;
    65     property StartRepeatCount: integer read FStartRepeatCount write SetStartRepeatCount;
    66     property EndRepeatCount: integer read FEndRepeatCount write SetEndRepeatCount;
     61    procedure StartAsNone; override;
     62    procedure StartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
     63    procedure StartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
     64    procedure StartAsTail; override;
     65    procedure EndAsNone; override;
     66    procedure EndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
     67    procedure EndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
     68    procedure EndAsTail; override;
     69    function ComputeStartAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override;
     70    function ComputeEndAt(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF; override;
     71
    6772  end;
    6873
     
    258263end;
    259264
     265function TBGRAArrow.GetEndOffsetX: single;
     266begin
     267  result := FEndOffsetX;
     268end;
     269
     270function TBGRAArrow.GetStartOffsetX: single;
     271begin
     272  result := FStartOffsetX;
     273end;
     274
     275function TBGRAArrow.GetStartRepeatCount: integer;
     276begin
     277  result := FStartRepeatCount;
     278end;
     279
     280function TBGRAArrow.GetStartSizeFactor: TPointF;
     281begin
     282  result := FStartSizeFactor;
     283end;
     284
    260285procedure TBGRAArrow.SetEndOffsetX(AValue: single);
    261286begin
     
    264289  FEndComputed:= false;
    265290  FEnd := nil;
     291end;
     292
     293function TBGRAArrow.GetLineCap: TPenEndCap;
     294begin
     295  result := FLineCap;
    266296end;
    267297
     
    324354end;
    325355
     356function TBGRAArrow.GetEndRepeatCount: integer;
     357begin
     358  Result:= FEndRepeatCount;
     359end;
     360
     361function TBGRAArrow.GetEndSizeFactor: TPointF;
     362begin
     363  Result:= FEndSizeFactor;
     364end;
     365
    326366constructor TBGRAArrow.Create;
    327367begin
     
    329369  FStartSizeFactor := PointF(2,2);
    330370  FEndSizeFactor := PointF(2,2);
     371end;
     372
     373procedure TBGRAArrow.StartAsNone;
     374begin
     375  SetStart(asNone);
     376end;
     377
     378procedure TBGRAArrow.StartAsClassic(AFlipped: boolean; ACut: boolean;
     379  ARelativePenWidth: single);
     380var join: TPenJoinStyle;
     381begin
     382  if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
     383  if ACut then
     384  begin
     385    if AFlipped then
     386      SetStart(asFlippedCut,join,ARelativePenWidth)
     387    else
     388      SetStart(asCut,join,ARelativePenWidth)
     389  end
     390  else
     391  begin
     392    if AFlipped then
     393      SetStart(asFlipped,join,ARelativePenWidth)
     394    else
     395      SetStart(asNormal,join,ARelativePenWidth)
     396  end;
     397end;
     398
     399procedure TBGRAArrow.StartAsTriangle(ABackOffset: single; ARounded: boolean;
     400  AHollow: boolean; AHollowPenWidth: single);
     401var join: TPenJoinStyle;
     402begin
     403  if ARounded then join := pjsRound else join := pjsMiter;
     404  if AHollow then
     405    SetStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
     406  else
     407    SetStart(asTriangle, join,1,ABackOffset);
     408end;
     409
     410procedure TBGRAArrow.StartAsTail;
     411begin
     412  SetStart(asTail);
     413end;
     414
     415procedure TBGRAArrow.EndAsNone;
     416begin
     417  SetEnd(asNone);
     418end;
     419
     420procedure TBGRAArrow.EndAsClassic(AFlipped: boolean; ACut: boolean;
     421  ARelativePenWidth: single);
     422var join: TPenJoinStyle;
     423begin
     424  if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
     425  if ACut then
     426  begin
     427    if AFlipped then
     428      SetEnd(asFlippedCut,join,ARelativePenWidth)
     429    else
     430      SetEnd(asCut,join,ARelativePenWidth)
     431  end
     432  else
     433  begin
     434    if AFlipped then
     435      SetEnd(asFlipped,join,ARelativePenWidth)
     436    else
     437      SetEnd(asNormal,join,ARelativePenWidth)
     438  end;
     439end;
     440
     441procedure TBGRAArrow.EndAsTriangle(ABackOffset: single; ARounded: boolean;
     442  AHollow: boolean; AHollowPenWidth: single);
     443var join: TPenJoinStyle;
     444begin
     445  if ARounded then join := pjsRound else join := pjsMiter;
     446  if AHollow then
     447    SetEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
     448  else
     449    SetEnd(asTriangle, join,1, ABackOffset);
     450end;
     451
     452procedure TBGRAArrow.EndAsTail;
     453begin
     454  SetEnd(asTail);
    331455end;
    332456
  • GraphicTest/Packages/bgrabitmap/bgrabitmap.pas

    r472 r494  
    55                 Free easy-to-use memory bitmap 32-bit,
    66                 8-bit for each channel, transparency.
    7                  Channels in that order : B G R A
     7                 Channels can be in the following orders:
     8                 - B G R A (recommended for Windows, required for fpGUI)
     9                 - R G B A (recommended for Gtk and MacOS)
    810
    911                 - Drawing primitives
     
    3840
    3941{$mode objfpc}{$H+}
     42{$i bgrabitmap.inc}
    4043
    4144interface
     
    4649uses
    4750  Classes, SysUtils,
    48 {$IFDEF LCLwin32}
    49   BGRAWinBitmap,
     51{$IFDEF BGRABITMAP_USE_FPGUI}
     52    BGRAfpGUIBitmap,
    5053{$ELSE}
    51   {$IFDEF LCLgtk}
    52   BGRAGtkBitmap,
    53   {$ELSE}
    54     {$IFDEF LCLgtk2}
    55   BGRAGtkBitmap,
    56     {$ELSE}
    57       {$IFDEF LCLqt}
    58   BGRAQtBitmap,
    59       {$ELSE}
    60   BGRADefaultBitmap,
    61       {$ENDIF}
    62     {$ENDIF}
    63   {$ENDIF}
     54        {$IFDEF BGRABITMAP_USE_LCL}
     55          {$IFDEF LCLwin32}
     56                BGRAWinBitmap,
     57          {$ELSE}
     58                {$IFDEF LCLgtk}
     59                BGRAGtkBitmap,
     60                {$ELSE}
     61                  {$IFDEF LCLgtk2}
     62                BGRAGtkBitmap,
     63                  {$ELSE}
     64                        {$IFDEF LCLqt}
     65                BGRAQtBitmap,
     66                        {$ELSE}
     67              {$IFDEF DARWIN}
     68        BGRAMacBitmap,
     69              {$ELSE}
     70                BGRALCLBitmap,
     71              {$ENDIF}
     72                        {$ENDIF}
     73                  {$ENDIF}
     74                {$ENDIF}
     75          {$ENDIF}
     76        {$ELSE}
     77          BGRANoGuiBitmap,
     78        {$ENDIF}
    6479{$ENDIF}
    65   Graphics;
     80  BGRAGraphics;
    6681
    6782type
    68 {$IFDEF LCLwin32}
    69   TBGRABitmap = TBGRAWinBitmap;
     83{$IFDEF BGRABITMAP_USE_FPGUI}
     84  TBGRABitmap = class(TBGRAfpGUIBitmap);
    7085{$ELSE}
    71   {$IFDEF LCLgtk}
    72   TBGRABitmap = TBGRAGtkBitmap;
    73   {$ELSE}
    74     {$IFDEF LCLgtk2}
    75   TBGRABitmap = TBGRAGtkBitmap;
     86    {$IFDEF BGRABITMAP_USE_LCL}
     87      {$IFDEF LCLwin32}
     88        TBGRABitmap = class(TBGRAWinBitmap);
     89      {$ELSE}
     90        {$IFDEF LCLgtk}
     91        TBGRABitmap = class(TBGRAGtkBitmap);
     92        {$ELSE}
     93          {$IFDEF LCLgtk2}
     94        TBGRABitmap = class(TBGRAGtkBitmap);
     95          {$ELSE}
     96            {$IFDEF LCLqt}
     97        TBGRABitmap = class(TBGRAQtBitmap);
     98            {$ELSE}
     99              {$IFDEF DARWIN}
     100        TBGRABitmap = class(TBGRAMacBitmap);
     101              {$ELSE}
     102        TBGRABitmap = class(TBGRALCLBitmap);
     103              {$ENDIF}
     104            {$ENDIF}
     105          {$ENDIF}
     106        {$ENDIF}
     107      {$ENDIF}
    76108    {$ELSE}
    77       {$IFDEF LCLqt}
    78   TBGRABitmap = TBGRAQtBitmap;
    79       {$ELSE}
    80   TBGRABitmap = TBGRADefaultBitmap;
    81       {$ENDIF}
     109      TBGRABitmap = class(TBGRANoGUIBitmap);
    82110    {$ENDIF}
    83   {$ENDIF}
    84111{$ENDIF}
    85112
     
    113140implementation
    114141
    115 uses GraphType, BGRABitmapTypes, BGRAReadBMP, BGRAReadGif,
    116   BGRAReadIco, bgrareadjpeg, BGRAReadLzp, BGRAReadPCX,
     142uses BGRABitmapTypes, BGRAReadBMP, BGRAReadBmpMioMap, BGRAReadGif,
     143  BGRAReadIco, BGRAReadJpeg, BGRAReadLzp, BGRAReadPCX,
    117144  BGRAReadPng, BGRAReadPSD, BGRAReadTGA, BGRAReadXPM,
    118145  BGRAWriteLzp;
  • GraphicTest/Packages/bgrabitmap/bgrabitmappack.lpk

    r472 r494  
    1 <?xml version="1.0"?>
     1<?xml version="1.0" encoding="UTF-8"?>
    22<CONFIG>
    33  <Package Version="4">
    44    <PathDelim Value="\"/>
    55    <Name Value="BGRABitmapPack"/>
    6     <AddToProjectUsesSection Value="True"/>
    76    <Author Value="Circular"/>
    87    <CompilerOptions>
     
    1918      <CodeGeneration>
    2019        <Optimizations>
     20          <OptimizationLevel Value="3"/>
    2121          <VariablesInRegisters Value="True"/>
    22           <OptimizationLevel Value="3"/>
    2322        </Optimizations>
    2423      </CodeGeneration>
     
    2827        </Debugging>
    2928      </Linking>
    30       <Other>
    31         <CompilerMessages>
    32           <UseMsgFile Value="True"/>
    33         </CompilerMessages>
    34         <CompilerPath Value="$(CompPath)"/>
    35       </Other>
    3629    </CompilerOptions>
    3730    <Description Value="Drawing routines with alpha blending and antialiasing"/>
    3831    <License Value="modified LGPL"/>
    39     <Version Major="8" Minor="1"/>
    40     <Files Count="91">
     32    <Version Major="9" Minor="3"/>
     33    <Files Count="108">
    4134      <Item1>
    4235        <Filename Value="bgraanimatedgif.pas"/>
     
    148141      </Item27>
    149142      <Item28>
    150         <Filename Value="bgrascene3dinterface.inc"/>
    151         <Type Value="Binary"/>
     143        <Filename Value="bgraslicescaling.pas"/>
     144        <UnitName Value="BGRASliceScaling"/>
    152145      </Item28>
    153146      <Item29>
    154         <Filename Value="bgraslicescaling.pas"/>
    155         <UnitName Value="BGRASliceScaling"/>
     147        <Filename Value="bgrasse.pas"/>
     148        <UnitName Value="BGRASSE"/>
    156149      </Item29>
    157150      <Item30>
    158         <Filename Value="bgrasse.pas"/>
    159         <UnitName Value="BGRASSE"/>
     151        <Filename Value="bgrastreamlayers.pas"/>
     152        <UnitName Value="BGRAStreamLayers"/>
    160153      </Item30>
    161154      <Item31>
    162         <Filename Value="bgrastreamlayers.pas"/>
    163         <UnitName Value="BGRAStreamLayers"/>
     155        <Filename Value="bgratext.pas"/>
     156        <UnitName Value="BGRAText"/>
    164157      </Item31>
    165158      <Item32>
    166         <Filename Value="bgratext.pas"/>
    167         <UnitName Value="BGRAText"/>
     159        <Filename Value="bgratextfx.pas"/>
     160        <UnitName Value="BGRATextFX"/>
    168161      </Item32>
    169162      <Item33>
    170         <Filename Value="bgratextfx.pas"/>
    171         <UnitName Value="BGRATextFX"/>
     163        <Filename Value="bgratransform.pas"/>
     164        <UnitName Value="BGRATransform"/>
    172165      </Item33>
    173166      <Item34>
    174         <Filename Value="bgratransform.pas"/>
    175         <UnitName Value="BGRATransform"/>
     167        <Filename Value="bgratypewriter.pas"/>
     168        <UnitName Value="BGRATypewriter"/>
    176169      </Item34>
    177170      <Item35>
    178         <Filename Value="bgratypewriter.pas"/>
    179         <UnitName Value="BGRATypewriter"/>
     171        <Filename Value="bgravectorize.pas"/>
     172        <UnitName Value="BGRAVectorize"/>
    180173      </Item35>
    181174      <Item36>
    182         <Filename Value="bgravectorize.pas"/>
    183         <UnitName Value="BGRAVectorize"/>
     175        <Filename Value="blendpixelinline.inc"/>
     176        <Type Value="Binary"/>
    184177      </Item36>
    185178      <Item37>
    186         <Filename Value="blendpixelinline.inc"/>
     179        <Filename Value="blendpixels.inc"/>
    187180        <Type Value="Binary"/>
    188181      </Item37>
    189182      <Item38>
    190         <Filename Value="blendpixels.inc"/>
     183        <Filename Value="blendpixelsover.inc"/>
    191184        <Type Value="Binary"/>
    192185      </Item38>
    193186      <Item39>
    194         <Filename Value="blendpixelsover.inc"/>
     187        <Filename Value="blurfast.inc"/>
    195188        <Type Value="Binary"/>
    196189      </Item39>
    197190      <Item40>
    198         <Filename Value="blurfast.inc"/>
     191        <Filename Value="blurnormal.inc"/>
    199192        <Type Value="Binary"/>
    200193      </Item40>
    201194      <Item41>
    202         <Filename Value="blurnormal.inc"/>
     195        <Filename Value="csscolorconst.inc"/>
    203196        <Type Value="Binary"/>
    204197      </Item41>
    205198      <Item42>
    206         <Filename Value="csscolorconst.inc"/>
     199        <Filename Value="lightingclasses3d.inc"/>
    207200        <Type Value="Binary"/>
    208201      </Item42>
    209202      <Item43>
    210         <Filename Value="filldensity256.inc"/>
     203        <Filename Value="lineartexscan.inc"/>
    211204        <Type Value="Binary"/>
    212205      </Item43>
    213206      <Item44>
    214         <Filename Value="filldensitysegment256.inc"/>
     207        <Filename Value="lineartexscan2.inc"/>
    215208        <Type Value="Binary"/>
    216209      </Item44>
    217210      <Item45>
    218         <Filename Value="lightingclasses3d.inc"/>
     211        <Filename Value="multishapeline.inc"/>
    219212        <Type Value="Binary"/>
    220213      </Item45>
    221214      <Item46>
    222         <Filename Value="lineartexscan.inc"/>
     215        <Filename Value="perspectivecolorscan.inc"/>
    223216        <Type Value="Binary"/>
    224217      </Item46>
    225218      <Item47>
    226         <Filename Value="lineartexscan2.inc"/>
     219        <Filename Value="perspectivescan.inc"/>
    227220        <Type Value="Binary"/>
    228221      </Item47>
    229222      <Item48>
    230         <Filename Value="multishapeline.inc"/>
     223        <Filename Value="perspectivescan2.inc"/>
    231224        <Type Value="Binary"/>
    232225      </Item48>
    233226      <Item49>
    234         <Filename Value="perspectivecolorscan.inc"/>
     227        <Filename Value="phongdraw.inc"/>
    235228        <Type Value="Binary"/>
    236229      </Item49>
    237230      <Item50>
    238         <Filename Value="perspectivescan.inc"/>
     231        <Filename Value="phongdrawsse.inc"/>
    239232        <Type Value="Binary"/>
    240233      </Item50>
    241234      <Item51>
    242         <Filename Value="perspectivescan2.inc"/>
     235        <Filename Value="phonglight.inc"/>
    243236        <Type Value="Binary"/>
    244237      </Item51>
    245238      <Item52>
    246         <Filename Value="phongdraw.inc"/>
     239        <Filename Value="phonglightsse.inc"/>
    247240        <Type Value="Binary"/>
    248241      </Item52>
    249242      <Item53>
    250         <Filename Value="phongdrawsse.inc"/>
     243        <Filename Value="polyaliaspersp.inc"/>
    251244        <Type Value="Binary"/>
    252245      </Item53>
    253246      <Item54>
    254         <Filename Value="phonglight.inc"/>
     247        <Filename Value="shapes3d.inc"/>
    255248        <Type Value="Binary"/>
    256249      </Item54>
    257250      <Item55>
    258         <Filename Value="phonglightsse.inc"/>
    259         <Type Value="Binary"/>
     251        <Filename Value="bgrasse.inc"/>
     252        <UnitName Value="bgrasse"/>
    260253      </Item55>
    261254      <Item56>
    262         <Filename Value="polyaliaspersp.inc"/>
    263         <Type Value="Binary"/>
     255        <Filename Value="bgragrayscalemask.pas"/>
     256        <UnitName Value="BGRAGrayscaleMask"/>
    264257      </Item56>
    265258      <Item57>
    266         <Filename Value="renderdensity256.inc"/>
    267         <Type Value="Binary"/>
     259        <Filename Value="bgrareadbmp.pas"/>
     260        <UnitName Value="BGRAReadBMP"/>
    268261      </Item57>
    269262      <Item58>
    270         <Filename Value="shapes3d.inc"/>
    271         <Type Value="Binary"/>
     263        <Filename Value="bgrareadgif.pas"/>
     264        <UnitName Value="BGRAReadGif"/>
    272265      </Item58>
    273266      <Item59>
    274         <Filename Value="winstream.inc"/>
    275         <Type Value="Binary"/>
     267        <Filename Value="bgrareadpcx.pas"/>
     268        <UnitName Value="BGRAReadPCX"/>
    276269      </Item59>
    277270      <Item60>
    278         <Filename Value="bgrasse.inc"/>
    279         <UnitName Value="bgrasse"/>
     271        <Filename Value="bgrareadpng.pas"/>
     272        <UnitName Value="BGRAReadPng"/>
    280273      </Item60>
    281274      <Item61>
    282         <Filename Value="sseloadv.inc"/>
    283         <UnitName Value="sseloadv"/>
     275        <Filename Value="bgrareadpsd.pas"/>
     276        <UnitName Value="BGRAReadPSD"/>
    284277      </Item61>
    285278      <Item62>
    286         <Filename Value="ssesavev.inc"/>
    287         <UnitName Value="ssesavev"/>
     279        <Filename Value="bgrathumbnail.pas"/>
     280        <UnitName Value="BGRAThumbnail"/>
    288281      </Item62>
    289282      <Item63>
    290         <Filename Value="bgragrayscalemask.pas"/>
    291         <UnitName Value="BGRAGrayscaleMask"/>
     283        <Filename Value="bgrareadtga.pas"/>
     284        <UnitName Value="BGRAReadTGA"/>
    292285      </Item63>
    293286      <Item64>
    294         <Filename Value="bgrareadbmp.pas"/>
    295         <UnitName Value="BGRAReadBMP"/>
     287        <Filename Value="bgrareadico.pas"/>
     288        <UnitName Value="BGRAReadIco"/>
    296289      </Item64>
    297290      <Item65>
    298         <Filename Value="bgrareadgif.pas"/>
    299         <UnitName Value="BGRAReadGif"/>
     291        <Filename Value="bgrareadjpeg.pas"/>
     292        <UnitName Value="BGRAReadJpeg"/>
    300293      </Item65>
    301294      <Item66>
    302         <Filename Value="bgrareadpcx.pas"/>
    303         <UnitName Value="BGRAReadPCX"/>
     295        <Filename Value="bgrareadlzp.pas"/>
     296        <UnitName Value="BGRAReadLzp"/>
    304297      </Item66>
    305298      <Item67>
    306         <Filename Value="bgrareadpng.pas"/>
    307         <UnitName Value="BGRAReadPng"/>
     299        <Filename Value="unzipperext.pas"/>
     300        <UnitName Value="UnzipperExt"/>
    308301      </Item67>
    309302      <Item68>
    310         <Filename Value="bgrareadpsd.pas"/>
    311         <UnitName Value="BGRAReadPSD"/>
     303        <Filename Value="bgralzpcommon.pas"/>
     304        <UnitName Value="BGRALzpCommon"/>
    312305      </Item68>
    313306      <Item69>
    314         <Filename Value="bgrathumbnail.pas"/>
    315         <UnitName Value="BGRAThumbnail"/>
     307        <Filename Value="bgrawritelzp.pas"/>
     308        <UnitName Value="BGRAWriteLzp"/>
    316309      </Item69>
    317310      <Item70>
    318         <Filename Value="bgrareadtga.pas"/>
    319         <UnitName Value="BGRAReadTGA"/>
     311        <Filename Value="bgrareadxpm.pas"/>
     312        <UnitName Value="BGRAReadXPM"/>
    320313      </Item70>
    321314      <Item71>
    322         <Filename Value="bgrareadico.pas"/>
    323         <UnitName Value="BGRAReadIco"/>
     315        <Filename Value="bgrasvg.pas"/>
     316        <UnitName Value="BGRASVG"/>
    324317      </Item71>
    325318      <Item72>
    326         <Filename Value="bgrareadjpeg.pas"/>
    327         <UnitName Value="bgrareadjpeg"/>
     319        <Filename Value="bgraunits.pas"/>
     320        <UnitName Value="BGRAUnits"/>
    328321      </Item72>
    329322      <Item73>
    330         <Filename Value="bgrareadlzp.pas"/>
    331         <UnitName Value="BGRAReadLzp"/>
     323        <Filename Value="bgrasvgshapes.pas"/>
     324        <UnitName Value="BGRASVGShapes"/>
    332325      </Item73>
    333326      <Item74>
    334         <Filename Value="unzipperext.pas"/>
    335         <UnitName Value="UnzipperExt"/>
     327        <Filename Value="bgrasvgtype.pas"/>
     328        <UnitName Value="BGRASVGType"/>
    336329      </Item74>
    337330      <Item75>
    338         <Filename Value="bgralzpcommon.pas"/>
    339         <UnitName Value="BGRALzpCommon"/>
     331        <Filename Value="bgrareadbmpmiomap.pas"/>
     332        <UnitName Value="BGRAReadBmpMioMap"/>
    340333      </Item75>
    341334      <Item76>
    342         <Filename Value="bgrawritelzp.pas"/>
    343         <UnitName Value="BGRAWriteLzp"/>
     335        <Filename Value="bgraarrow.pas"/>
     336        <UnitName Value="BGRAArrow"/>
    344337      </Item76>
    345338      <Item77>
    346         <Filename Value="bgrareadxpm.pas"/>
    347         <UnitName Value="BGRAReadXPM"/>
     339        <Filename Value="vertex3d.inc"/>
     340        <Type Value="Binary"/>
    348341      </Item77>
    349342      <Item78>
    350         <Filename Value="bgrasvg.pas"/>
    351         <UnitName Value="BGRASVG"/>
     343        <Filename Value="face3d.inc"/>
     344        <Type Value="Binary"/>
    352345      </Item78>
    353346      <Item79>
    354         <Filename Value="bgraunits.pas"/>
    355         <UnitName Value="BGRAUnits"/>
     347        <Filename Value="part3d.inc"/>
     348        <Type Value="Binary"/>
    356349      </Item79>
    357350      <Item80>
    358         <Filename Value="bgrasvgshapes.pas"/>
    359         <UnitName Value="BGRASVGShapes"/>
     351        <Filename Value="object3d.inc"/>
     352        <Type Value="Binary"/>
    360353      </Item80>
    361354      <Item81>
    362         <Filename Value="bgrasvgtype.pas"/>
    363         <UnitName Value="BGRASVGType"/>
     355        <Filename Value="bgrapalette.pas"/>
     356        <UnitName Value="BGRAPalette"/>
    364357      </Item81>
    365358      <Item82>
    366         <Filename Value="bgrareadbmpmiomap.pas"/>
    367         <UnitName Value="BGRAReadBmpMioMap"/>
     359        <Filename Value="bgracolorquantization.pas"/>
     360        <UnitName Value="BGRAColorQuantization"/>
    368361      </Item82>
    369362      <Item83>
    370         <Filename Value="bgraarrow.pas"/>
    371         <UnitName Value="BGRAArrow"/>
     363        <Filename Value="bgradithering.pas"/>
     364        <UnitName Value="BGRADithering"/>
    372365      </Item83>
    373366      <Item84>
    374         <Filename Value="vertex3d.inc"/>
     367        <Filename Value="paletteformats.inc"/>
    375368        <Type Value="Binary"/>
    376369      </Item84>
    377370      <Item85>
    378         <Filename Value="face3d.inc"/>
    379         <Type Value="Binary"/>
     371        <Filename Value="bgrautf8.pas"/>
     372        <UnitName Value="BGRAUTF8"/>
    380373      </Item85>
    381374      <Item86>
    382         <Filename Value="part3d.inc"/>
    383         <Type Value="Binary"/>
     375        <Filename Value="bgralclbitmap.pas"/>
     376        <UnitName Value="BGRALCLBitmap"/>
    384377      </Item86>
    385378      <Item87>
    386         <Filename Value="object3d.inc"/>
    387         <Type Value="Binary"/>
     379        <Filename Value="bgrawritepng.pas"/>
     380        <UnitName Value="BGRAWritePNG"/>
    388381      </Item87>
    389382      <Item88>
    390         <Filename Value="bgrapalette.pas"/>
    391         <UnitName Value="BGRAPalette"/>
     383        <Filename Value="bgragifformat.pas"/>
     384        <UnitName Value="BGRAGifFormat"/>
    392385      </Item88>
    393386      <Item89>
    394         <Filename Value="bgracolorquantization.pas"/>
    395         <UnitName Value="BGRAColorQuantization"/>
     387        <Filename Value="geometrytypes.inc"/>
     388        <Type Value="Binary"/>
    396389      </Item89>
    397390      <Item90>
    398         <Filename Value="bgradithering.pas"/>
    399         <UnitName Value="BGRADithering"/>
     391        <Filename Value="bgracustombitmap.inc"/>
     392        <Type Value="Binary"/>
    400393      </Item90>
    401394      <Item91>
    402         <Filename Value="paletteformats.inc"/>
    403         <Type Value="Binary"/>
     395        <Filename Value="bgragraphics.pas"/>
     396        <UnitName Value="BGRAGraphics"/>
    404397      </Item91>
     398      <Item92>
     399        <Filename Value="bgrascenetypes.pas"/>
     400        <UnitName Value="BGRASceneTypes"/>
     401      </Item92>
     402      <Item93>
     403        <Filename Value="bgrarenderer3d.pas"/>
     404        <UnitName Value="BGRARenderer3D"/>
     405      </Item93>
     406      <Item94>
     407        <Filename Value="bgrawritebmpmiomap.pas"/>
     408        <UnitName Value="BGRAWriteBmpMioMap"/>
     409      </Item94>
     410      <Item95>
     411        <Filename Value="bgraopengltype.pas"/>
     412        <UnitName Value="BGRAOpenGLType"/>
     413      </Item95>
     414      <Item96>
     415        <Filename Value="bgraspritegl.pas"/>
     416        <UnitName Value="BGRASpriteGL"/>
     417      </Item96>
     418      <Item97>
     419        <Filename Value="bgraopengl.pas"/>
     420        <UnitName Value="BGRAOpenGL"/>
     421      </Item97>
     422      <Item98>
     423        <Filename Value="bgracanvasgl.pas"/>
     424        <UnitName Value="BGRACanvasGL"/>
     425      </Item98>
     426      <Item99>
     427        <Filename Value="bgrafontgl.pas"/>
     428        <UnitName Value="BGRAFontGL"/>
     429      </Item99>
     430      <Item100>
     431        <Filename Value="bgraopengl3d.pas"/>
     432        <UnitName Value="BGRAOpenGL3D"/>
     433      </Item100>
     434      <Item101>
     435        <Filename Value="blurbox.inc"/>
     436        <Type Value="Text"/>
     437      </Item101>
     438      <Item102>
     439        <Filename Value="bgraphoxo.pas"/>
     440        <UnitName Value="BGRAPhoxo"/>
     441      </Item102>
     442      <Item103>
     443        <Filename Value="bgrafilterscanner.pas"/>
     444        <UnitName Value="BGRAFilterScanner"/>
     445      </Item103>
     446      <Item104>
     447        <Filename Value="bgrafiltertype.pas"/>
     448        <UnitName Value="BGRAFilterType"/>
     449      </Item104>
     450      <Item105>
     451        <Filename Value="bgrafilterblur.pas"/>
     452        <UnitName Value="BGRAFilterBlur"/>
     453      </Item105>
     454      <Item106>
     455        <Filename Value="bgramultifiletype.pas"/>
     456        <UnitName Value="bgramultifiletype"/>
     457      </Item106>
     458      <Item107>
     459        <Filename Value="bgrawinresource.pas"/>
     460        <UnitName Value="BGRAWinResource"/>
     461      </Item107>
     462      <Item108>
     463        <Filename Value="bgralazresource.pas"/>
     464        <UnitName Value="BGRALazResource"/>
     465      </Item108>
    405466    </Files>
    406467    <RequiredPkgs Count="2">
  • GraphicTest/Packages/bgrabitmap/bgrabitmappack.pas

    r472 r494  
    1717  BGRATransform, BGRATypewriter, BGRAVectorize, BGRAGrayscaleMask,
    1818  BGRAReadBMP, BGRAReadGif, BGRAReadPCX, BGRAReadPng, BGRAReadPSD,
    19   BGRAThumbnail, BGRAReadTGA, BGRAReadIco, bgrareadjpeg, BGRAReadLzp,
     19  BGRAThumbnail, BGRAReadTGA, BGRAReadIco, BGRAReadJpeg, BGRAReadLzp,
    2020  UnzipperExt, BGRALzpCommon, BGRAWriteLzp, BGRAReadXPM, BGRASVG, BGRAUnits,
    2121  BGRASVGShapes, BGRASVGType, BGRAReadBmpMioMap, BGRAArrow, BGRAPalette,
    22   BGRAColorQuantization, BGRADithering;
     22  BGRAColorQuantization, BGRADithering, BGRAUTF8, BGRALCLBitmap, BGRAWritePNG,
     23  BGRAGifFormat, BGRAGraphics, BGRASceneTypes, BGRARenderer3D,
     24  BGRAWriteBmpMioMap, BGRAOpenGLType, BGRASpriteGL, BGRAOpenGL, BGRACanvasGL,
     25  BGRAFontGL, BGRAOpenGL3D, BGRAPhoxo, BGRAFilterScanner, BGRAFilterType,
     26  BGRAFilterBlur, BGRAMultiFileType, BGRAWinResource, BGRALazResource;
    2327
    2428implementation
  • GraphicTest/Packages/bgrabitmap/bgrabitmaptypes.pas

    r472 r494  
    77
    88       --> Include BGRABitmap and BGRABitmapTypes in the 'uses' clause.
     9               If you are using LCL types, add also BGRAGraphics unit.
    910
    1011 ****************************************************************************
     
    2627
    2728{$mode objfpc}{$H+}
     29{$i bgrabitmap.inc}
    2830
    2931interface
    3032
    3133uses
    32   Classes, Types, Graphics, FPImage, FPImgCanv, GraphType;
     34  Classes, Types, BGRAGraphics,
     35  FPImage, FPImgCanv{$IFDEF BGRABITMAP_USE_LCL}, GraphType{$ENDIF},
     36  BGRAMultiFileType;
    3337
    3438type
    35   //pointer for direct pixel access
    36   PBGRAPixel = ^TBGRAPixel;
    37 
     39  TMultiFileContainer = BGRAMultiFileType.TMultiFileContainer;
    3840  Int32or64 = {$IFDEF CPU64}Int64{$ELSE}LongInt{$ENDIF};
    3941  UInt32or64 = {$IFDEF CPU64}UInt64{$ELSE}LongWord{$ENDIF};
    4042
    41   //Each pixel is a sequence of 4 bytes containing blue, green, red and alpha channel.
    42   TBGRAPixel = packed record
    43     blue, green, red, alpha: byte;
    44   end;
    45 
    46   ArrayOfTBGRAPixel = array of TBGRAPixel;
    47 
    48   //gamma expanded values
    49   TExpandedPixel = packed record
    50     red, green, blue, alpha: word;
    51   end;
    52 
    53   //pixel color defined in HSL colorspace
    54   THSLAPixel = packed record
    55     hue, saturation, lightness, alpha: word;
    56   end;
    57   TGSBAPixel = THSLAPixel;
    58 
    59   //general purpose color variable with floating point values
    60   TColorF = packed array[1..4] of single;
    61  
    62   { These types are used as parameters }
    63 
    64   TDrawMode = (dmSet,                   //replace pixels
    65                dmSetExceptTransparent,  //draw pixels with alpha=255
    66                dmLinearBlend,           //blend without gamma correction
    67                dmDrawWithTransparency,  //normal blending with gamma correction
    68                dmXor);                  //bitwise xor for all channels
    69   TChannel = (cRed, cGreen, cBlue, cAlpha);
    70   TChannels = set of TChannel;
    71                
    72   //floodfill option
    73   TFloodfillMode = (fmSet,                   //set pixels
    74                     fmDrawWithTransparency,  //draw fill color with transparency
    75                     fmProgressive);          //draw fill color with transparency according to similarity with start color
    76 
    77   TResampleMode = (rmSimpleStretch,   //low quality resample
    78                    rmFineResample);   //use resample filters and pixel-centered coordinates
    79   TResampleFilter = (rfBox,           //equivalent of stretch with high quality
    80                      rfLinear,        //linear interpolation
    81                      rfHalfCosine,    //mix of rfLinear and rfCosine
    82                      rfCosine,        //cosine-like interpolation
    83                      rfBicubic,       //simple bi-cubic filter (blur)
    84                      rfMitchell,      //downsizing interpolation
    85                      rfSpline,        //upsizing interpolation
    86                      rfLanczos2,      //Lanczos with radius 2
    87                      rfLanczos3,      //Lanczos with radius 3
    88                      rfLanczos4,      //Lanczos with radius 4
    89                      rfBestQuality);  //mix of rfMitchell and rfSpline
    90 
    91   TDitheringAlgorithm = (daNearestNeighbor, daFloydSteinberg);
    92   TAlphaChannelPaletteOption = (acIgnore, acTransparentEntry, acFullChannelInPalette);
     43{=== Miscellaneous types ===}
     44
     45type
     46  {* Options when doing a floodfill (also called bucket fill) }
     47  TFloodfillMode = (
     48    {** Pixels that are filled are replaced }
     49    fmSet,
     50    {** Pixels that are filled are drawn upon with the fill color }
     51    fmDrawWithTransparency,
     52    {** Pixels that are filled are drawn upon to the extent that the color underneath is similar to
     53        the start color. The more different the different is, the less it is drawn upon }
     54    fmProgressive);
     55
     56  {* Specifies how much smoothing is applied to the computation of the median }
     57  TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
     58  {* Specifies the shape of a predefined blur }
     59  TRadialBlurType = (
     60    {** Gaussian-like, pixel importance decreases progressively }
     61    rbNormal,
     62    {** Disk blur, pixel importance does not decrease progressively }
     63    rbDisk,
     64    {** Pixel are considered when they are at a certain distance }
     65    rbCorona,
     66    {** Gaussian-like, but 10 times smaller than ''rbNormal'' }
     67    rbPrecise,
     68    {** Gaussian-like but simplified to be computed faster }
     69    rbFast,
     70    {** Box blur, pixel importance does not decrease progressively
     71        and the pixels are included when they are in a square.
     72        This is much faster than ''rbFast'' however you may get
     73        square shapes in the resulting image }
     74    rbBox);
     75
     76  TEmbossOption = (eoTransparent, eoPreserveHue);
     77  TEmbossOptions = set of TEmbossOption;
     78
     79  TTextLayout = BGRAGraphics.TTextLayout;
    9380
    9481const
     82  tlTop = BGRAGraphics.tlTop;
     83  tlCenter = BGRAGraphics.tlCenter;
     84  tlBottom = BGRAGraphics.tlBottom;
     85
     86  // checks the bounds of an image in the given clipping rectangle
     87  function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean;
     88
     89{==== Imported from GraphType ====}
     90//if this unit is defined, otherwise
     91//define here the types used by the library.
     92{$IFDEF BGRABITMAP_USE_LCL}
     93  type
     94    { Order of the lines in an image }
     95    TRawImageLineOrder = GraphType.TRawImageLineOrder;
     96    { Order of the bits in a byte containing pixel values }
     97    TRawImageBitOrder = GraphType.TRawImageBitOrder;
     98    { Order of the bytes in a group of byte containing pixel values }
     99    TRawImageByteOrder = GraphType.TRawImageByteOrder;
     100    { Definition of a single line 3D bevel }
     101    TGraphicsBevelCut = GraphType.TGraphicsBevelCut;
     102
     103  const
     104    riloTopToBottom = GraphType.riloTopToBottom;   // The first line (line 0) is the top line
     105    riloBottomToTop = GraphType.riloBottomToTop;   // The first line (line 0) is the bottom line
     106
     107    riboBitsInOrder = GraphType.riboBitsInOrder;   // Bit 0 is pixel 0
     108    riboReversedBits = GraphType.riboReversedBits; // Bit 0 is pixel 7 (Bit 1 is pixel 6, ...)
     109
     110    riboLSBFirst = GraphType.riboLSBFirst; // least significant byte first (little endian)
     111    riboMSBFirst = GraphType.riboMSBFirst; // most significant byte first (big endian)
     112
     113    fsSurface = GraphType.fsSurface; //type is defined as Graphics.TFillStyle
     114    fsBorder = GraphType.fsBorder;
     115
     116    bvNone = GraphType.bvNone;
     117    bvLowered = GraphType.bvLowered;
     118    bvRaised = GraphType.bvRaised;
     119    bvSpace = GraphType.bvSpace;
     120{$ELSE}
     121  type
     122    {* Order of the lines in an image }
     123    TRawImageLineOrder = (
     124      {** The first line in memory (line 0) is the top line }
     125      riloTopToBottom,
     126      {** The first line in memory (line 0) is the bottom line }
     127      riloBottomToTop);
     128
     129    {* Order of the bits in a byte containing pixel values }
     130    TRawImageBitOrder = (
     131      {** The lowest bit is on the left. So with a monochrome picture, bit 0 would be pixel 0 }
     132      riboBitsInOrder,
     133      {** The lowest bit is on the right. So with a momochrome picture, bit 0 would be pixel 7 (bit 1 would be pixel 6, ...) }
     134      riboReversedBits);
     135
     136    {* Order of the bytes in a group of byte containing pixel values }
     137    TRawImageByteOrder = (
     138      {** Least significant byte first (little endian) }
     139      riboLSBFirst,
     140      {** most significant byte first (big endian) }
     141      riboMSBFirst);
     142
     143    {* Definition of a single line 3D bevel }
     144    TGraphicsBevelCut =
     145    (
     146      {** No bevel }
     147      bvNone,
     148      {** Shape is lowered, light is on the bottom-right corner }
     149      bvLowered,
     150      {** Shape is raised, light is on the top-left corner }
     151      bvRaised,
     152      {** Shape is at the same level, there is no particular lighting }
     153      bvSpace);
     154{$ENDIF}
     155
     156{$DEFINE INCLUDE_INTERFACE}
     157{$I bgrapixel.inc}
     158
     159{$DEFINE INCLUDE_INTERFACE}
     160{$I geometrytypes.inc}
     161
     162{$DEFINE INCLUDE_INTERFACE}
     163{$i csscolorconst.inc}
     164
     165{$DEFINE INCLUDE_SCANNER_INTERFACE }
     166{$I bgracustombitmap.inc}
     167
     168{==== Integer math ====}
     169
     170  {* Computes the value modulo cycle, and if the ''value'' is negative, the result
     171     is still positive }
     172  function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
     173
     174  { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
     175    They use a table to store already computed values. The return value is an integer
     176    ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is
     177    32768 instead of 1. The input has a period of 65536, so you can supply any integer
     178    without applying a modulo. }
     179
     180  { Compute all values now }
     181  procedure PrecalcSin65536;
     182
     183  {* Returns an integer approximation of the sine. Value ranges from 0 to 65535,
     184     where 65536 corresponds to the next cycle }
     185  function Sin65536(value: word): Int32or64; inline;
     186  {* Returns an integer approximation of the cosine. Value ranges from 0 to 65535,
     187     where 65536 corresponds to the next cycle }
     188  function Cos65536(value: word): Int32or64; inline;
     189
     190  {* Returns the square root of the given byte, considering that
     191     255 is equal to unity }
     192  function ByteSqrt(value: byte): byte; inline;
     193
     194{==== Types provided for fonts ====}
     195type
     196  {* Quality to be used to render text }
     197  TBGRAFontQuality = (
     198    {** Use the system capabilities. It is rather fast however it may be
     199        not be smoothed. }
     200    fqSystem,
     201    {** Use the system capabilities to render with ClearType. This quality is
     202        of course better than fqSystem however it may not be perfect.}
     203    fqSystemClearType,
     204    {** Garanties a high quality antialiasing. }
     205    fqFineAntialiasing,
     206    {** Fine antialiasing with ClearType in assuming an LCD display in red/green/blue order }
     207    fqFineClearTypeRGB,
     208    {** Fine antialiasing with ClearType in assuming an LCD display in blue/green/red order }
     209    fqFineClearTypeBGR);
     210
     211  {* Measurements of a font }
     212  TFontPixelMetric = record
     213    {** The values have been computed }
     214    Defined: boolean;
     215    {** Position of the baseline, where most letters lie }
     216    Baseline,
     217    {** Position of the top of the small letters (x being one of them) }
     218    xLine,
     219    {** Position of the top of the UPPERCASE letters }
     220    CapLine,
     221    {** Position of the bottom of letters like g and p }
     222    DescentLine,
     223    {** Total line height including line spacing defined by the font }
     224    Lineheight: integer;
     225  end;
     226
     227  {* Vertical anchoring of the font. When text is drawn, a start coordinate
     228      is necessary. Text can be positioned in different ways. This enum
     229      defines what position it is regarding the font }
     230  TFontVerticalAnchor = (
     231    {** The top of the font. Everything will be drawn below the start coordinate. }
     232    fvaTop,
     233    {** The center of the font }
     234    fvaCenter,
     235    {** The top of capital letters }
     236    fvaCapLine,
     237    {** The center of capital letters }
     238    fvaCapCenter,
     239    {** The top of small letters }
     240    fvaXLine,
     241    {** The center of small letters }
     242    fvaXCenter,
     243    {** The baseline, the bottom of most letters }
     244    fvaBaseline,
     245    {** The bottom of letters that go below the baseline }
     246    fvaDescentLine,
     247    {** The bottom of the font. Everything will be drawn above the start coordinate }
     248    fvaBottom);
     249
     250  {* Definition of a function that handles work-break }
     251  TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;
     252
     253  {* Alignment for a typewriter, that does not have any more information
     254     than a square shape containing glyphs }
     255  TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight, twaLeft, twaMiddle, twaRight, twaBottomLeft, twaBottom, twaBottomRight);
     256  {* How a typewriter must render its content on a Canvas2d }
     257  TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill);
     258
     259  { TBGRACustomFontRenderer }
     260  {* Abstract class for all font renderers }
     261  TBGRACustomFontRenderer = class
     262    {** Specifies the font to use. Unless the font renderer accept otherwise,
     263        the name is in human readable form, like 'Arial', 'Times New Roman', ...  }
     264    FontName: string;
     265
     266    {** Specifies the set of styles to be applied to the font.
     267        These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
     268        So the value [fsBold,fsItalic] means that the font must be bold and italic }
     269    FontStyle: TFontStyles;
     270
     271    {** Specifies the quality of rendering. Default value is fqSystem }
     272    FontQuality : TBGRAFontQuality;
     273
     274    {** Specifies the rotation of the text, for functions that support text rotation.
     275        It is expressed in tenth of degrees, positive values going counter-clockwise }
     276    FontOrientation: integer;
     277
     278    {** Specifies the height of the font without taking into account additional line spacing.
     279        A negative value means that it is the full height instead }
     280    FontEmHeight: integer;
     281
     282    {** Returns measurement for the current font in pixels }
     283    function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
     284
     285    {** Returns the total size of the string provided using the current font.
     286        Orientation is not taken into account, so that the width is along the text }
     287    function TextSize(sUTF8: string): TSize; virtual; abstract;
     288
     289    {** Draws the UTF8 encoded string, with color ''c''.
     290        If align is taLeftJustify, (''x'',''y'') is the top-left corner.
     291        If align is taCenter, (''x'',''y'') is at the top and middle of the text.
     292        If align is taRightJustify, (''x'',''y'') is the top-right corner.
     293        The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
     294    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
     295
     296    {** Same as above functions, except that the text is filled using texture.
     297        The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
     298    procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
     299
     300    {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
     301    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
     302    {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
     303    procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
     304
     305    {** Draw the UTF8 encoded string at the coordinate (''x'',''y''), clipped inside the rectangle ''ARect''.
     306        Additional style information is provided by the style parameter.
     307        The color ''c'' is used to fill the text. No rotation is applied. }
     308    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract;
     309
     310    {** Same as above except a ''texture'' is used to fill the text }
     311    procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract;
     312
     313    {** Copy the path for the UTF8 encoded string into ''ADest''.
     314        If ''align'' is ''taLeftJustify'', (''x'',''y'') is the top-left corner.
     315        If ''align'' is ''taCenter'', (''x'',''y'') is at the top and middle of the text.
     316        If ''align'' is ''taRightJustify'', (''x'',''y'') is the top-right corner. }
     317    procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional
     318  end;
     319
     320  {* Output mode for the improved renderer for readability. This is used by the font renderer based on LCL in ''BGRAText'' }
     321  TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
     322
     323{** Removes line ending and tab characters from a string (for a function
     324    like ''TextOut'' that does not handle this). this works with UTF8 strings
     325    as well }
     326function CleanTextOutString(s: string): string;
     327{** Remove the line ending at the specified position or return False.
     328    This works with UTF8 strings however the index is the byte index }
     329function RemoveLineEnding(var s: string; indexByte: integer): boolean;
     330{** Remove the line ending at the specified position or return False.
     331    The index is the character index, that may be different from the
     332    byte index }
     333function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     334{** Default word break handler, that simply divide when there is a space }
     335procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
     336
     337{==== Images and resampling ====}
     338
     339type
     340  {* How the resample is to be computed }
     341  TResampleMode = (
     342    {** Low quality resample by repeating pixels, stretching them }
     343    rmSimpleStretch,
     344    {** Use resample filters. This gives high
     345        quality resampling however this the proportion changes slightly because
     346        the first and last pixel are considered to occupy only half a unit as
     347        they are considered as the border of the picture
     348        (pixel-centered coordinates) }
     349    rmFineResample);
     350
     351  {* List of resample filter to be used with ''rmFineResample'' }
     352  TResampleFilter = (
     353    {** Equivalent of simple stretch with high quality and pixel-centered coordinates }
     354    rfBox,
     355    {** Linear interpolation giving slow transition between pixels }
     356    rfLinear,
     357    {** Mix of ''rfLinear'' and ''rfCosine'' giving medium speed stransition between pixels }
     358    rfHalfCosine,
     359    {** Cosine-like interpolation giving fast transition between pixels }
     360    rfCosine,
     361    {** Simple bi-cubic filter (blurry) }
     362    rfBicubic,
     363    {** Mitchell filter, good for downsizing interpolation }
     364    rfMitchell,
     365    {** Spline filter, good for upsizing interpolation, however slightly blurry }
     366    rfSpline,
     367    {** Lanczos with radius 2, blur is corrected }
     368    rfLanczos2,
     369    {** Lanczos with radius 3, high contrast }
     370    rfLanczos3,
     371    {** Lanczos with radius 4, high contrast }
     372    rfLanczos4,
     373    {** Best quality using rfMitchell or rfSpline }
     374    rfBestQuality);
     375
     376const
     377  {** List of strings to represent resample filters }
    95378  ResampleFilterStr : array[TResampleFilter] of string =
    96379   ('Box','Linear','HalfCosine','Cosine','Bicubic','Mitchell','Spline',
    97380    'Lanczos2','Lanczos3','Lanczos4','BestQuality');
    98381
    99 function StrToResampleFilter(str: string): TResampleFilter;
     382  {** Gives the sample filter represented by a string }
     383  function StrToResampleFilter(str: string): TResampleFilter;
    100384
    101385type
    102   TBGRAImageFormat = (ifUnknown, ifJpeg, ifPng, ifGif, ifBmp, ifIco, ifPcx, ifPaintDotNet, ifLazPaint, ifOpenRaster,
    103     ifPsd, ifTarga, ifTiff, ifXwd, ifXPixMap, ifBmpMioMap);
     386  {* List of image formats }
     387  TBGRAImageFormat = (
     388    {** Unknown format }
     389    ifUnknown,
     390    {** JPEG format, opaque, lossy compression }
     391    ifJpeg,
     392    {** PNG format, transparency, lossless compression }
     393    ifPng,
     394    {** GIF format, single transparent color, lossless in theory but only low number of colors allowed }
     395    ifGif,
     396    {** BMP format, transparency, no compression. Note that transparency is
     397        not supported by all BMP readers so it is not recommended to avoid
     398        storing images with transparency in this format }
     399    ifBmp,
     400    {** ICO format, contains different sizes of the same image }
     401    ifIco,
     402    {** PCX format, opaque, rudimentary lossless compression }
     403    ifPcx,
     404    {** Paint.NET format, layers, lossless compression }
     405    ifPaintDotNet,
     406    {** LazPaint format, layers, lossless compression }
     407    ifLazPaint,
     408    {** OpenRaster format, layers, lossless compression }
     409    ifOpenRaster,
     410    {** Phoxo format, layers }
     411    ifPhoxo,
     412    {** Photoshop format, layers, rudimentary lossless compression }
     413    ifPsd,
     414    {** Targa format (TGA), transparency, rudimentary lossless compression }
     415    ifTarga,
     416    {** TIFF format, limited support }
     417    ifTiff,
     418    {** X-Window capture, limited support }
     419    ifXwd,
     420    {** X-Pixmap, text encoded image, limited support }
     421    ifXPixMap,
     422    {** iGO BMP, limited support }
     423    ifBmpMioMap);
     424
     425  {* Options when loading an image }
     426  TBGRALoadingOption = (
     427     {** Do not clear RGB channels when alpha is zero (not recommended) }
     428     loKeepTransparentRGB,
     429     {** Consider BMP to be opaque if no alpha value is provided (for compatibility) }
     430     loBmpAutoOpaque,
     431     {** Load JPEG quickly however with a lower quality }
     432     loJpegQuick);
     433  TBGRALoadingOptions = set of TBGRALoadingOption;
    104434
    105435var
     436  {** List of stream readers for images }
    106437  DefaultBGRAImageReader: array[TBGRAImageFormat] of TFPCustomImageReaderClass;
     438  {** List of stream writers for images }
    107439  DefaultBGRAImageWriter: array[TBGRAImageFormat] of TFPCustomImageWriterClass;
    108440
    109 type
    110   TBGRAFontQuality = (fqSystem, fqSystemClearType, fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR);
    111         // fqSystem: use system rendering. It is fast however it may be not be smoothed.
    112         // fqSystemClearType: use system rendering with ClearType. This quality is of course better than fqSystem however it may not be much smoother.
    113         // fqFineAntialiasing: garanties a high quality antialiasing. This is slower.
    114         // fqFineClearTypeRGB: garanties a high quality antialiasing with ClearType. The order of the color in the LCD screen is supposed to be un red/green/blue order.
    115         // fqFineClearTypeBGR: same as above, except the color of the LCD screen is supposed to be in blue/green/red order.
    116 
    117   TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
    118   TRadialBlurType = (rbNormal, rbDisk, rbCorona, rbPrecise, rbFast, rbBox);
    119   TSplineStyle = (ssInside, ssInsideWithEnds, ssCrossing, ssCrossingWithEnds,
    120     ssOutside, ssRoundOutside, ssVertexToSide);
    121  
    122   { Advanced blending modes
    123     see : http://www.brighthub.com/multimedia/photography/articles/18301.aspx
    124     and : http://www.pegtop.net/delphi/articles/blendmodes/ }
    125   TBlendOperation = (boLinearBlend, boTransparent,                                  //blending
    126     boLighten, boScreen, boAdditive, boLinearAdd, boColorDodge, boDivide, boNiceGlow, boSoftLight, boHardLight, //lighting
    127     boGlow, boReflect, boOverlay, boDarkOverlay, boDarken, boMultiply, boColorBurn, //masking
    128     boDifference, boLinearDifference, boExclusion, boLinearExclusion, boSubtract, boLinearSubtract, boSubtractInverse, boLinearSubtractInverse,
    129     boNegation, boLinearNegation, boXor);         //negative
    130 
    131 const
    132   boGlowMask = boGlow;
    133   boLinearMultiply = boMultiply;
    134   boNonLinearOverlay = boDarkOverlay;
    135   EmptyRect : TRect = (left:0; top:0; right:0; bottom: 0);
    136 
    137 const
    138   BlendOperationStr : array[TBlendOperation] of string =
    139    ('LinearBlend', 'Transparent',
    140     'Lighten', 'Screen', 'Additive', 'LinearAdd', 'ColorDodge', 'Divide', 'NiceGlow', 'SoftLight', 'HardLight',
    141     'Glow', 'Reflect', 'Overlay', 'DarkOverlay', 'Darken', 'Multiply', 'ColorBurn',
    142     'Difference', 'LinearDifference', 'Exclusion', 'LinearExclusion', 'Subtract', 'LinearSubtract', 'SubtractInverse', 'LinearSubtractInverse',
    143     'Negation', 'LinearNegation', 'Xor');
    144 
    145 function StrToBlendOperation(str: string): TBlendOperation;
    146 
    147 type
    148   TGradientType = (gtLinear, gtReflected, gtDiamond, gtRadial);
    149 const
    150   GradientTypeStr : array[TGradientType] of string =
    151   ('Linear','Reflected','Diamond','Radial');
    152 function StrToGradientType(str: string): TGradientType;
    153  
    154 type
    155   { A pen style is defined as a list of floating number. The first number is the length of the first dash,
    156     the second number is the length of the first gap, the third number is the length of the second dash...
    157     It must have an even number of values. }
    158   TBGRAPenStyle = Array Of Single;
    159   TRoundRectangleOption = (rrTopLeftSquare,rrTopRightSquare,rrBottomRightSquare,rrBottomLeftSquare,
    160                            rrTopLeftBevel,rrTopRightBevel,rrBottomRightBevel,rrBottomLeftBevel,rrDefault);
    161   TRoundRectangleOptions = set of TRoundRectangleOption;
    162   TPolygonOrder = (poNone, poFirstOnTop, poLastOnTop); //see TBGRAMultiShapeFiller in BGRAPolygon
    163  
    164 function BGRAPenStyle(dash1, space1: single; dash2: single=0; space2: single = 0; dash3: single=0; space3: single = 0; dash4 : single = 0; space4 : single = 0): TBGRAPenStyle; 
    165  
    166 { Point, polygon and curve structures }
    167 type
    168   PPointF = ^TPointF;
    169   TPointF = packed record
    170     x, y: single;
    171   end;
    172   ArrayOfTPointF = array of TPointF;
    173   TArcOption = (aoClosePath, aoPie, aoFillPath);
    174   TArcOptions = set of TArcOption;
    175 
    176   TCubicBezierCurve = record
    177     p1,c1,c2,p2: TPointF;
    178   end;
    179   TQuadraticBezierCurve = record
    180     p1,c,p2: TPointF;
    181   end;
    182 
    183   TArcDef = record
    184     center: TPointF;
    185     radius: TPointF;
    186     xAngleRadCW, startAngleRadCW, endAngleRadCW: single; //see convention in BGRAPath
    187     anticlockwise: boolean
    188   end;
    189   PArcDef = ^TArcDef;
    190 
    191   TPoint3D = record
    192     x,y,z: single;
    193   end;
    194 
    195   TBGRAArrowStyle = (asNone, asNormal, asCut, asTriangle, asHollowTriangle, asFlipped, asFlippedCut, asTail, asTailRepeat);
    196 
    197   TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight,
    198                               twaLeft, twaMiddle, twaRight,
    199                               twaBottomLeft, twaBottom, twaBottomRight);
    200   TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill);
    201 
    202 function ConcatPointsF(const APolylines: array of ArrayOfTPointF): ArrayOfTPointF;
    203 
    204 function Point3D(x,y,z: single): TPoint3D;
    205 operator = (const v1,v2: TPoint3D): boolean; inline;
    206 operator * (const v1,v2: TPoint3D): single; inline;
    207 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline;
    208 operator - (const v1,v2: TPoint3D): TPoint3D; inline;
    209 operator - (const v: TPoint3D): TPoint3D; inline;
    210 operator + (const v1,v2: TPoint3D): TPoint3D; inline;
    211 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D);
    212 procedure Normalize3D(var v: TPoint3D); inline;
    213 
    214 function BezierCurve(origin, control1, control2, destination: TPointF) : TCubicBezierCurve; overload;
    215 function BezierCurve(origin, control, destination: TPointF) : TQuadraticBezierCurve; overload;
    216 function BezierCurve(origin, destination: TPointF) : TQuadraticBezierCurve; overload;
    217 function ArcDef(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean) : TArcDef;
    218 
    219 { Useful constants }
    220 const
    221   dmFastBlend = dmLinearBlend;
    222   EmptySingle: single = -3.402823e38;                        //used as a separator in floating point lists
    223   EmptyPointF: TPointF = (x: -3.402823e38; y: -3.402823e38); //used as a separator in TPointF lists
    224   BGRAPixelTransparent: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 0);
    225   BGRAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255);
    226   BGRABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
    227 
    228   { This color is needed for drawing black shapes on the standard TCanvas, because
    229     when drawing with pure black, there is no way to know if something has been
    230     drawn or if it is transparent }
    231   clBlackOpaque = TColor($010000);
    232 
    233 {$DEFINE INCLUDE_COLOR_CONST}
    234 {$i csscolorconst.inc}
    235 
    236 type
    237   TBGRAColorDefinition = record
    238     Name: string;
    239     Color: TBGRAPixel;
    240   end;
    241 
    242   { TBGRAColorList }
    243 
    244   TBGRAColorList = class
    245   protected
    246     FFinished: boolean;
    247     FNbColors: integer;
    248     FColors: array of TBGRAColorDefinition;
    249     function GetByIndex(Index: integer): TBGRAPixel;
    250     function GetByName(Name: string): TBGRAPixel;
    251     function GetName(Index: integer): string;
    252   public
    253     constructor Create;
    254     procedure Add(Name: string; const Color: TBGRAPixel);
    255     procedure Finished;
    256     function IndexOf(Name: string): integer;
    257     function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
    258 
    259     property ByName[Name: string]: TBGRAPixel read GetByName;
    260     property ByIndex[Index: integer]: TBGRAPixel read GetByIndex; default;
    261     property Name[Index: integer]: string read GetName;
    262     property Count: integer read FNbColors;
    263   end;
    264 
    265 var
    266   VGAColors, CSSColors: TBGRAColorList;
    267 
    268 function isEmptyPointF(pt: TPointF): boolean;
    269 
    270 type
    271   TFontPixelMetric = record
    272     Defined: boolean;
    273     Baseline, xLine, CapLine, DescentLine, Lineheight: integer;
    274   end;
    275 
    276   { A scanner is like an image, but its content has no limit and can be calculated on the fly.
    277     It must not implement reference counting. }
    278   IBGRAScanner = interface
    279     procedure ScanMoveTo(X,Y: Integer);
    280     function ScanNextPixel: TBGRAPixel;
    281     function ScanAt(X,Y: Single): TBGRAPixel;
    282     function ScanAtInteger(X,Y: integer): TBGRAPixel;
    283     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode);
    284     function IsScanPutPixelsDefined: boolean;
    285   end;
    286 
    287   { A path is the ability to define a contour with moveTo, lineTo...
    288     It must not implement reference counting. }
    289   IBGRAPath = interface
    290     procedure closePath;
    291     procedure moveTo(const pt: TPointF);
    292     procedure lineTo(const pt: TPointF);
    293     procedure polylineTo(const pts: array of TPointF);
    294     procedure quadraticCurveTo(const cp,pt: TPointF);
    295     procedure bezierCurveTo(const cp1,cp2,pt: TPointF);
    296     procedure arc(const arcDef: TArcDef);
    297     procedure copyTo(dest: IBGRAPath);
    298   end;
    299 
    300   TScanAtFunction = function (X,Y: Single): TBGRAPixel of object;
    301   TScanAtIntegerFunction = function (X,Y: Integer): TBGRAPixel of object;
    302   TScanNextPixelFunction = function: TBGRAPixel of object;
    303   TBGRACustomGradient = class;
    304 
    305   TBGRACustomFillInfo = class;
    306   TBGRACustomFontRenderer = class;
    307 
    308   { TBGRACustomBitmap }
    309 
    310   TBGRACustomBitmap = class(TFPCustomImage,IBGRAScanner) // a bitmap can be used as a scanner
    311   private
    312     function GetFontAntialias: Boolean;
    313     procedure SetFontAntialias(const AValue: Boolean);
    314   protected
    315      { accessors to properies }
    316      function GetArrowEndRepeat: integer; virtual; abstract;
    317      function GetArrowStartRepeat: integer; virtual; abstract;
    318      procedure SetArrowEndRepeat(AValue: integer); virtual; abstract;
    319      procedure SetArrowStartRepeat(AValue: integer); virtual; abstract;
    320      function GetArrowEndOffset: single; virtual; abstract;
    321      function GetArrowStartOffset: single; virtual; abstract;
    322      procedure SetArrowEndOffset(AValue: single); virtual; abstract;
    323      procedure SetArrowStartOffset(AValue: single); virtual; abstract;
    324      function GetArrowEndSize: TPointF; virtual; abstract;
    325      function GetArrowStartSize: TPointF; virtual; abstract;
    326      procedure SetArrowEndSize(AValue: TPointF); virtual; abstract;
    327      procedure SetArrowStartSize(AValue: TPointF); virtual; abstract;
    328      function GetLineCap: TPenEndCap; virtual; abstract;
    329      procedure SetLineCap(AValue: TPenEndCap); virtual; abstract;
    330      function GetFontRenderer: TBGRACustomFontRenderer; virtual; abstract;
    331      procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); virtual; abstract;
    332      function GetHeight: integer; virtual; abstract;
    333      function GetWidth: integer; virtual; abstract;
    334      function GetDataPtr: PBGRAPixel; virtual; abstract;
    335      function GetNbPixels: integer; virtual; abstract;
    336      function CheckEmpty: boolean; virtual; abstract;
    337      function GetHasTransparentPixels: boolean; virtual; abstract;
    338      function GetAverageColor: TColor; virtual; abstract;
    339      function GetAveragePixel: TBGRAPixel; virtual; abstract;
    340      procedure SetCanvasOpacity(AValue: byte); virtual; abstract;
    341      function GetScanLine(y: integer): PBGRAPixel; virtual; abstract;
    342      function GetRefCount: integer; virtual; abstract;
    343      function GetBitmap: TBitmap; virtual; abstract;
    344      function GetLineOrder: TRawImageLineOrder; virtual; abstract;
    345      function GetCanvasFP: TFPImageCanvas; virtual; abstract;
    346      function GetCanvasDrawModeFP: TDrawMode; virtual; abstract;
    347      procedure SetCanvasDrawModeFP(const AValue: TDrawMode); virtual; abstract;
    348      function GetCanvas: TCanvas; virtual; abstract;
    349      function GetCanvasOpacity: byte; virtual; abstract;
    350      function GetCanvasAlphaCorrection: boolean; virtual; abstract;
    351      procedure SetCanvasAlphaCorrection(const AValue: boolean); virtual; abstract;
    352      function GetFontHeight: integer; virtual; abstract;
    353      procedure SetFontHeight(AHeight: integer); virtual; abstract;
    354      function GetFontFullHeight: integer; virtual; abstract;
    355      procedure SetFontFullHeight(AHeight: integer); virtual; abstract;
    356      function GetPenStyle: TPenStyle; virtual; abstract;
    357      procedure SetPenStyle(const AValue: TPenStyle); virtual; abstract;
    358      function GetCustomPenStyle: TBGRAPenStyle; virtual; abstract;
    359      procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); virtual; abstract;
    360      function GetClipRect: TRect; virtual; abstract;
    361      procedure SetClipRect(const AValue: TRect); virtual; abstract;
    362      function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
    363      procedure ClearTransparentPixels; virtual; abstract;
    364      procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;
    365      procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;
    366 
    367   public
    368      Caption:   string;  //user defined caption
    369 
    370      {-------------------font style------------------------}
    371      FontName: string;              //Specifies the font to use. Unless the font renderer accept otherwise,
    372                                     //the name is in human readable form, like 'Arial', 'Times New Roman', ...
    373 
    374      FontStyle: TFontStyles;        //Specifies the set of styles to be applied to the font.
    375                                     //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
    376                                     //So the value [fsBold,fsItalic] means that the font must be bold and italic.
    377 
    378      FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem.
    379 
    380      FontOrientation: integer;      //Specifies the rotation of the text, for functions that support text rotation.
    381                                     //It is expressed in tenth of degrees, positive values going counter-clockwise.
    382 
    383      //line style
    384      JoinStyle: TPenJoinStyle;
    385      JoinMiterLimit: single;
    386 
    387      FillMode:  TFillMode;  //winding or alternate
    388      LinearAntialiasing: boolean;
    389 
    390      { The resample filter is used when resizing the bitmap, and
    391        scan interpolation filter is used when the bitmap is used
    392        as a scanner (IBGRAScanner) }
    393      ResampleFilter,
    394      ScanInterpolationFilter: TResampleFilter;
    395      ScanOffset: TPoint;
    396 
    397      constructor Create; virtual; abstract; overload;
    398      constructor Create(ABitmap: TBitmap); virtual; abstract; overload;
    399      constructor Create(AWidth, AHeight: integer; Color: TColor); virtual; abstract; overload;
    400      constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); virtual; abstract; overload;
    401      constructor Create(AFilename: string); virtual; abstract; overload;
    402      constructor Create(AFilename: string; AIsUtf8Filename: boolean); virtual; abstract; overload;
    403      constructor Create(AStream: TStream); virtual; abstract; overload;
    404 
    405      function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; virtual; abstract; overload;
    406      function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; overload;
    407      function NewBitmap(Filename: string): TBGRACustomBitmap; virtual; abstract; overload;
    408      function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; virtual; abstract; overload;
    409 
    410      //there are UTF8 functions that are different from standard function as those
    411      //depend on TFPCustomImage that does not clearly handle UTF8
    412      procedure LoadFromFile(const filename: string); virtual;
    413      procedure LoadFromFileUTF8(const filenameUTF8: string); virtual;
    414      procedure LoadFromFileUTF8(const filenameUTF8: string; AHandler: TFPCustomImageReader); virtual;
    415      procedure LoadFromStream(Str: TStream); virtual; overload;
    416      procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader); virtual; overload;
    417      procedure SaveToFile(const filename: string); virtual; overload;
    418      procedure SaveToFile(const filename: string; Handler:TFPCustomImageWriter); virtual; overload;
    419      procedure SaveToFileUTF8(const filenameUTF8: string); virtual; overload;
    420      procedure SaveToFileUTF8(const filenameUTF8: string; Handler:TFPCustomImageWriter); virtual; overload;
    421      procedure SaveToStreamAsPng(Str: TStream); virtual; abstract;
    422      procedure SaveToStreamAs(Str: TStream; AFormat: TBGRAImageFormat); virtual;
    423      procedure Assign(ARaster: TRasterImage); virtual; abstract; overload;
    424      procedure Assign(MemBitmap: TBGRACustomBitmap); virtual; abstract; overload;
    425      procedure Serialize(AStream: TStream); virtual; abstract;
    426      procedure Deserialize(AStream: TStream); virtual; abstract;
    427 
    428      {Pixel functions}
    429      procedure SetPixel(x, y: int32or64; c: TColor); virtual; abstract; overload;
    430      procedure XorPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    431      procedure SetPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    432      procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    433      procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
    434      procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
    435      procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract;
    436      procedure ErasePixel(x, y: int32or64; alpha: byte); virtual; abstract;
    437      procedure AlphaPixel(x, y: int32or64; alpha: byte); virtual; abstract;
    438      function GetPixel(x, y: int32or64): TBGRAPixel; virtual; abstract; overload;
    439      function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract;
    440      function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract; overload;
    441      function GetPixelCycle(x, y: int32or64): TBGRAPixel; virtual; overload;
    442      function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    443      function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;
    444      function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
    445      function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;
    446 
    447      {Line primitives}
    448      procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    449      procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    450      procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; overload;
    451      procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
    452      procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload;
    453      procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
    454      procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); virtual; abstract;
    455      procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    456      procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    457      procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    458      procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); virtual; abstract;
    459      procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
    460      procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; maxDiff: byte); virtual; abstract;
    461      procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
    462      procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode);
    463      procedure HorizLine(x,y,x2: Int32or64; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
    464 
    465      {Shapes}
    466      procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); virtual; abstract;
    467      procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); virtual; abstract;
    468 
    469      procedure ArrowStartAsNone;
    470      procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
    471      procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
    472      procedure ArrowStartAsTail;
    473 
    474      procedure ArrowEndAsNone;
    475      procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
    476      procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
    477      procedure ArrowEndAsTail;
    478 
    479      procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode= dmDrawWithTransparency); virtual; abstract;
    480      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract; overload;
    481      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; abstract; overload;
    482      procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); virtual; abstract; overload;
    483      procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); virtual; abstract; overload;
    484      procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    485      procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
    486      procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); virtual; abstract; overload;
    487 
    488      procedure DrawPolyLine(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode=dmDrawWithTransparency);
    489      procedure DrawPolyLineAntialias(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean); virtual; overload;
    490      procedure DrawPolyLineAntialias(const points: array of TPoint; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; overload;
    491      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
    492      procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    493      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
    494      procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;
    495      procedure DrawPolygon(const points: array of TPoint; c: TBGRAPixel; ADrawMode: TDrawMode=dmDrawWithTransparency);
    496      procedure DrawPolygonAntialias(const points: array of TPoint; c: TBGRAPixel); overload;
    497      procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
    498      procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    499      procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;
    500 
    501      procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract;
    502      procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract; overload;
    503      procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); virtual; abstract; overload;
    504      procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); virtual; abstract; overload;
    505      procedure ErasePolyLine(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
    506      procedure ErasePolyLineAntialias(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); overload;
    507      procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); virtual; abstract; overload;
    508      procedure ErasePolygonOutline(const points: array of TPoint; alpha: byte);
    509      procedure ErasePolygonOutlineAntialias(const points: array of TPoint; alpha: byte);
    510 
    511      procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); virtual; abstract;
    512      procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); virtual; abstract;
    513 
    514      procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
    515      procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
    516      procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    517      procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    518      procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); virtual; abstract; overload;
    519 
    520      procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
    521      procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
    522      procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True);  virtual; abstract; overload;
    523      procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); virtual; abstract; overload;
    524      procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    525      procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    526      procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;
    527      procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
    528      procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;
    529 
    530      procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel);  virtual; abstract; overload;
    531      procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); virtual; abstract; overload;
    532      procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); virtual; abstract; overload;
    533      procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload;
    534      procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); virtual; abstract; overload;
    535 
    536      procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
    537      procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
    538      procedure FillPolyAntialias(const points: array of TPointF; c: TBGRAPixel); virtual; abstract;
    539      procedure FillPolyAntialias(const points: array of TPointF; texture: IBGRAScanner); virtual; abstract;
    540      procedure ErasePoly(const points: array of TPointF; alpha: byte); virtual; abstract;
    541      procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); virtual; abstract;
    542 
    543      procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
    544      procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
    545      procedure FillShapeAntialias(shape: TBGRACustomFillInfo; c: TBGRAPixel); virtual; abstract;
    546      procedure FillShapeAntialias(shape: TBGRACustomFillInfo; texture: IBGRAScanner); virtual; abstract;
    547      procedure EraseShape(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;
    548      procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;
    549 
    550      procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); virtual; abstract;
    551      procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); virtual; abstract;
    552      procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract;
    553      procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); virtual; abstract;
    554      procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); virtual; abstract;
    555      procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); virtual; abstract;
    556      procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); virtual; abstract;
    557 
    558      procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    559      procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    560      procedure Rectangle(x, y, x2, y2: integer; c: TColor); virtual; overload;
    561      procedure Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
    562      procedure Rectangle(r: TRect; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual;overload;
    563      procedure Rectangle(r: TRect; c: TColor); virtual; overload;
    564      procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single); virtual; overload;
    565      procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract; overload;
    566      procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
    567 
    568      procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
    569      procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
    570      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
    571      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    572      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    573      procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
    574      procedure FillRoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;
    575      procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
    576      procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
    577      procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); virtual; abstract;
    578 
    579      procedure EllipseInRect(r: TRect; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
    580      procedure EllipseInRect(r: TRect; BorderColor,FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
    581      procedure FillEllipseInRect(r: TRect; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;
    582 
    583      procedure FillRect(r: TRect; c: TColor); virtual; overload;
    584      procedure FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
    585      procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode); virtual; overload;
    586      procedure FillRect(x, y, x2, y2: integer; c: TColor); virtual; overload;
    587      procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
    588      procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
    589      procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); virtual; abstract;
    590      procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); virtual; abstract;
    591      procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); virtual; abstract;
    592      procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); virtual; abstract;
    593 
    594      procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; overload;
    595      procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; overload;
    596      procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    597      procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    598      procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; overload;
    599      procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; overload;
    600      function TextSize(sUTF8: string): TSize; virtual; abstract;
    601 
    602      { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c or texture is used to fill the text.
    603        The value of FontOrientation is taken into account, so that the text may be rotated. }
    604      procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel); virtual; overload;
    605      procedure TextOut(x, y: single; sUTF8: string; c: TColor); virtual; overload;
    606      procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner); virtual; overload;
    607 
    608      { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    609        The position depends on the specified horizontal alignment halign and vertical alignement valign.
    610        The color c or texture is used to fill the text. No rotation is applied. }
    611      procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); virtual; overload;
    612      procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); virtual; overload;
    613 
    614      {Spline}
    615      function ComputeClosedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
    616      function ComputeOpenedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
    617      function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
    618      function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;
    619      function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
    620      function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;
    621 
    622      function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;
    623      function ComputeWidePolyline(const points: array of TPointF; w: single; Closed: boolean): ArrayOfTPointF; virtual; abstract;
    624      function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;
    625 
    626      function ComputeEllipse(x,y,rx,ry: single): ArrayOfTPointF; deprecated;
    627      function ComputeEllipse(x,y,rx,ry,w: single): ArrayOfTPointF; deprecated;
    628      function ComputeEllipseContour(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    629      function ComputeEllipseBorder(x,y,rx,ry,w: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    630      function ComputeArc65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    631      function ComputeArcRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    632      function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    633      function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    634      function ComputePie65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    635      function ComputePieRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
    636 
    637      {Filling}
    638      procedure FillTransparent; virtual;
    639      procedure NoClip; virtual; abstract;
    640      procedure ApplyGlobalOpacity(alpha: byte); virtual; abstract;
    641      procedure Fill(c: TColor); virtual; overload;
    642      procedure Fill(c: TBGRAPixel); virtual; overload;
    643      procedure Fill(texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
    644      procedure Fill(texture: IBGRAScanner); virtual; abstract; overload;
    645      procedure Fill(c: TBGRAPixel; start, Count: integer); virtual; abstract; overload;
    646      procedure DrawPixels(c: TBGRAPixel; start, Count: integer); virtual; abstract;
    647      procedure AlphaFill(alpha: byte); virtual; overload;
    648      procedure AlphaFill(alpha: byte; start, Count: integer); virtual; abstract; overload;
    649      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); virtual; overload;
    650      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); virtual; overload;
    651      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); virtual; abstract; overload;
    652      procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
    653      procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); virtual; abstract; overload;
    654      procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); virtual; abstract; overload;
    655      procedure ReplaceColor(before, after: TColor); virtual; abstract; overload;
    656      procedure ReplaceColor(before, after: TBGRAPixel); virtual; abstract; overload;
    657      procedure ReplaceTransparent(after: TBGRAPixel); virtual; abstract; overload;
    658      procedure FloodFill(X, Y: integer; Color: TBGRAPixel;
    659        mode: TFloodfillMode; Tolerance: byte = 0); virtual;
    660      procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel;
    661        mode: TFloodfillMode; Tolerance: byte = 0); virtual; abstract;
    662      procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel;
    663        gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    664        gammaColorCorrection: boolean = True; Sinus: Boolean=False); virtual; abstract;
    665      procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
    666        gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    667        Sinus: Boolean=False); virtual; abstract;
    668      function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
    669                 AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; virtual; abstract;
    670 
    671      {Canvas drawing functions}
    672      procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
    673        AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
    674      procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
    675        ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
    676      procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); virtual; abstract;
    677      procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); virtual; abstract;
    678      procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); virtual; abstract;
    679      procedure DrawPart(ARect: TRect; Canvas: TCanvas; x, y: integer; Opaque: boolean); virtual;
    680      function GetPart(ARect: TRect): TBGRACustomBitmap; virtual; abstract;
    681      function GetPtrBitmap(Top,Bottom: Integer): TBGRACustomBitmap; virtual; abstract;
    682      procedure InvalidateBitmap; virtual; abstract;         //call if you modify with Scanline
    683      procedure LoadFromBitmapIfNeeded; virtual; abstract;   //call to ensure that bitmap data is up to date
    684 
    685      {BGRA bitmap functions}
    686      procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
    687      procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
    688      procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
    689      procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
    690      procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
    691      procedure PutImagePart(x,y: integer; Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte = 255);
    692      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
    693      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte=255); overload;
    694      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); virtual; abstract; overload;
    695      procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
    696      function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap): TRect;
    697      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload;
    698      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false; ACorrectBlur: Boolean = false); overload;
    699      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload;
    700      procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload;
    701      procedure ComputeImageAngleAxes(x,y,w,h,angle: single; imageCenterX,imageCenterY: single; ARestoreOffsetAfterRotation: boolean;
    702        out Origin,HAxis,VAxis: TPointF);
    703      function GetImageAngleBounds(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; ARestoreOffsetAfterRotation: boolean = false): TRect;
    704      procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); virtual; abstract;
    705      procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255;
    706          ALinearBlend: boolean = false); virtual; abstract;
    707      function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; virtual; abstract;
    708      function Equals(comp: TBGRACustomBitmap): boolean; virtual; abstract;
    709      function Equals(comp: TBGRAPixel): boolean; virtual; abstract;
    710      function Resample(newWidth, newHeight: integer;
    711        mode: TResampleMode = rmFineResample): TBGRACustomBitmap; virtual; abstract;
    712      procedure VerticalFlip; virtual; overload;
    713      procedure VerticalFlip(ARect: TRect); virtual; abstract; overload;
    714      procedure HorizontalFlip; virtual; overload;
    715      procedure HorizontalFlip(ARect: TRect); virtual; abstract; overload;
    716      function RotateCW: TBGRACustomBitmap; virtual; abstract;
    717      function RotateCCW: TBGRACustomBitmap; virtual; abstract;
    718      procedure Negative; virtual; abstract;
    719      procedure NegativeRect(ABounds: TRect); virtual; abstract;
    720      procedure LinearNegative; virtual; abstract;
    721      procedure LinearNegativeRect(ABounds: TRect); virtual; abstract;
    722      procedure InplaceGrayscale; virtual; abstract;
    723      procedure InplaceGrayscale(ABounds: TRect); virtual; abstract;
    724      procedure ConvertToLinearRGB; virtual; abstract;
    725      procedure ConvertFromLinearRGB; virtual; abstract;
    726      procedure SwapRedBlue; virtual; abstract;
    727      procedure GrayscaleToAlpha; virtual; abstract;
    728      procedure AlphaToGrayscale; virtual; abstract;
    729      procedure ApplyMask(mask: TBGRACustomBitmap); overload;
    730      procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect); overload;
    731      procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); virtual; abstract; overload;
    732      function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; virtual; abstract;
    733      function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; virtual; abstract;
    734      function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; virtual; abstract;
    735      function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; virtual; abstract;
    736 
    737      {Filters}
    738      function FilterSmartZoom3(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
    739      function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
    740      function FilterSmooth: TBGRACustomBitmap; virtual; abstract;
    741      function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
    742      function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
    743      function FilterContour: TBGRACustomBitmap; virtual; abstract;
    744      function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract;
    745      function FilterBlurRadial(radius: integer;
    746        blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    747      function FilterBlurRadial(ABounds: TRect; radius: integer;
    748        blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
    749      function FilterBlurMotion(distance: integer; angle: single;
    750        oriented: boolean): TBGRACustomBitmap; virtual; abstract;
    751      function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
    752        oriented: boolean): TBGRACustomBitmap; virtual; abstract;
    753      function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
    754      function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
    755      function FilterEmboss(angle: single): TBGRACustomBitmap; virtual; abstract;
    756      function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    757      function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; virtual; abstract;
    758      function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; virtual; abstract;
    759      function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; virtual; abstract;
    760      function FilterGrayscale: TBGRACustomBitmap; virtual; abstract;
    761      function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
    762      function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
    763      function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
    764      function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; virtual; abstract;
    765      function FilterSphere: TBGRACustomBitmap; virtual; abstract;
    766      function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
    767      function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
    768      function FilterCylinder: TBGRACustomBitmap; virtual; abstract;
    769      function FilterPlane: TBGRACustomBitmap; virtual; abstract;
    770 
    771      property Width: integer Read GetWidth;        //width of the image in pixels
    772      property Height: integer Read GetHeight;      //height of the image in pixels
    773      property NbPixels: integer Read GetNbPixels;  //total number of pixels. It is always true that NbPixels = Width * Height
    774 
    775      property ScanLine[y: integer]: PBGRAPixel Read GetScanLine;   //Returns the address of the left-most pixel of any line.
    776                                                                    //The parameter y ranges from 0 to Height-1.
    777 
    778      property LineOrder: TRawImageLineOrder Read GetLineOrder;     //Indicates the order in which lines are stored in memory.
    779                                                                    //If it is equal to riloTopToBottom, the first line is the top line.
    780                                                                    //If it is equal to riloBottomToTop, the first line is the bottom line.
    781 
    782      property Data: PBGRAPixel Read GetDataPtr;  //Provides a pointer to the first pixel in memory.
    783                                                  //Depending on the LineOrder property, this can be the top-left pixel or the bottom-left pixel.
    784                                                  //There is no padding between scanlines, so the start of the next line is at the address Data + Width.
    785 
    786      property Empty: boolean Read CheckEmpty;    //Returns True if the bitmap only contains transparent pixels or has a size of zero.
    787 
    788      property HasTransparentPixels: boolean Read GetHasTransparentPixels; //Returns True if there are transparent or semitransparent pixels,
    789                                                                           //and so if the image would be stored with an alpha channel.
    790 
    791      property RefCount: integer Read GetRefCount;
    792      property Bitmap: TBitmap Read GetBitmap; //don't forget to call InvalidateBitmap before if you changed something with Scanline
    793      property AverageColor: TColor Read GetAverageColor;
    794      property AveragePixel: TBGRAPixel Read GetAveragePixel;
    795      property CanvasFP: TFPImageCanvas read GetCanvasFP;
    796      property CanvasDrawModeFP: TDrawMode read GetCanvasDrawModeFP write SetCanvasDrawModeFP;
    797      property Canvas: TCanvas Read GetCanvas;
    798      property CanvasOpacity: byte Read GetCanvasOpacity Write SetCanvasOpacity;
    799      property CanvasAlphaCorrection: boolean
    800        Read GetCanvasAlphaCorrection Write SetCanvasAlphaCorrection;
    801 
    802      property PenStyle: TPenStyle read GetPenStyle Write SetPenStyle;
    803      property CustomPenStyle: TBGRAPenStyle read GetCustomPenStyle write SetCustomPenStyle;
    804      property ClipRect: TRect read GetClipRect write SetClipRect;
    805 
    806      { Specifies the height of the font without taking into account additional line spacing.
    807        A negative value means that it is the full height instead (see below). }
    808      property FontHeight: integer Read GetFontHeight Write SetFontHeight;
    809 
    810      { Specifies the height of the font, taking into account the additional line spacing defined for the font. }
    811      property FontFullHeight: integer read GetFontFullHeight write SetFontFullHeight;
    812 
    813      property FontAntialias: Boolean read GetFontAntialias write SetFontAntialias;    //Simplified property to specify the quality.
    814      property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;   //Returns measurement for the current font in pixels.
    815 
    816      { Specifies the font renderer. By default it is an instance of TLCLFontRenderer of unit BGRAText.
    817        Other renderers are provided in BGRATextFX unit and BGRAVectorize unit.
    818        Once you assign a renderer, it will automatically be freed.
    819        The renderers may provide additional styling for the font. }
    820      property FontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;
    821 
    822      property LineCap: TPenEndCap read GetLineCap write SetLineCap;
    823      property ArrowStartSize: TPointF read GetArrowStartSize write SetArrowStartSize;
    824      property ArrowEndSize: TPointF read GetArrowEndSize write SetArrowEndSize;
    825      property ArrowStartOffset: single read GetArrowStartOffset write SetArrowStartOffset;
    826      property ArrowEndOffset: single read GetArrowEndOffset write SetArrowEndOffset;
    827      property ArrowStartRepeat: integer read GetArrowStartRepeat write SetArrowStartRepeat;
    828      property ArrowEndRepeat: integer read GetArrowEndRepeat write SetArrowEndRepeat;
    829 
    830      //IBGRAScanner
    831      function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual; abstract;
    832      procedure ScanMoveTo(X,Y: Integer); virtual; abstract;
    833      function ScanNextPixel: TBGRAPixel; virtual; abstract;
    834      function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
    835      procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    836      function IsScanPutPixelsDefined: boolean; virtual;
    837 
    838   protected
    839      //interface
    840      function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    841      function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    842      function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    843 
    844   end;
    845 
    846   { TBGRACustomScanner }
    847 
    848   TBGRACustomScanner = class(IBGRAScanner)
    849   private
    850     FCurX,FCurY: integer;
    851   public
    852     function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual;
    853     procedure ScanMoveTo(X,Y: Integer); virtual;
    854     function ScanNextPixel: TBGRAPixel; virtual;
    855     function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
    856     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    857     function IsScanPutPixelsDefined: boolean; virtual;
    858   protected
    859     function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    860     function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    861     function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    862   end;
    863 
    864   { TBGRACustomGradient }
    865 
    866   TBGRACustomGradient = class
    867   public
    868     function GetColorAt(position: integer): TBGRAPixel; virtual; abstract;
    869     function GetColorAtF(position: single): TBGRAPixel; virtual;
    870     function GetAverageColor: TBGRAPixel; virtual; abstract;
    871     function GetMonochrome: boolean; virtual; abstract;
    872     property Monochrome: boolean read GetMonochrome;
    873   end;
    874 
    875   { TIntersectionInfo }
    876 
    877   TIntersectionInfo = class
    878     interX: single;
    879     winding: integer;
    880     numSegment: integer;
    881     procedure SetValues(AInterX: Single; AWinding, ANumSegment: integer);
    882   end;
    883   ArrayOfTIntersectionInfo = array of TIntersectionInfo;
    884 
    885   TBGRACustomFillInfo = class
    886     public
    887       //returns true if the same segment number can be curved
    888       function SegmentsCurved: boolean; virtual; abstract;
    889 
    890       //returns integer bounds
    891       function GetBounds: TRect; virtual; abstract;
    892 
    893       //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if
    894       //there is nothing to draw
    895       function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; virtual; abstract;
    896 
    897       //check if the point is inside the filling zone
    898       function IsPointInside(x,y: single; windingMode: boolean): boolean; virtual; abstract;
    899 
    900       //create an array that will contain computed intersections.
    901       //you may augment, in this case, use CreateIntersectionInfo for new items
    902       function CreateIntersectionArray: ArrayOfTIntersectionInfo; virtual; abstract;
    903       function CreateIntersectionInfo: TIntersectionInfo; virtual; abstract; //creates a single info
    904       procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo); virtual; abstract;
    905 
    906       //fill a previously created array of intersections with actual intersections at the current y coordinate.
    907       //nbInter gets the number of computed intersections
    908       procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); virtual; abstract;
    909   end;
    910 
    911   { TBGRACustomFontRenderer }
    912 
    913   TBGRACustomFontRenderer = class
    914     FontName: string;              //Specifies the font to use. Unless the font renderer accept otherwise,
    915                                    //the name is in human readable form, like 'Arial', 'Times New Roman', ...
    916 
    917     FontStyle: TFontStyles;        //Specifies the set of styles to be applied to the font.
    918                                    //These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
    919                                    //So the value [fsBold,fsItalic] means that the font must be bold and italic.
    920 
    921     FontQuality : TBGRAFontQuality;//Specifies the quality of rendering. Default value is fqSystem.
    922 
    923     FontOrientation: integer;      //Specifies the rotation of the text, for functions that support text rotation.
    924                                    //It is expressed in tenth of degrees, positive values going counter-clockwise.
    925 
    926     FontEmHeight: integer;         // Specifies the height of the font without taking into account additional line spacing.
    927                                    // A negative value means that it is the full height instead.
    928 
    929     { Returns measurement for the current font in pixels. }
    930     function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
    931 
    932     { Returns the total size of the string provided using the current font.
    933       Orientation is not taken into account, so that the width is along the text.  }
    934     function TextSize(sUTF8: string): TSize; virtual; abstract;
    935 
    936     { Draws the UTF8 encoded string, with color c.
    937       If align is taLeftJustify, (x,y) is the top-left corner.
    938       If align is taCenter, (x,y) is at the top and middle of the text.
    939       If align is taRightJustify, (x,y) is the top-right corner.
    940       The value of FontOrientation is taken into account, so that the text may be rotated. }
    941     procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    942 
    943     { Same as above functions, except that the text is filled using texture.
    944       The value of FontOrientation is taken into account, so that the text may be rotated. }
    945     procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    946 
    947     { Same as above, except that the orientation is specified, overriding the value of the property FontOrientation. }
    948     procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
    949     procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
    950 
    951     { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect.
    952       Additional style information is provided by the style parameter.
    953       The color c or texture is used to fill the text. No rotation is applied. }
    954     procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract;
    955     procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract;
    956 
    957     { Copy the path for the UTF8 encoded string into ADest.
    958       If align is taLeftJustify, (x,y) is the top-left corner.
    959       If align is taCenter, (x,y) is at the top and middle of the text.
    960       If align is taRightJustify, (x,y) is the top-right corner. }
    961     procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional
    962   end;
    963 
    964 type
    965   TBGRABitmapAny = class of TBGRACustomBitmap;  //used to create instances of the same type (see NewBitmap)
    966   TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
    967 
    968 var
    969   BGRABitmapFactory : TBGRABitmapAny;
    970   BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
    971 
    972 function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean; inline;
    973 
    974 { Color functions }
    975 function GetIntensity(const c: TExpandedPixel): word; inline;
    976 function GetIntensity(c: TBGRAPixel): word; inline;
    977 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
    978 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
    979 function GetLightness(c: TBGRAPixel): word;
    980 function GetLightness(const c: TExpandedPixel): word; inline;
    981 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
    982 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
    983 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel; //if you already know the current lightness of the color
    984 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel; inline;
    985 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
    986 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    987 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;
    988 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
    989 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
    990 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
    991 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
    992 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    993 function GtoH(ghue: word): word;
    994 function HtoG(hue: word): word;
    995 function HueDiff(h1, h2: word): word;
    996 function GetHue(ec: TExpandedPixel): word;
    997 function ColorImportance(ec: TExpandedPixel): word;
    998 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
    999 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
    1000 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    1001 function GammaExpansion(c: TBGRAPixel): TExpandedPixel; inline;
    1002 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel; inline;
    1003 function GammaCompression(red,green,blue,alpha: word): TBGRAPixel; inline;
    1004 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
    1005 function GrayscaleToBGRA(lightness: word): TBGRAPixel;
    1006 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel; overload;
    1007 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel; weight2: byte): TBGRAPixel;
    1008 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel; overload;
    1009 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel; weight2: integer): TBGRAPixel; overload;
    1010 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel; overload;
    1011 function BGRA(red, green, blue, alpha: byte): TBGRAPixel; overload; inline;
    1012 function BGRA(red, green, blue: byte): TBGRAPixel; overload; inline;
    1013 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel; overload; inline;
    1014 function HSLA(hue, saturation, lightness: word): THSLAPixel; overload; inline;
    1015 function ColorToBGRA(color: TColor): TBGRAPixel; overload;
    1016 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
    1017 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
    1018 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
    1019 function BGRAToColor(c: TBGRAPixel): TColor;
    1020 operator = (const c1, c2: TBGRAPixel): boolean; inline;
    1021 function ExpandedDiff(ec1, ec2: TExpandedPixel): word;
    1022 function BGRAWordDiff(c1, c2: TBGRAPixel): word;
    1023 function BGRADiff(c1, c2: TBGRAPixel): byte;
    1024 operator - (const c1, c2: TColorF): TColorF; inline;
    1025 operator + (const c1, c2: TColorF): TColorF; inline;
    1026 operator * (const c1, c2: TColorF): TColorF; inline;
    1027 operator * (const c1: TColorF; factor: single): TColorF; inline;
    1028 function ColorF(red,green,blue,alpha: single): TColorF;
    1029 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
    1030 function StrToBGRA(str: string): TBGRAPixel; //full parse
    1031 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel; //full parse with default when error or missing values
    1032 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel; //partial parse allowed
    1033 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    1034 
    1035 { Get height [0..1] stored in a TBGRAPixel }
    1036 function MapHeight(Color: TBGRAPixel): Single;
    1037 
    1038 { Get TBGRAPixel to store height [0..1] }
    1039 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
    1040 
    1041 
    1042 { Gamma conversion arrays. Should be used as readonly }
    1043 var
    1044   // TBGRAPixel -> TExpandedPixel
    1045   GammaExpansionTab:   packed array[0..255] of word;
    1046  
    1047   // TExpandedPixel -> TBGRAPixel
    1048   GammaCompressionTab: packed array[0..65535] of byte;
    1049 
    1050 { Point functions }
    1051 function PointF(x, y: single): TPointF;
    1052 function PointsF(const pts: array of TPointF): ArrayOfTPointF;
    1053 operator = (const pt1, pt2: TPointF): boolean; inline;
    1054 operator - (const pt1, pt2: TPointF): TPointF; inline;
    1055 operator - (const pt2: TPointF): TPointF; inline;
    1056 operator + (const pt1, pt2: TPointF): TPointF; inline;
    1057 operator * (const pt1, pt2: TPointF): single; inline; //scalar product
    1058 operator * (const pt1: TPointF; factor: single): TPointF; inline;
    1059 operator * (factor: single; const pt1: TPointF): TPointF; inline;
    1060 function PtInRect(const pt: TPoint; r: TRect): boolean; overload;
    1061 function RectWithSize(left,top,width,height: integer): TRect;
    1062 function VectLen(dx,dy: single): single; overload;
    1063 function VectLen(v: TPointF): single; overload;
    1064 
    1065 { Line and polygon functions }
    1066 type
    1067     TLineDef = record
    1068        origin, dir: TPointF;
    1069     end;
    1070 
    1071 function IntersectLine(line1, line2: TLineDef): TPointF;
    1072 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;
    1073 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean;
    1074 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    1075 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    1076 
    1077 { Cyclic functions }
    1078 function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
    1079 
    1080 { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
    1081   They use a table to store already computed values. The return value is an integer
    1082   ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is
    1083   32768 instead of 1. The input has a period of 65536, so you can supply any integer
    1084   without applying a modulo. }
    1085 procedure PrecalcSin65536; // compute all values now
    1086 function Sin65536(value: word): Int32or64; inline;
    1087 function Cos65536(value: word): Int32or64; inline;
    1088 function ByteSqrt(value: byte): byte; inline;
    1089 
    1090 function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
    1091 function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
    1092 function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
    1093 function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
    1094 function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     441  {** Detect the file format of a given file }
     442  function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
     443  {** Detect the file format of a given stream. ''ASuggestedExtensionUTF8'' can
     444      be provided to guess the format }
     445  function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
     446  {** Returns the file format that is most likely to be stored in the
     447      given filename (according to its extension) }
     448  function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
     449  {** Returns a likely image extension for the format }
     450  function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
     451  {** Create an image reader for the given format }
     452  function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
     453  {** Create an image writer for the given format. ''AHasTransparentPixels''
     454      specifies if alpha channel must be supported }
     455  function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
     456
     457{$DEFINE INCLUDE_INTERFACE}
     458{$I bgracustombitmap.inc}
    1095459
    1096460implementation
    1097461
    1098 uses Math, SysUtils, FileUtil, lazutf8classes, LCLProc,
     462uses Math, SysUtils, BGRAUTF8,
    1099463  FPReadTiff, FPReadXwd, FPReadXPM,
    1100   FPWriteTiff, FPWriteJPEG, FPWritePNG, FPWriteBMP, FPWritePCX,
     464  FPWriteTiff, FPWriteJPEG, BGRAWritePNG, FPWriteBMP, FPWritePCX,
    1101465  FPWriteTGA, FPWriteXPM;
     466
     467{$DEFINE INCLUDE_IMPLEMENTATION}
     468{$I geometrytypes.inc}
     469
     470{$DEFINE INCLUDE_IMPLEMENTATION}
     471{$I csscolorconst.inc}
     472
     473{$DEFINE INCLUDE_IMPLEMENTATION}
     474{$I bgracustombitmap.inc}
     475
     476{$DEFINE INCLUDE_IMPLEMENTATION}
     477{$I bgrapixel.inc}
     478
     479function CleanTextOutString(s: string): string;
     480var idxIn, idxOut: integer;
     481begin
     482  setlength(result, length(s));
     483  idxIn := 1;
     484  idxOut := 1;
     485  while IdxIn <= length(s) do
     486  begin
     487    if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8
     488    begin
     489      result[idxOut] := s[idxIn];
     490      inc(idxOut);
     491    end;
     492    inc(idxIn);
     493  end;
     494  setlength(result, idxOut-1);
     495end;
     496
     497function RemoveLineEnding(var s: string; indexByte: integer): boolean;
     498begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long
     499      //so this function can be applied to UTF8 strings as well
     500  result := false;
     501  if length(s) >= indexByte then
     502  begin
     503    if s[indexByte] in[#13,#10] then
     504    begin
     505      result := true;
     506      if length(s) >= indexByte+1 then
     507      begin
     508        if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then
     509          delete(s,indexByte,2)
     510        else
     511          delete(s,indexByte,1);
     512      end
     513        else
     514          delete(s,indexByte,1);
     515    end;
     516  end;
     517end;
     518
     519function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
     520var indexByte: integer;
     521    pIndex: PChar;
     522begin
     523  pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8);
     524  if pIndex = nil then
     525  begin
     526    result := false;
     527    exit;
     528  end;
     529  indexByte := pIndex - @sUTF8[1];
     530  result := RemoveLineEnding(sUTF8, indexByte);
     531end;
     532
     533procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
     534var p: integer;
     535begin
     536  if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then
     537  begin
     538    p := length(ABefore);
     539    while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);
     540    if p > 1 then //can put the word after
     541    begin
     542      AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
     543      ABefore := copy(ABefore,1,p-1);
     544    end else
     545    begin //cannot put the word after, so before
     546
     547    end;
     548  end;
     549  while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);
     550  while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);
     551end;
     552
    1102553
    1103554function StrToResampleFilter(str: string): TResampleFilter;
     
    1114565end;
    1115566
    1116 function StrToBlendOperation(str: string): TBlendOperation;
    1117 var op: TBlendOperation;
    1118 begin
    1119   result := boTransparent;
    1120   str := LowerCase(str);
    1121   for op := low(TBlendOperation) to high(TBlendOperation) do
    1122     if str = LowerCase(BlendOperationStr[op]) then
    1123     begin
    1124       result := op;
    1125       exit;
    1126     end;
    1127 end;
    1128 
    1129 function StrToGradientType(str: string): TGradientType;
    1130 var gt: TGradientType;
    1131 begin
    1132   result := gtLinear;
    1133   str := LowerCase(str);
    1134   for gt := low(TGradientType) to high(TGradientType) do
    1135     if str = LowerCase(GradientTypeStr[gt]) then
    1136     begin
    1137       result := gt;
    1138       exit;
    1139     end;
    1140 end;
    1141 
    1142 { Make a pen style. Need an even number of values. See TBGRAPenStyle }
    1143 function BGRAPenStyle(dash1, space1: single; dash2: single; space2: single;
    1144   dash3: single; space3: single; dash4: single; space4: single): TBGRAPenStyle;
    1145 var
    1146   i: Integer;
    1147 begin
    1148   if dash4 <> 0 then
    1149   begin
    1150     setlength(result,8);
    1151     result[6] := dash4;
    1152     result[7] := space4;
    1153     result[4] := dash3;
    1154     result[5] := space3;
    1155     result[2] := dash2;
    1156     result[3] := space2;
    1157   end else
    1158   if dash3 <> 0 then
    1159   begin
    1160     setlength(result,6);
    1161     result[4] := dash3;
    1162     result[5] := space3;
    1163     result[2] := dash2;
    1164     result[3] := space2;
    1165   end else
    1166   if dash2 <> 0 then
    1167   begin
    1168     setlength(result,4);
    1169     result[2] := dash2;
    1170     result[3] := space2;
    1171   end else
    1172   begin
    1173     setlength(result,2);
    1174   end;
    1175   result[0] := dash1;
    1176   result[1] := space1;
    1177   for i := 0 to high(result) do
    1178     if result[i]=0 then
    1179       raise exception.Create('Zero is not a valid value');
    1180 end;
    1181 
    1182 { Bézier curves definitions. See : http://en.wikipedia.org/wiki/B%C3%A9zier_curve }
    1183 
    1184 function ConcatPointsF(const APolylines: array of ArrayOfTPointF
    1185   ): ArrayOfTPointF;
    1186 var
    1187   i,pos,count:integer;
    1188   j: Integer;
    1189 begin
    1190   count := 0;
    1191   for i := 0 to high(APolylines) do
    1192     inc(count,length(APolylines[i]));
    1193   setlength(result,count);
    1194   pos := 0;
    1195   for i := 0 to high(APolylines) do
    1196     for j := 0 to high(APolylines[i]) do
    1197     begin
    1198       result[pos] := APolylines[i][j];
    1199       inc(pos);
    1200     end;
    1201 end;
    1202 
    1203 operator-(const v: TPoint3D): TPoint3D;
    1204 begin
    1205   result.x := -v.x;
    1206   result.y := -v.y;
    1207   result.z := -v.z;
    1208 end;
    1209 
    1210 operator + (const v1,v2: TPoint3D): TPoint3D; inline;
    1211 begin
    1212   result.x := v1.x+v2.x;
    1213   result.y := v1.y+v2.y;
    1214   result.z := v1.z+v2.z;
    1215 end;
    1216 
    1217 operator - (const v1,v2: TPoint3D): TPoint3D; inline;
    1218 begin
    1219   result.x := v1.x-v2.x;
    1220   result.y := v1.y-v2.y;
    1221   result.z := v1.z-v2.z;
    1222 end;
    1223 
    1224 operator * (const v1: TPoint3D; const factor: single): TPoint3D; inline;
    1225 begin
    1226   result.x := v1.x*factor;
    1227   result.y := v1.y*factor;
    1228   result.z := v1.z*factor;
    1229 end;
    1230 
    1231 function Point3D(x, y, z: single): TPoint3D;
    1232 begin
    1233   result.x := x;
    1234   result.y := y;
    1235   result.z := z;
    1236 end;
    1237 
    1238 operator=(const v1, v2: TPoint3D): boolean;
    1239 begin
    1240   result := (v1.x=v2.x) and (v1.y=v2.y) and (v1.z=v2.z);
    1241 end;
    1242 
    1243 operator * (const v1,v2: TPoint3D): single; inline;
    1244 begin
    1245   result := v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
    1246 end;
    1247 
    1248 procedure Normalize3D(var v: TPoint3D); inline;
    1249 var len: double;
    1250 begin
    1251   len := v*v;
    1252   if len = 0 then exit;
    1253   len := sqrt(len);
    1254   v.x /= len;
    1255   v.y /= len;
    1256   v.z /= len;
    1257 end;
    1258 
    1259 procedure VectProduct3D(u,v: TPoint3D; out w: TPoint3D);
    1260 begin
    1261   w.x := u.y*v.z-u.z*v.y;
    1262   w.y := u.z*v.x-u.x*v.z;
    1263   w.z := u.x*v.Y-u.y*v.x;
    1264 end;
    1265 
    1266 // Define a Bézier curve with two control points.
    1267 function BezierCurve(origin, control1, control2, destination: TPointF): TCubicBezierCurve;
    1268 begin
    1269   result.p1 := origin;
    1270   result.c1 := control1;
    1271   result.c2 := control2;
    1272   result.p2 := destination;
    1273 end;
    1274 
    1275 // Define a Bézier curve with one control point.
    1276 function BezierCurve(origin, control, destination: TPointF
    1277   ): TQuadraticBezierCurve;
    1278 begin
    1279   result.p1 := origin;
    1280   result.c := control;
    1281   result.p2 := destination;
    1282 end;
    1283 
    1284 //straight line
    1285 function BezierCurve(origin, destination: TPointF): TQuadraticBezierCurve;
    1286 begin
    1287   result.p1 := origin;
    1288   result.c := (origin+destination)*0.5;
    1289   result.p2 := destination;
    1290 end;
    1291 
    1292 function ArcDef(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single;
    1293   anticlockwise: boolean): TArcDef;
    1294 begin
    1295   result.center := PointF(cx,cy);
    1296   result.radius := PointF(rx,ry);
    1297   result.xAngleRadCW:= xAngleRadCW;
    1298   result.startAngleRadCW := startAngleRadCW;
    1299   result.endAngleRadCW:= endAngleRadCW;
    1300   result.anticlockwise:= anticlockwise;
    1301 end;
    1302 
    1303 { Check if a PointF structure is empty or should be treated as a list separator }
    1304 function isEmptyPointF(pt: TPointF): boolean;
    1305 begin
    1306   Result := (pt.x = EmptySingle) and (pt.y = EmptySingle);
    1307 end;
    1308 
    1309567{ TBGRACustomFontRenderer }
    1310568
    1311569procedure TBGRACustomFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
    1312 begin
    1313 end;
    1314 
    1315 { TIntersectionInfo }
    1316 
    1317 procedure TIntersectionInfo.SetValues(AInterX: Single; AWinding,
    1318   ANumSegment: integer);
    1319 begin
    1320   interX := AInterX;
    1321   winding := AWinding;
    1322   numSegment := ANumSegment;
    1323 end;
    1324 
    1325 { TBGRACustomGradient }
    1326 
    1327 function TBGRACustomGradient.GetColorAtF(position: single): TBGRAPixel;
    1328 begin
    1329   position *= 65536;
    1330   if position < low(integer) then
    1331     result := GetColorAt(low(Integer))
    1332   else if position > high(integer) then
    1333     result := GetColorAt(high(Integer))
    1334   else
    1335     result := GetColorAt(round(position));
    1336 end;
    1337 
    1338 { TBGRAColorList }
    1339 
    1340 function TBGRAColorList.GetByIndex(Index: integer): TBGRAPixel;
    1341 begin
    1342   if (Index < 0) or (Index >= FNbColors) then
    1343     result := BGRAPixelTransparent
    1344   else
    1345     result := FColors[Index].Color;
    1346 end;
    1347 
    1348 function TBGRAColorList.GetByName(Name: string): TBGRAPixel;
    1349 var i: integer;
    1350 begin
    1351   i := IndexOf(Name);
    1352   if i = -1 then
    1353     result := BGRAPixelTransparent
    1354   else
    1355     result := FColors[i].Color;
    1356 end;
    1357 
    1358 function TBGRAColorList.GetName(Index: integer): string;
    1359 begin
    1360   if (Index < 0) or (Index >= FNbColors) then
    1361     result := ''
    1362   else
    1363     result := FColors[Index].Name;
    1364 end;
    1365 
    1366 constructor TBGRAColorList.Create;
    1367 begin
    1368   FNbColors:= 0;
    1369   FColors := nil;
    1370   FFinished:= false;
    1371 end;
    1372 
    1373 procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel);
    1374 begin
    1375   if FFinished then
    1376     raise Exception.Create('This list is already finished');
    1377   if length(FColors) = FNbColors then
    1378     SetLength(FColors, FNbColors*2+1);
    1379   FColors[FNbColors].Name := Name;
    1380   FColors[FNbColors].Color := Color;
    1381   inc(FNbColors);
    1382 end;
    1383 
    1384 procedure TBGRAColorList.Finished;
    1385 begin
    1386   if FFinished then exit;
    1387   FFinished := true;
    1388   SetLength(FColors, FNbColors);
    1389 end;
    1390 
    1391 function TBGRAColorList.IndexOf(Name: string): integer;
    1392 var i: integer;
    1393 begin
    1394   for i := 0 to FNbColors-1 do
    1395     if CompareText(Name, FColors[i].Name) = 0 then
    1396     begin
    1397       result := i;
    1398       exit;
    1399     end;
    1400   result := -1;
    1401 end;
    1402 
    1403 function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
    1404 var i: integer;
    1405   MinDiff,CurDiff: Word;
    1406 begin
    1407   if AMaxDiff = 0 then
    1408   begin
    1409     for i := 0 to FNbColors-1 do
    1410       if AColor = FColors[i].Color then
    1411       begin
    1412         result := i;
    1413         exit;
    1414       end;
    1415     result := -1;
    1416   end else
    1417   begin
    1418     MinDiff := AMaxDiff;
    1419     result := -1;
    1420     for i := 0 to FNbColors-1 do
    1421     begin
    1422       CurDiff := BGRAWordDiff(AColor,FColors[i].Color);
    1423       if CurDiff <= MinDiff then
    1424       begin
    1425         result := i;
    1426         MinDiff := CurDiff;
    1427         if MinDiff = 0 then exit;
    1428       end;
    1429     end;
    1430   end;
    1431 end;
    1432 
    1433 { TBGRACustomBitmap }
    1434 
    1435 function TBGRACustomBitmap.GetFontAntialias: Boolean;
    1436 begin
    1437   result := FontQuality <> fqSystem;
    1438 end;
    1439 
    1440 procedure TBGRACustomBitmap.SetFontAntialias(const AValue: Boolean);
    1441 begin
    1442   if AValue and not FontAntialias then
    1443     FontQuality := fqFineAntialiasing
    1444   else if not AValue and (FontQuality <> fqSystem) then
    1445     FontQuality := fqSystem;
    1446 end;
    1447 
    1448 { These declaration make sure that these methods are virtual }
    1449 procedure TBGRACustomBitmap.LoadFromFile(const filename: string);
    1450 begin
    1451   LoadFromFileUTF8(SysToUtf8(filename));
    1452 end;
    1453 
    1454 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string);
    1455 var
    1456   Stream: TStream;
    1457   format: TBGRAImageFormat;
    1458   reader: TFPCustomImageReader;
    1459 begin
    1460   stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
    1461   try
    1462     format := DetectFileFormat(Stream, ExtractFileExt(filenameUTF8));
    1463     reader := CreateBGRAImageReader(format);
    1464     try
    1465       LoadFromStream(stream, reader);
    1466     finally
    1467       reader.Free;
    1468     end;
    1469   finally
    1470     ClearTransparentPixels;
    1471     stream.Free;
    1472   end;
    1473 end;
    1474 
    1475 procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string;
    1476   AHandler: TFPCustomImageReader);
    1477 var
    1478   Stream: TStream;
    1479 begin
    1480   stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
    1481   try
    1482     LoadFromStream(stream, AHandler);
    1483   finally
    1484     ClearTransparentPixels;
    1485     stream.Free;
    1486   end;
    1487 end;
    1488 
    1489 procedure TBGRACustomBitmap.SaveToFile(const filename: string);
    1490 begin
    1491   SaveToFileUTF8(SysToUtf8(filename));
    1492 end;
    1493 
    1494 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string);
    1495 var
    1496   writer: TFPCustomImageWriter;
    1497   format: TBGRAImageFormat;
    1498 begin
    1499   format := SuggestImageFormat(filenameUTF8);
    1500   writer := CreateBGRAImageWriter(Format, HasTransparentPixels);
    1501   try
    1502     SaveToFileUTF8(filenameUTF8, writer);
    1503   finally
    1504     writer.free;
    1505   end;
    1506 end;
    1507 
    1508 procedure TBGRACustomBitmap.SaveToFile(const filename: string;
    1509   Handler: TFPCustomImageWriter);
    1510 begin
    1511   SaveToFileUTF8(SysToUtf8(filename),Handler);
    1512 end;
    1513 
    1514 procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string;
    1515   Handler: TFPCustomImageWriter);
    1516 var
    1517   stream: TFileStreamUTF8;
    1518 begin
    1519    stream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
    1520    try
    1521      SaveToStream(stream, Handler);
    1522    finally
    1523      stream.Free;
    1524    end;
    1525 end;
    1526 
    1527 procedure TBGRACustomBitmap.SaveToStreamAs(Str: TStream;
    1528   AFormat: TBGRAImageFormat);
    1529 var handler: TFPCustomImageWriter;
    1530 begin
    1531   handler := CreateBGRAImageWriter(AFormat, HasTransparentPixels);
    1532   try
    1533     SaveToStream(Str, handler)
    1534   finally
    1535     handler.Free;
    1536   end;
    1537 end;
    1538 
    1539 procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel;
    1540   ADrawMode: TDrawMode);
    1541 begin
    1542   case ADrawMode of
    1543   dmSet: SetPixel(x,y,c);
    1544   dmSetExceptTransparent: if c.alpha = 255 then SetPixel(x,y,c);
    1545   dmLinearBlend: FastBlendPixel(x,y,c);
    1546   dmDrawWithTransparency: DrawPixel(x,y,c);
    1547   dmXor: XorPixel(x,y,c);
    1548   end;
    1549 end;
    1550 
    1551 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream);
    1552 var
    1553   format: TBGRAImageFormat;
    1554   reader: TFPCustomImageReader;
    1555 begin
    1556   format := DetectFileFormat(Str);
    1557   reader := CreateBGRAImageReader(format);
    1558   try
    1559     LoadFromStream(Str,reader);
    1560   finally
    1561     reader.Free;
    1562   end;
    1563 end;
    1564 
    1565 { LoadFromStream uses TFPCustomImage routine, which uses
    1566   Colors property to access pixels. That's why the
    1567   FP drawing mode is temporarily changed to load
    1568   bitmaps properly }
    1569 procedure TBGRACustomBitmap.LoadFromStream(Str: TStream;
    1570   Handler: TFPCustomImageReader);
    1571 var
    1572   OldDrawMode: TDrawMode;
    1573 begin
    1574   OldDrawMode := CanvasDrawModeFP;
    1575   CanvasDrawModeFP := dmSet;
    1576   try
    1577     inherited LoadFromStream(Str, Handler);
    1578   finally
    1579     CanvasDrawModeFP := OldDrawMode;
    1580   end;
    1581 end;
    1582 
    1583 { Look for a pixel considering the bitmap is repeated in both directions }
    1584 function TBGRACustomBitmap.GetPixelCycle(x, y: int32or64): TBGRAPixel;
    1585 begin
    1586   if (Width = 0) or (Height = 0) then
    1587     Result := BGRAPixelTransparent
    1588   else
    1589     Result := (Scanline[PositiveMod(y,Height)] + PositiveMod(x,Width))^;
    1590 end;
    1591 
    1592 procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64;
    1593   texture: IBGRAScanner);
    1594 begin
    1595   HorizLine(x,y,x2,texture,dmDrawWithTransparency);
    1596 end;
    1597 
    1598 procedure TBGRACustomBitmap.HorizLine(x, y, x2: Int32or64; c: TBGRAPixel;
    1599   ADrawMode: TDrawMode);
    1600 begin
    1601   case ADrawMode of
    1602     dmSet: SetHorizLine(x,y,x2,c);
    1603     dmSetExceptTransparent: if c.alpha = 255 then SetHorizLine(x,y,x2,c);
    1604     dmXor: XorHorizLine(x,y,x2,c);
    1605     dmLinearBlend: FastBlendHorizLine(x,y,x2,c);
    1606     dmDrawWithTransparency: DrawHorizLine(x,y,x2,c);
    1607   end;
    1608 end;
    1609 
    1610 procedure TBGRACustomBitmap.VertLine(x, y, y2: Int32or64; c: TBGRAPixel;
    1611   ADrawMode: TDrawMode);
    1612 begin
    1613   case ADrawMode of
    1614     dmSet: SetVertLine(x,y,y2,c);
    1615     dmSetExceptTransparent: if c.alpha = 255 then SetVertLine(x,y,y2,c);
    1616     dmXor: XorVertLine(x,y,y2,c);
    1617     dmLinearBlend: FastBlendVertLine(x,y,y2,c);
    1618     dmDrawWithTransparency: DrawVertLine(x,y,y2,c);
    1619   end;
    1620 end;
    1621 
    1622 procedure TBGRACustomBitmap.ArrowStartAsNone;
    1623 begin
    1624   SetArrowStart(asNone);
    1625 end;
    1626 
    1627 procedure TBGRACustomBitmap.ArrowStartAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
    1628 var join: TPenJoinStyle;
    1629 begin
    1630   if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
    1631   if ACut then
    1632   begin
    1633     if AFlipped then
    1634       SetArrowStart(asFlippedCut,join,ARelativePenWidth)
    1635     else
    1636       SetArrowStart(asCut,join,ARelativePenWidth)
    1637   end
    1638   else
    1639   begin
    1640     if AFlipped then
    1641       SetArrowStart(asFlipped,join,ARelativePenWidth)
    1642     else
    1643       SetArrowStart(asNormal,join,ARelativePenWidth)
    1644   end;
    1645 end;
    1646 
    1647 procedure TBGRACustomBitmap.ArrowStartAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
    1648   AHollowPenWidth: single);
    1649 var join: TPenJoinStyle;
    1650 begin
    1651   if ARounded then join := pjsRound else join := pjsMiter;
    1652   if AHollow then
    1653     SetArrowStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
    1654   else
    1655     SetArrowStart(asTriangle, join,1,ABackOffset);
    1656 end;
    1657 
    1658 procedure TBGRACustomBitmap.ArrowStartAsTail;
    1659 begin
    1660   SetArrowStart(asTail);
    1661 end;
    1662 
    1663 procedure TBGRACustomBitmap.ArrowEndAsNone;
    1664 begin
    1665   SetArrowEnd(asNone);
    1666 end;
    1667 
    1668 procedure TBGRACustomBitmap.ArrowEndAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
    1669 var join: TPenJoinStyle;
    1670 begin
    1671   if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
    1672   if ACut then
    1673   begin
    1674     if AFlipped then
    1675       SetArrowEnd(asFlippedCut,join,ARelativePenWidth)
    1676     else
    1677       SetArrowEnd(asCut,join,ARelativePenWidth)
    1678   end
    1679   else
    1680   begin
    1681     if AFlipped then
    1682       SetArrowEnd(asFlipped,join,ARelativePenWidth)
    1683     else
    1684       SetArrowEnd(asNormal,join,ARelativePenWidth)
    1685   end;
    1686 end;
    1687 
    1688 procedure TBGRACustomBitmap.ArrowEndAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
    1689   AHollowPenWidth: single);
    1690 var join: TPenJoinStyle;
    1691 begin
    1692   if ARounded then join := pjsRound else join := pjsMiter;
    1693   if AHollow then
    1694     SetArrowEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
    1695   else
    1696     SetArrowEnd(asTriangle, join,1, ABackOffset);
    1697 end;
    1698 
    1699 procedure TBGRACustomBitmap.ArrowEndAsTail;
    1700 begin
    1701   SetArrowEnd(asTail);
    1702 end;
    1703 
    1704 procedure TBGRACustomBitmap.DrawPolyLine(const points: array of TPoint;
    1705   c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
    1706 var i: integer;
    1707 begin
    1708    if length(points) = 1 then
    1709    begin
    1710      if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c,ADrawMode);
    1711    end
    1712    else
    1713      for i := 0 to high(points)-1 do
    1714        DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1),ADrawMode);
    1715 end;
    1716 
    1717 { Pixel polylines are constructed by concatenation }
    1718 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint;
    1719   c: TBGRAPixel; DrawLastPixel: boolean);
    1720 var i: integer;
    1721 begin
    1722    if length(points) = 1 then
    1723    begin
    1724      if DrawLastPixel then DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
    1725    end
    1726    else
    1727      for i := 0 to high(points)-1 do
    1728        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1));
    1729 end;
    1730 
    1731 procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint; c1,
    1732   c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean);
    1733 var i: integer;
    1734   DashPos: integer;
    1735 begin
    1736    DashPos := 0;
    1737    if length(points) = 1 then
    1738    begin
    1739      if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c1);
    1740    end
    1741    else
    1742      for i := 0 to high(points)-1 do
    1743        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c1,c2,dashLen,DrawLastPixel and (i=high(points)-1),DashPos);
    1744 end;
    1745 
    1746 procedure TBGRACustomBitmap.DrawPolygon(const points: array of TPoint;
    1747   c: TBGRAPixel; ADrawMode: TDrawMode);
    1748 var i: integer;
    1749 begin
    1750    if length(points) = 1 then
    1751    begin
    1752      DrawPixel(points[0].x,points[0].y,c,ADrawMode);
    1753    end
    1754    else
    1755    begin
    1756      for i := 0 to high(points)-1 do
    1757        DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false,ADrawMode);
    1758      DrawLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false,ADrawMode);
    1759    end;
    1760 end;
    1761 
    1762 procedure TBGRACustomBitmap.DrawPolygonAntialias(const points: array of TPoint;
    1763   c: TBGRAPixel);
    1764 var i: integer;
    1765 begin
    1766    if length(points) = 1 then
    1767    begin
    1768      DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
    1769    end
    1770    else
    1771    begin
    1772      for i := 0 to high(points)-1 do
    1773        DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false);
    1774      DrawLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false);
    1775    end;
    1776 end;
    1777 
    1778 procedure TBGRACustomBitmap.ErasePolyLine(const points: array of TPoint; alpha: byte;
    1779   DrawLastPixel: boolean);
    1780 var i: integer;
    1781 begin
    1782    if length(points) = 1 then
    1783    begin
    1784      if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
    1785    end
    1786    else
    1787      for i := 0 to high(points)-1 do
    1788        EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
    1789 end;
    1790 
    1791 procedure TBGRACustomBitmap.ErasePolyLineAntialias(
    1792   const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
    1793 var i: integer;
    1794 begin
    1795    if length(points) = 1 then
    1796    begin
    1797      if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
    1798    end
    1799    else
    1800      for i := 0 to high(points)-1 do
    1801        EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
    1802 end;
    1803 
    1804 procedure TBGRACustomBitmap.ErasePolygonOutline(const points: array of TPoint;
    1805   alpha: byte);
    1806 var i: integer;
    1807 begin
    1808    if length(points) = 1 then
    1809    begin
    1810      ErasePixel(points[0].x,points[0].y,alpha);
    1811    end
    1812    else
    1813    begin
    1814      for i := 0 to high(points)-1 do
    1815        EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
    1816      EraseLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
    1817    end;
    1818 end;
    1819 
    1820 procedure TBGRACustomBitmap.ErasePolygonOutlineAntialias(
    1821   const points: array of TPoint; alpha: byte);
    1822 var i: integer;
    1823 begin
    1824    if length(points) = 1 then
    1825    begin
    1826      ErasePixel(points[0].x,points[0].y,alpha);
    1827    end
    1828    else
    1829    begin
    1830      for i := 0 to high(points)-1 do
    1831        EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
    1832      EraseLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
    1833    end;
    1834 end;
    1835 
    1836 { Following functions are defined for convenience }
    1837 procedure TBGRACustomBitmap.Rectangle(x, y, x2, y2: integer; c: TColor);
    1838 begin
    1839   Rectangle(x, y, x2, y2, ColorToBGRA(c), dmSet);
    1840 end;
    1841 
    1842 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode
    1843   );
    1844 begin
    1845   Rectangle(r.left, r.top, r.right, r.bottom, c, mode);
    1846 end;
    1847 
    1848 procedure TBGRACustomBitmap.Rectangle(r: TRect; BorderColor,
    1849   FillColor: TBGRAPixel; mode: TDrawMode);
    1850 begin
    1851   Rectangle(r.left, r.top, r.right, r.bottom, BorderColor, FillColor, mode);
    1852 end;
    1853 
    1854 procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TColor);
    1855 begin
    1856   Rectangle(r.left, r.top, r.right, r.bottom, c);
    1857 end;
    1858 
    1859 procedure TBGRACustomBitmap.RectangleAntialias(x, y, x2, y2: single;
    1860   c: TBGRAPixel; w: single);
    1861 begin
    1862   RectangleAntialias(x, y, x2, y2, c, w, BGRAPixelTransparent);
    1863 end;
    1864 
    1865 procedure TBGRACustomBitmap.FillRoundRect(X1, Y1, X2, Y2: integer; DX,
    1866   DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode);
    1867 begin
    1868   RoundRect(X1,Y1,X2,Y2,DX,DY,FillColor,FillColor,ADrawMode);
    1869 end;
    1870 
    1871 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor: TBGRAPixel;
    1872   ADrawMode: TDrawMode);
    1873 begin
    1874   RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,ADrawMode);
    1875 end;
    1876 
    1877 procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor,
    1878   FillColor: TBGRAPixel; ADrawMode: TDrawMode);
    1879 begin
    1880   RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,FillColor,ADrawMode);
    1881 end;
    1882 
    1883 procedure TBGRACustomBitmap.FillEllipseInRect(r: TRect; FillColor: TBGRAPixel;
    1884   ADrawMode: TDrawMode);
    1885 begin
    1886   FillRoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),FillColor,ADrawMode);
    1887 end;
    1888 
    1889 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TColor);
    1890 begin
    1891   FillRect(r.Left, r.top, r.right, r.bottom, c);
    1892 end;
    1893 
    1894 procedure TBGRACustomBitmap.FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode);
    1895 begin
    1896   FillRect(r.Left, r.top, r.right, r.bottom, c, mode);
    1897 end;
    1898 
    1899 procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
    1900   mode: TDrawMode);
    1901 begin
    1902   FillRect(r.Left, r.top, r.right, r.bottom, texture, mode);
    1903 end;
    1904 
    1905 procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer; c: TColor);
    1906 begin
    1907   FillRect(x, y, x2, y2, ColorToBGRA(c), dmSet);
    1908 end;
    1909 
    1910 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
    1911   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1912 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TBGRAPixel);
    1913 begin
    1914   TextOut(x, y, sUTF8, c, taLeftJustify);
    1915 end;
    1916 
    1917 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
    1918   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1919 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TColor);
    1920 begin
    1921   TextOut(x, y, sUTF8, ColorToBGRA(c));
    1922 end;
    1923 
    1924 { Draw the UTF8 encoded string, (x,y) being the top-left corner. The texture is used to fill the text.
    1925   The value of FontOrientation is taken into account, so that the text may be rotated. }
    1926 procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string;
    1927   texture: IBGRAScanner);
    1928 begin
    1929   TextOut(x, y, sUTF8, texture, taLeftJustify);
    1930 end;
    1931 
    1932 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    1933   The position depends on the specified horizontal alignment halign and vertical alignement valign.
    1934   The color c is used to fill the text. No rotation is applied. }
    1935 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    1936   halign: TAlignment; valign: TTextLayout; c: TBGRAPixel);
    1937 var
    1938   style: TTextStyle;
    1939 begin
    1940   {$hints off}
    1941   FillChar(style,sizeof(style),0);
    1942   {$hints on}
    1943   style.Alignment := halign;
    1944   style.Layout := valign;
    1945   style.Wordbreak := true;
    1946   style.ShowPrefix := false;
    1947   style.Clipping := false;
    1948   TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,c);
    1949 end;
    1950 
    1951 { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
    1952   The position depends on the specified horizontal alignment halign and vertical alignement valign.
    1953   The texture is used to fill the text. No rotation is applied. }
    1954 procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
    1955   halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner);
    1956 var
    1957   style: TTextStyle;
    1958 begin
    1959   {$hints off}
    1960   FillChar(style,sizeof(style),0);
    1961   {$hints on}
    1962   style.Alignment := halign;
    1963   style.Layout := valign;
    1964   style.Wordbreak := true;
    1965   style.ShowPrefix := false;
    1966   style.Clipping := false;
    1967   TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,texture);
    1968 end;
    1969 
    1970 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry: single): ArrayOfTPointF;
    1971 begin
    1972   result := ComputeEllipseContour(x,y,rx,ry);
    1973 end;
    1974 
    1975 function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry, w: single
    1976   ): ArrayOfTPointF;
    1977 begin
    1978   result := ComputeEllipseBorder(x,y,rx,ry,w);
    1979 end;
    1980 
    1981 procedure TBGRACustomBitmap.FillTransparent;
    1982 begin
    1983   Fill(BGRAPixelTransparent);
    1984 end;
    1985 
    1986 procedure TBGRACustomBitmap.Fill(c: TColor);
    1987 begin
    1988   Fill(ColorToBGRA(c));
    1989 end;
    1990 
    1991 procedure TBGRACustomBitmap.Fill(c: TBGRAPixel);
    1992 begin
    1993   Fill(c, 0, NbPixels);
    1994 end;
    1995 
    1996 procedure TBGRACustomBitmap.AlphaFill(alpha: byte);
    1997 begin
    1998   AlphaFill(alpha, 0, NbPixels);
    1999 end;
    2000 
    2001 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    2002   color: TBGRAPixel);
    2003 begin
    2004   FillMask(x,y, AMask, color, dmDrawWithTransparency);
    2005 end;
    2006 
    2007 procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    2008   texture: IBGRAScanner);
    2009 begin
    2010   FillMask(x,y, AMask, texture, dmDrawWithTransparency);
    2011 end;
    2012 
    2013 procedure TBGRACustomBitmap.FloodFill(X, Y: integer; Color: TBGRAPixel;
    2014   mode: TFloodfillMode; Tolerance: byte);
    2015 begin
    2016   ParallelFloodFill(X,Y,Self,Color,mode,Tolerance);
    2017 end;
    2018 
    2019 procedure TBGRACustomBitmap.DrawPart(ARect: TRect; Canvas: TCanvas; x,
    2020   y: integer; Opaque: boolean);
    2021 var
    2022   partial: TBGRACustomBitmap;
    2023 begin
    2024   partial := GetPart(ARect);
    2025   if partial <> nil then
    2026   begin
    2027     partial.Draw(Canvas, x, y, Opaque);
    2028     partial.Free;
    2029   end;
    2030 end;
    2031 
    2032 procedure TBGRACustomBitmap.PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
    2033 begin
    2034   PutImageAngle(x,y,source,0);
    2035 end;
    2036 
    2037 procedure TBGRACustomBitmap.PutImagePart(x, y: integer;
    2038   Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte);
    2039 var w,h,sourcex,sourcey,nx,ny,xb,yb,destx,desty: integer;
    2040     oldClip,newClip: TRect;
    2041 begin
    2042   if (Source = nil) or (AOpacity = 0) then exit;
    2043   w := SourceRect.Right-SourceRect.Left;
    2044   h := SourceRect.Bottom-SourceRect.Top;
    2045   if (w <= 0) or (h <= 0) or (Source.Width = 0) or (Source.Height = 0) then exit;
    2046   sourcex := PositiveMod(SourceRect.Left, Source.Width);
    2047   sourcey := PositiveMod(SourceRect.Top, Source.Height);
    2048   nx := (sourceX+w + Source.Width-1) div Source.Width;
    2049   ny := (sourceY+h + Source.Height-1) div Source.Height;
    2050 
    2051   oldClip := ClipRect;
    2052   newClip := rect(x,y,x+w,y+h);
    2053   if not IntersectRect(newClip,newClip,oldClip) then exit;
    2054 
    2055   ClipRect := newClip;
    2056 
    2057   desty := y-sourcey;
    2058   for yb := 0 to ny-1 do
    2059   begin
    2060     destx := x-sourcex;
    2061     for xb := 0 to nx-1 do
    2062     begin
    2063       self.PutImage(destx,desty,Source,mode,AOpacity);
    2064       inc(destx,Source.Width);
    2065     end;
    2066     inc(desty,Source.Height);
    2067   end;
    2068 
    2069   ClipRect := oldClip;
    2070 end;
    2071 
    2072 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2073   Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean);
    2074 begin
    2075   if ACorrectBlur then
    2076     PutImageAffine(Origin,HAxis,VAxis,Source,rfCosine,AOpacity)
    2077   else
    2078     PutImageAffine(Origin,HAxis,VAxis,Source,rfLinear,AOpacity);
    2079 end;
    2080 
    2081 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2082   Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte);
    2083 var outputBounds: TRect;
    2084 begin
    2085   if (Source = nil) or (AOpacity = 0) then exit;
    2086   if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    2087      (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    2088      (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    2089   begin
    2090     PutImage(round(origin.x),round(origin.y),Source,dmDrawWithTransparency,AOpacity);
    2091     exit;
    2092   end;
    2093   outputBounds := GetImageAffineBounds(Origin,HAxis,VAxis,Source);
    2094   PutImageAffine(Origin,HAxis,VAxis,Source,outputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
    2095 end;
    2096 
    2097 procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    2098   Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte;
    2099   ACorrectBlur: Boolean);
    2100 begin
    2101   if ACorrectBlur then
    2102     PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfCosine,dmDrawWithTransparency, AOpacity)
    2103   else
    2104     PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfLinear,dmDrawWithTransparency,AOpacity);
    2105 end;
    2106 
    2107 { Returns the area that contains the affine transformed image }
    2108 function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
    2109   Source: TBGRACustomBitmap): TRect;
    2110 var minx,miny,maxx,maxy: integer;
    2111     vx,vy,pt1: TPointF;
    2112     sourceBounds: TRect;
    2113 
    2114   //include specified point in the bounds
    2115   procedure Include(pt: TPointF);
    2116   begin
    2117     if floor(pt.X) < minx then minx := floor(pt.X);
    2118     if floor(pt.Y) < miny then miny := floor(pt.Y);
    2119     if ceil(pt.X) > maxx then maxx := ceil(pt.X);
    2120     if ceil(pt.Y) > maxy then maxy := ceil(pt.Y);
    2121   end;
    2122 
    2123 begin
    2124   result := EmptyRect;
    2125   if (Source = nil) then exit;
    2126   sourceBounds := source.GetImageBounds;
    2127   if IsRectEmpty(sourceBounds) then exit;
    2128 
    2129   if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    2130      (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    2131      (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    2132   begin
    2133     result := sourceBounds;
    2134     OffsetRect(result,round(origin.x),round(origin.y));
    2135     IntersectRect(result,result,ClipRect);
    2136     exit;
    2137   end;
    2138 
    2139   { Compute bounds }
    2140   vx := (HAxis-Origin)*(1/source.Width);
    2141   vy := (VAxis-Origin)*(1/source.Height);
    2142   pt1 := Origin+vx*sourceBounds.Left+vy*sourceBounds.Top;
    2143   minx := floor(pt1.X);
    2144   miny := floor(pt1.Y);
    2145   maxx := ceil(pt1.X);
    2146   maxy := ceil(pt1.Y);
    2147   Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Top);
    2148   Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Bottom);
    2149   Include(Origin+vx*sourceBounds.Left+vy*sourceBounds.Bottom);
    2150 
    2151   result := rect(minx,miny,maxx+1,maxy+1);
    2152   IntersectRect(result,result,ClipRect);
    2153 end;
    2154 
    2155 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2156   Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
    2157   imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2158   ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
    2159 begin
    2160   if ACorrectBlur then
    2161     PutImageAngle(x,y,Source,angle,AOutputBounds,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
    2162   else
    2163     PutImageAngle(x,y,Source,angle,AOutputBounds,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
    2164 end;
    2165 
    2166 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2167   Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
    2168   imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
    2169 begin
    2170   if ACorrectBlur then
    2171     PutImageAngle(x,y,Source,angle,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
    2172   else
    2173     PutImageAngle(x,y,Source,angle,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
    2174 end;
    2175 
    2176 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2177   Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
    2178   AResampleFilter: TResampleFilter; imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2179   ARestoreOffsetAfterRotation: boolean);
    2180 var
    2181   Origin,HAxis,VAxis: TPointF;
    2182 begin
    2183   if (source = nil) or (AOpacity=0) then exit;
    2184   ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
    2185      Origin,HAxis,VAxis);
    2186   PutImageAffine(Origin,HAxis,VAxis,source,AOutputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
    2187 end;
    2188 
    2189 procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
    2190   Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter;
    2191   imageCenterX: single; imageCenterY: single; AOpacity: Byte;
    2192   ARestoreOffsetAfterRotation: boolean);
    2193 var
    2194   Origin,HAxis,VAxis: TPointF;
    2195 begin
    2196   if (source = nil) or (AOpacity=0) then exit;
    2197   ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
    2198      Origin,HAxis,VAxis);
    2199   PutImageAffine(Origin,HAxis,VAxis,source,AResampleFilter,AOpacity);
    2200 end;
    2201 
    2202 procedure TBGRACustomBitmap.ComputeImageAngleAxes(x, y, w, h,
    2203   angle: single; imageCenterX, imageCenterY: single;
    2204   ARestoreOffsetAfterRotation: boolean; out Origin, HAxis, VAxis: TPointF);
    2205 var
    2206   cosa,sina: single;
    2207 
    2208   { Compute rotated coordinates }
    2209   function Coord(relX,relY: single): TPointF;
    2210   begin
    2211     relX -= imageCenterX;
    2212     relY -= imageCenterY;
    2213     result.x := relX*cosa-relY*sina+x;
    2214     result.y := relY*cosa+relX*sina+y;
    2215     if ARestoreOffsetAfterRotation then
    2216     begin
    2217       result.x += imageCenterX;
    2218       result.y += imageCenterY;
    2219     end;
    2220   end;
    2221 
    2222 begin
    2223   cosa := cos(-angle*Pi/180);
    2224   sina := -sin(-angle*Pi/180);
    2225   Origin := Coord(0,0);
    2226   HAxis := Coord(w,0);
    2227   VAxis := Coord(0,h);
    2228 end;
    2229 
    2230 function TBGRACustomBitmap.GetImageAngleBounds(x, y: single;
    2231   Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
    2232   imageCenterY: single; ARestoreOffsetAfterRotation: boolean): TRect;
    2233 var
    2234   cosa,sina: single;
    2235 
    2236   { Compute rotated coordinates }
    2237   function Coord(relX,relY: single): TPointF;
    2238   begin
    2239     relX -= imageCenterX;
    2240     relY -= imageCenterY;
    2241     result.x := relX*cosa-relY*sina+x;
    2242     result.y := relY*cosa+relX*sina+y;
    2243     if ARestoreOffsetAfterRotation then
    2244     begin
    2245       result.x += imageCenterX;
    2246       result.y += imageCenterY;
    2247     end;
    2248   end;
    2249 
    2250 begin
    2251   if (source = nil) then
    2252   begin
    2253     result := EmptyRect;
    2254     exit;
    2255   end;
    2256   cosa := cos(-angle*Pi/180);
    2257   sina := -sin(-angle*Pi/180);
    2258   result := GetImageAffineBounds(Coord(0,0),Coord(source.Width,0),Coord(0,source.Height),source);
    2259 end;
    2260 
    2261 procedure TBGRACustomBitmap.VerticalFlip;
    2262 begin
    2263   VerticalFlip(rect(0,0,Width,Height));
    2264 end;
    2265 
    2266 procedure TBGRACustomBitmap.HorizontalFlip;
    2267 begin
    2268   HorizontalFlip(rect(0,0,Width,Height));
    2269 end;
    2270 
    2271 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap);
    2272 begin
    2273   ApplyMask(mask, Rect(0,0,Width,Height), Point(0,0));
    2274 end;
    2275 
    2276 procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect);
    2277 begin
    2278   ApplyMask(mask, ARect, ARect.TopLeft);
    2279 end;
    2280 
    2281 { Interface gateway }
    2282 function TBGRACustomBitmap.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2283 begin
    2284   if GetInterface(iid, obj) then
    2285     Result := S_OK
    2286   else
    2287     Result := longint(E_NOINTERFACE);
    2288 end;
    2289 
    2290 { There is no automatic reference counting, but it is compulsory to define these functions }
    2291 function TBGRACustomBitmap._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2292 begin
    2293   result := 0;
    2294 end;
    2295 
    2296 function TBGRACustomBitmap._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2297 begin
    2298   result := 0;
    2299 end;
    2300 
    2301 {$hints off}
    2302 procedure TBGRACustomBitmap.ScanPutPixels(pdest: PBGRAPixel; count: integer;
    2303   mode: TDrawMode);
    2304 begin
    2305   //do nothing
    2306 end;
    2307 {$hints on}
    2308 
    2309 function TBGRACustomBitmap.IsScanPutPixelsDefined: boolean;
    2310 begin
    2311   result := False;
    2312 end;
    2313 
    2314 {********************** End of TBGRACustomBitmap **************************}
    2315 
    2316 { TBGRACustomScanner }
    2317 { The abstract class record the position so that a derived class
    2318   need only to redefine ScanAt }
    2319 
    2320 function TBGRACustomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel;
    2321 begin
    2322   result := ScanAt(X,Y);
    2323 end;
    2324 
    2325 procedure TBGRACustomScanner.ScanMoveTo(X, Y: Integer);
    2326 begin
    2327   FCurX := X;
    2328   FCurY := Y;
    2329 end;
    2330 
    2331 { Call ScanAt to determine pixel value }
    2332 function TBGRACustomScanner.ScanNextPixel: TBGRAPixel;
    2333 begin
    2334   result := ScanAt(FCurX,FCurY);
    2335   Inc(FCurX);
    2336 end;
    2337 
    2338 {$hints off}
    2339 procedure TBGRACustomScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
    2340   mode: TDrawMode);
    2341 begin
    2342   //do nothing
    2343 end;
    2344 {$hints on}
    2345 
    2346 function TBGRACustomScanner.IsScanPutPixelsDefined: boolean;
    2347 begin
    2348   result := false;
    2349 end;
    2350 
    2351 { Interface gateway }
    2352 function TBGRACustomScanner.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2353 begin
    2354   if GetInterface(iid, obj) then
    2355     Result := S_OK
    2356   else
    2357     Result := longint(E_NOINTERFACE);
    2358 end;
    2359 
    2360 { There is no automatic reference counting, but it is compulsory to define these functions }
    2361 function TBGRACustomScanner._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2362 begin
    2363   result := 0;
    2364 end;
    2365 
    2366 function TBGRACustomScanner._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    2367 begin
    2368   result := 0;
    2369 end;
    2370 
    2371 {********************** End of TBGRACustomScanner **************************}
    2372 
    2373 { The gamma correction is approximated here by a power function }
    2374 const
    2375   GammaExpFactor   = 1.7; //exponent
    2376   redWeightShl10   = 306; // = 0.299
    2377   greenWeightShl10 = 601; // = 0.587
    2378   blueWeightShl10  = 117; // = 0.114
    2379 
    2380 var
    2381   GammaLinearFactor: single;
    2382 
    2383 procedure InitGamma;
    2384 var
    2385   i: integer;
    2386 {$IFDEF WINCE}
    2387   j,prevpos,curpos,midpos: integer;
    2388 {$ENDIF}
    2389 begin
    2390   //the linear factor is used to normalize expanded values in the range 0..65535
    2391   GammaLinearFactor := 65535 / power(255, GammaExpFactor);
    2392 
    2393 {$IFDEF WINCE}
    2394   curpos := 0;
    2395   GammaExpansionTab[0] := 0;
    2396   GammaCompressionTab[0] := 0;
    2397   for i := 0 to 255 do
    2398   begin
    2399     prevpos := curpos;
    2400     curpos := round(power(i, GammaExpFactor) * GammaLinearFactor);
    2401     if i = 1 then curpos := 1; //to avoid information loss
    2402     GammaExpansionTab[i] := curpos;
    2403     midpos := (prevpos+1+curpos) div 2;
    2404     for j := prevpos+1 to midpos-1 do
    2405       GammaCompressionTab[j] := i-1;
    2406     for j := midpos to curpos do
    2407       GammaCompressionTab[j] := i;
    2408   end;
    2409 {$ELSE}
    2410   for i := 0 to 255 do
    2411     GammaExpansionTab[i] := round(power(i, GammaExpFactor) * GammaLinearFactor);
    2412 
    2413   for i := 0 to 65535 do
    2414     GammaCompressionTab[i] := round(power(i / GammaLinearFactor, 1 / GammaExpFactor));
    2415 
    2416   GammaExpansionTab[1]   := 1; //to avoid information loss
    2417   GammaCompressionTab[1] := 1;
    2418 {$ENDIF}
    2419 end;
    2420 
    2421 {************************** Color functions **************************}
     570begin {optional implementation} end;
     571
    2422572
    2423573function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb,
     
    2460610
    2461611  result := true;
    2462 end;
    2463 
    2464 { The intensity is defined here as the maximum value of any color component }
    2465 function GetIntensity(const c: TExpandedPixel): word; inline;
    2466 begin
    2467   Result := c.red;
    2468   if c.green > Result then
    2469     Result := c.green;
    2470   if c.blue > Result then
    2471     Result := c.blue;
    2472 end;
    2473 
    2474 function GetIntensity(c: TBGRAPixel): word;
    2475 begin
    2476   Result := c.red;
    2477   if c.green > Result then
    2478     Result := c.green;
    2479   if c.blue > Result then
    2480     Result := c.blue;
    2481   result := GammaExpansionTab[Result];
    2482 end;
    2483 
    2484 function SetIntensity(const c: TExpandedPixel; intensity: word): TExpandedPixel;
    2485 var
    2486   curIntensity: word;
    2487 begin
    2488   curIntensity := GetIntensity(c);
    2489   if curIntensity = 0 then //suppose it's gray if there is no color information
    2490   begin
    2491     Result.red := intensity;
    2492     Result.green := intensity;
    2493     Result.blue := intensity;
    2494     result.alpha := c.alpha;
    2495   end
    2496   else
    2497   begin
    2498     //linear interpolation to reached wanted intensity
    2499     Result.red   := (c.red * intensity + (curIntensity shr 1)) div curIntensity;
    2500     Result.green := (c.green * intensity + (curIntensity shr 1)) div curIntensity;
    2501     Result.blue  := (c.blue * intensity + (curIntensity shr 1)) div curIntensity;
    2502     Result.alpha := c.alpha;
    2503   end;
    2504 end;
    2505 
    2506 function SetIntensity(c: TBGRAPixel; intensity: word): TBGRAPixel;
    2507 begin
    2508   result := GammaCompression(SetIntensity(GammaExpansion(c),intensity));
    2509 end;
    2510 
    2511 function GetLightness(c: TBGRAPixel): word;
    2512 begin
    2513   result := GetLightness(GammaExpansion(c));
    2514 end;
    2515 
    2516 { The lightness here is defined as the subjective sensation of luminosity, where
    2517   blue is the darkest component and green the lightest }
    2518 function GetLightness(const c: TExpandedPixel): word; inline;
    2519 begin
    2520   Result := (c.red * redWeightShl10 + c.green * greenWeightShl10 +
    2521     c.blue * blueWeightShl10 + 512) shr 10;
    2522 end;
    2523 
    2524 function SetLightness(const c: TExpandedPixel; lightness: word): TExpandedPixel;
    2525 var
    2526   curLightness: word;
    2527 begin
    2528   curLightness := GetLightness(c);
    2529   if lightness = curLightness then
    2530   begin //no change
    2531     Result := c;
    2532     exit;
    2533   end;
    2534   result := SetLightness(c, lightness, curLightness);
    2535 end;
    2536 
    2537 function SetLightness(c: TBGRAPixel; lightness: word): TBGRAPixel;
    2538 begin
    2539   result := GammaCompression(SetLightness(GammaExpansion(c),lightness));
    2540 end;
    2541 
    2542 function SetLightness(const c: TExpandedPixel; lightness: word; curLightness: word): TExpandedPixel;
    2543 var
    2544   AddedWhiteness, maxBeforeWhite: word;
    2545   clip: boolean;
    2546 begin
    2547   if lightness = curLightness then
    2548   begin //no change
    2549     Result := c;
    2550     exit;
    2551   end;
    2552   if lightness = 65535 then //set to white
    2553   begin
    2554     Result.red   := 65535;
    2555     Result.green := 65535;
    2556     Result.blue  := 65535;
    2557     Result.alpha := c.alpha;
    2558     exit;
    2559   end;
    2560   if lightness = 0 then  //set to black
    2561   begin
    2562     Result.red   := 0;
    2563     Result.green := 0;
    2564     Result.blue  := 0;
    2565     Result.alpha := c.alpha;
    2566     exit;
    2567   end;
    2568   if curLightness = 0 then  //set from black
    2569   begin
    2570     Result.red   := lightness;
    2571     Result.green := lightness;
    2572     Result.blue  := lightness;
    2573     Result.alpha := c.alpha;
    2574     exit;
    2575   end;
    2576   if lightness < curLightness then //darker is easy
    2577   begin
    2578     result.alpha:= c.alpha;
    2579     result.red := (c.red * lightness + (curLightness shr 1)) div curLightness;
    2580     result.green := (c.green * lightness + (curLightness shr 1)) div curLightness;
    2581     result.blue := (c.blue * lightness + (curLightness shr 1)) div curLightness;
    2582     exit;
    2583   end;
    2584   //lighter and grayer
    2585   Result := c;
    2586   AddedWhiteness := lightness - curLightness;
    2587   maxBeforeWhite := 65535 - AddedWhiteness;
    2588   clip   := False;
    2589   if Result.red <= maxBeforeWhite then
    2590     Inc(Result.red, AddedWhiteness)
    2591   else
    2592   begin
    2593     Result.red := 65535;
    2594     clip := True;
    2595   end;
    2596   if Result.green <= maxBeforeWhite then
    2597     Inc(Result.green, AddedWhiteness)
    2598   else
    2599   begin
    2600     Result.green := 65535;
    2601     clip := True;
    2602   end;
    2603   if Result.blue <= maxBeforeWhite then
    2604     Inc(Result.blue, AddedWhiteness)
    2605   else
    2606   begin
    2607     Result.blue := 65535;
    2608     clip := True;
    2609   end;
    2610 
    2611   if clip then //light and whiter
    2612   begin
    2613     curLightness   := GetLightness(Result);
    2614     addedWhiteness := lightness - curLightness;
    2615     maxBeforeWhite := 65535 - curlightness;
    2616     Result.red     := Result.red + addedWhiteness * (65535 - Result.red) div
    2617       maxBeforeWhite;
    2618     Result.green   := Result.green + addedWhiteness * (65535 - Result.green) div
    2619       maxBeforeWhite;
    2620     Result.blue    := Result.blue + addedWhiteness * (65535 - Result.blue) div
    2621       maxBeforeWhite;
    2622   end;
    2623 end;
    2624 
    2625 function ApplyLightnessFast(color: TBGRAPixel; lightness: word): TBGRAPixel;
    2626 var
    2627   r,g,b: word;
    2628   lightness256: byte;
    2629 begin
    2630   if lightness <= 32768 then
    2631   begin
    2632     if lightness = 32768 then
    2633       result := color else
    2634     begin
    2635       lightness256 := GammaCompressionTab[lightness shl 1];
    2636       result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
    2637                      color.blue * lightness256 shr 8, color.alpha);
    2638     end;
    2639   end else
    2640   begin
    2641     if lightness = 65535 then
    2642       result := BGRA(255,255,255,color.alpha) else
    2643     begin
    2644       lightness -= 32767;
    2645       r := GammaExpansionTab[color.red];
    2646       g := GammaExpansionTab[color.green];
    2647       b := GammaExpansionTab[color.blue];
    2648       result := BGRA(GammaCompressionTab[ r + (not r)*lightness shr 15 ],
    2649                      GammaCompressionTab[ g + (not g)*lightness shr 15 ],
    2650                      GammaCompressionTab[ b + (not b)*lightness shr 15 ],
    2651                      color.alpha);
    2652     end;
    2653   end;
    2654 end;
    2655 
    2656 function CombineLightness(lightness1,lightness2: Int32or64): Int32or64;
    2657 {$ifdef CPUI386} {$asmmode intel} assembler;
    2658   asm
    2659     imul edx
    2660     shl edx, 17
    2661     shr eax, 15
    2662     or edx, eax
    2663     mov result, edx
    2664   end;
    2665 {$ELSE}
    2666 begin
    2667   result := int64(lightness1)*lightness2 shr 15;
    2668 end;
    2669 {$ENDIF}
    2670 
    2671 function ApplyIntensityFast(color: TBGRAPixel; lightness: longword): TBGRAPixel;
    2672 var
    2673     maxValue,invMaxValue,r,g,b: longword;
    2674     lightness256: byte;
    2675 begin
    2676   if lightness <= 32768 then
    2677   begin
    2678     if lightness = 32768 then
    2679       result := color else
    2680     begin
    2681       lightness256 := GammaCompressionTab[lightness shl 1];
    2682       result := BGRA(color.red * lightness256 shr 8, color.green*lightness256 shr 8,
    2683                      color.blue * lightness256 shr 8, color.alpha);
    2684     end;
    2685   end else
    2686   begin
    2687     r := CombineLightness(GammaExpansionTab[color.red], lightness);
    2688     g := CombineLightness(GammaExpansionTab[color.green], lightness);
    2689     b := CombineLightness(GammaExpansionTab[color.blue], lightness);
    2690     maxValue := r;
    2691     if g > maxValue then maxValue := g;
    2692     if b > maxValue then maxValue := b;
    2693     if maxValue <= 65535 then
    2694       result := BGRA(GammaCompressionTab[r],
    2695                      GammaCompressionTab[g],
    2696                      GammaCompressionTab[b],
    2697                      color.alpha)
    2698     else
    2699     begin
    2700       invMaxValue := (longword(2147483647)+longword(maxValue-1)) div maxValue;
    2701       maxValue := (maxValue-65535) shr 1;
    2702       r := r*invMaxValue shr 15 + maxValue;
    2703       g := g*invMaxValue shr 15 + maxValue;
    2704       b := b*invMaxValue shr 15 + maxValue;
    2705       if r >= 65535 then result.red := 255 else
    2706         result.red := GammaCompressionTab[r];
    2707       if g >= 65535 then result.green := 255 else
    2708         result.green := GammaCompressionTab[g];
    2709       if b >= 65535 then result.blue := 255 else
    2710         result.blue := GammaCompressionTab[b];
    2711       result.alpha := color.alpha;
    2712     end;
    2713   end;
    2714 end;
    2715 
    2716 { Conversion from RGB value to HSL colorspace. See : http://en.wikipedia.org/wiki/HSL_color_space }
    2717 function BGRAToHSLA(c: TBGRAPixel): THSLAPixel;
    2718 begin
    2719   result := ExpandedToHSLA(GammaExpansion(c));
    2720 end;
    2721 
    2722 procedure ExpandedToHSLAInline(r,g,b: Int32Or64; var dest: THSLAPixel); inline;
    2723 const
    2724   deg60  = 10922;
    2725   deg120 = 21845;
    2726   deg240 = 43690;
    2727 var
    2728   min, max, minMax: Int32or64;
    2729   UMinMax,UTwiceLightness: UInt32or64;
    2730 begin
    2731   if g > r then
    2732   begin
    2733     max := g;
    2734     min := r;
    2735   end
    2736   else
    2737   begin
    2738     max := r;
    2739     min := g;
    2740   end;
    2741   if b > max then
    2742     max := b
    2743   else
    2744   if b < min then
    2745     min  := b;
    2746   minMax := max - min;
    2747 
    2748   if minMax = 0 then
    2749     dest.hue := 0
    2750   else
    2751   if max = r then
    2752     {$PUSH}{$RANGECHECKS OFF}
    2753     dest.hue := ((g - b) * deg60) div minMax
    2754     {$POP}
    2755   else
    2756   if max = g then
    2757     dest.hue := ((b - r) * deg60) div minMax + deg120
    2758   else
    2759     {max = b} dest.hue := ((r - g) * deg60) div minMax + deg240;
    2760   UTwiceLightness := max + min;
    2761   if min = max then
    2762     dest.saturation := 0
    2763   else
    2764   begin
    2765     UMinMax:= minMax;
    2766     if UTwiceLightness < 65536 then
    2767       dest.saturation := (UMinMax shl 16) div (UTwiceLightness + 1)
    2768     else
    2769       dest.saturation := (UMinMax shl 16) div (131072 - UTwiceLightness);
    2770   end;
    2771   dest.lightness := UTwiceLightness shr 1;
    2772 end;
    2773 
    2774 function ExpandedToHSLA(const ec: TExpandedPixel): THSLAPixel;
    2775 begin
    2776   result.alpha := ec.alpha;
    2777   ExpandedToHSLAInline(ec.red,ec.green,ec.blue,result);
    2778 end;
    2779 
    2780 function HtoG(hue: word): word;
    2781 const
    2782   segmentDest: array[0..5] of NativeUInt =
    2783      (13653, 10923, 8192, 13653, 10923, 8192);
    2784   segmentSrc: array[0..5] of NativeUInt =
    2785      (10923, 10922, 10923, 10923, 10922, 10923);
    2786 var
    2787   h,g: NativeUInt;
    2788 begin
    2789   h := hue;
    2790   if h < segmentSrc[0] then
    2791     g := h * segmentDest[0] div segmentSrc[0]
    2792   else
    2793   begin
    2794     g := segmentDest[0];
    2795     h -= segmentSrc[0];
    2796     if h < segmentSrc[1] then
    2797       g += h * segmentDest[1] div segmentSrc[1]
    2798     else
    2799     begin
    2800       g += segmentDest[1];
    2801       h -= segmentSrc[1];
    2802       if h < segmentSrc[2] then
    2803         g += h * segmentDest[2] div segmentSrc[2]
    2804       else
    2805       begin
    2806         g += segmentDest[2];
    2807         h -= segmentSrc[2];
    2808         if h < segmentSrc[3] then
    2809           g += h * segmentDest[3] div segmentSrc[3]
    2810         else
    2811         begin
    2812           g += segmentDest[3];
    2813           h -= segmentSrc[3];
    2814           if h < segmentSrc[4] then
    2815             g += h * segmentDest[4] div segmentSrc[4]
    2816           else
    2817           begin
    2818             g += segmentDest[4];
    2819             h -= segmentSrc[4];
    2820             g += h * segmentDest[5] div segmentSrc[5];
    2821           end;
    2822         end;
    2823       end;
    2824     end;
    2825   end;
    2826   result := g;
    2827 end;
    2828 
    2829 function GtoH(ghue: word): word;
    2830 const
    2831   segment: array[0..5] of NativeUInt =
    2832      (13653, 10923, 8192, 13653, 10923, 8192);
    2833 var g: NativeUint;
    2834 begin
    2835   g := ghue;
    2836   if g < segment[0] then
    2837     result := g * 10923 div segment[0]
    2838   else
    2839   begin
    2840     g -= segment[0];
    2841     if g < segment[1] then
    2842       result := g * (21845-10923) div segment[1] + 10923
    2843     else
    2844     begin
    2845       g -= segment[1];
    2846       if g < segment[2] then
    2847         result := g * (32768-21845) div segment[2] + 21845
    2848       else
    2849       begin
    2850         g -= segment[2];
    2851         if g < segment[3] then
    2852           result := g * (43691-32768) div segment[3] + 32768
    2853         else
    2854         begin
    2855           g -= segment[3];
    2856           if g < segment[4] then
    2857             result := g * (54613-43691) div segment[4] + 43691
    2858           else
    2859           begin
    2860             g -= segment[4];
    2861             result := g * (65536-54613) div segment[5] + 54613;
    2862           end;
    2863         end;
    2864       end;
    2865     end;
    2866   end;
    2867 end;
    2868 
    2869 function BGRAToGSBA(c: TBGRAPixel): TGSBAPixel;
    2870 var lightness: UInt32Or64;
    2871     red,green,blue: Int32or64;
    2872 begin
    2873   red   := GammaExpansionTab[c.red];
    2874   green := GammaExpansionTab[c.green];
    2875   blue  := GammaExpansionTab[c.blue];
    2876   result.alpha := c.alpha shl 8 + c.alpha;
    2877 
    2878   lightness := (red * redWeightShl10 + green * greenWeightShl10 +
    2879     blue * blueWeightShl10 + 512) shr 10;
    2880 
    2881   ExpandedToHSLAInline(red,green,blue,result);
    2882   if result.lightness > 32768 then
    2883     result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;
    2884   result.lightness := lightness;
    2885   result.hue := HtoG(result.hue);
    2886 end;
    2887 
    2888 function ExpandedToGSBA(ec: TExpandedPixel): TGSBAPixel;
    2889 var lightness: UInt32Or64;
    2890     red,green,blue: Int32or64;
    2891 begin
    2892   red   := ec.red;
    2893   green := ec.green;
    2894   blue  := ec.blue;
    2895   result.alpha := ec.alpha;
    2896 
    2897   lightness := (red * redWeightShl10 + green * greenWeightShl10 +
    2898     blue * blueWeightShl10 + 512) shr 10;
    2899 
    2900   ExpandedToHSLAInline(red,green,blue,result);
    2901   if result.lightness > 32768 then
    2902     result.saturation := result.saturation* UInt32or64(not result.lightness) div 32767;
    2903   result.lightness := lightness;
    2904   result.hue := HtoG(result.hue);
    2905 end;
    2906 
    2907 function HSLAToExpanded(const c: THSLAPixel): TExpandedPixel;
    2908 const
    2909   deg30  = 4096;
    2910   deg60  = 8192;
    2911   deg120 = deg60 * 2;
    2912   deg180 = deg60 * 3;
    2913   deg240 = deg60 * 4;
    2914   deg360 = deg60 * 6;
    2915 
    2916   function ComputeColor(p, q: Int32or64; h: Int32or64): Int32or64; inline;
    2917   begin
    2918     if h < deg180 then
    2919     begin
    2920       if h < deg60 then
    2921         Result := p + ((q - p) * h + deg30) div deg60
    2922       else
    2923         Result := q
    2924     end else
    2925     begin
    2926       if h < deg240 then
    2927         Result := p + ((q - p) * (deg240 - h) + deg30) div deg60
    2928       else
    2929         Result := p;
    2930     end;
    2931   end;
    2932 
    2933 var
    2934   q, p, L, S, H: Int32or64;
    2935 begin
    2936   L := c.lightness;
    2937   S := c.saturation;
    2938   if S = 0 then  //gray
    2939   begin
    2940     result.red   := L;
    2941     result.green := L;
    2942     result.blue  := L;
    2943     result.alpha := c.alpha;
    2944     exit;
    2945   end;
    2946   {$hints off}
    2947   if L < 32768 then
    2948     q := (L shr 1) * ((65535 + S) shr 1) shr 14
    2949   else
    2950     q := L + S - ((L shr 1) *
    2951       (S shr 1) shr 14);
    2952   {$hints on}
    2953   if q > 65535 then q := 65535;
    2954   p   := (L shl 1) - q;
    2955   if p > 65535 then p := 65535;
    2956   H := c.hue * deg360 shr 16;
    2957   result.green := ComputeColor(p, q, H);
    2958   inc(H, deg120);
    2959   if H > deg360 then Dec(H, deg360);
    2960   result.red   := ComputeColor(p, q, H);
    2961   inc(H, deg120);
    2962   if H > deg360 then Dec(H, deg360);
    2963   result.blue  := ComputeColor(p, q, H);
    2964   result.alpha := c.alpha;
    2965 end;
    2966 
    2967 { Conversion from HSL colorspace to RGB. See : http://en.wikipedia.org/wiki/HSL_color_space }
    2968 function HSLAToBGRA(const c: THSLAPixel): TBGRAPixel;
    2969 var ec: TExpandedPixel;
    2970 begin
    2971   ec := HSLAToExpanded(c);
    2972   Result := GammaCompression(ec);
    2973 end;
    2974 
    2975 function HueDiff(h1, h2: word): word;
    2976 begin
    2977   result := abs(integer(h1)-integer(h2));
    2978   if result > 32768 then result := 65536-result;
    2979 end;
    2980 
    2981 function GetHue(ec: TExpandedPixel): word;
    2982 const
    2983   deg60  = 8192;
    2984   deg120 = deg60 * 2;
    2985   deg240 = deg60 * 4;
    2986   deg360 = deg60 * 6;
    2987 var
    2988   min, max, minMax: integer;
    2989   r,g,b: integer;
    2990 begin
    2991   r := ec.red;
    2992   g := ec.green;
    2993   b := ec.blue;
    2994   min := r;
    2995   max := r;
    2996   if g > max then
    2997     max := g
    2998   else
    2999   if g < min then
    3000     min := g;
    3001   if b > max then
    3002     max := b
    3003   else
    3004   if b < min then
    3005     min  := b;
    3006   minMax := max - min;
    3007 
    3008   if minMax = 0 then
    3009     Result := 0
    3010   else
    3011   if max = r then
    3012     Result := (((g - b) * deg60) div
    3013       minMax + deg360) mod deg360
    3014   else
    3015   if max = g then
    3016     Result := ((b - r) * deg60) div minMax + deg120
    3017   else
    3018     {max = b} Result :=
    3019       ((r - g) * deg60) div minMax + deg240;
    3020 
    3021   Result   := (Result shl 16) div deg360; //normalize
    3022 end;
    3023 
    3024 function ColorImportance(ec: TExpandedPixel): word;
    3025 var min,max: word;
    3026 begin
    3027   min := ec.red;
    3028   max := ec.red;
    3029   if ec.green > max then
    3030     max := ec.green
    3031   else
    3032   if ec.green < min then
    3033     min := ec.green;
    3034   if ec.blue > max then
    3035     max := ec.blue
    3036   else
    3037   if ec.blue < min then
    3038     min  := ec.blue;
    3039   result := max - min;
    3040 end;
    3041 
    3042 function GSBAToBGRA(c: TGSBAPixel): TBGRAPixel;
    3043 var ec: TExpandedPixel;
    3044     lightness: word;
    3045 begin
    3046   c.hue := GtoH(c.hue);
    3047   lightness := c.lightness;
    3048   c.lightness := 32768;
    3049   ec := HSLAToExpanded(c);
    3050   result := GammaCompression(SetLightness(ec, lightness));
    3051 end;
    3052 
    3053 function GSBAToExpanded(c: TGSBAPixel): TExpandedPixel;
    3054 var lightness: word;
    3055 begin
    3056   c.hue := GtoH(c.hue);
    3057   lightness := c.lightness;
    3058   c.lightness := 32768;
    3059   result := SetLightness(HSLAToExpanded(c),lightness);
    3060 end;
    3061 
    3062 function GSBAToHSLA(c: TGSBAPixel): THSLAPixel;
    3063 begin
    3064   result := BGRAToHSLA(GSBAToBGRA(c));
    3065 end;
    3066 
    3067 { Apply gamma correction using conversion tables }
    3068 function GammaExpansion(c: TBGRAPixel): TExpandedPixel;
    3069 begin
    3070   Result.red   := GammaExpansionTab[c.red];
    3071   Result.green := GammaExpansionTab[c.green];
    3072   Result.blue  := GammaExpansionTab[c.blue];
    3073   Result.alpha := c.alpha shl 8 + c.alpha;
    3074 end;
    3075 
    3076 function GammaCompression(const ec: TExpandedPixel): TBGRAPixel;
    3077 begin
    3078   Result.red   := GammaCompressionTab[ec.red];
    3079   Result.green := GammaCompressionTab[ec.green];
    3080   Result.blue  := GammaCompressionTab[ec.blue];
    3081   Result.alpha := ec.alpha shr 8;
    3082 end;
    3083 
    3084 function GammaCompression(red, green, blue, alpha: word): TBGRAPixel;
    3085 begin
    3086   Result.red   := GammaCompressionTab[red];
    3087   Result.green := GammaCompressionTab[green];
    3088   Result.blue  := GammaCompressionTab[blue];
    3089   Result.alpha := alpha shr 8;
    3090 end;
    3091 
    3092 // Conversion to grayscale by taking into account
    3093 // different color weights
    3094 function BGRAToGrayscale(c: TBGRAPixel): TBGRAPixel;
    3095 var
    3096   ec:    TExpandedPixel;
    3097   gray:  word;
    3098   cgray: byte;
    3099 begin
    3100   if c.alpha = 0 then
    3101   begin
    3102     result := BGRAPixelTransparent;
    3103     exit;
    3104   end;
    3105   //gamma expansion
    3106   ec    := GammaExpansion(c);
    3107   //gray composition
    3108   gray  := (ec.red * redWeightShl10 + ec.green * greenWeightShl10 +
    3109     ec.blue * blueWeightShl10 + 512) shr 10;
    3110   //gamma compression
    3111   cgray := GammaCompressionTab[gray];
    3112   Result.red := cgray;
    3113   Result.green := cgray;
    3114   Result.blue := cgray;
    3115   Result.alpha := c.alpha;
    3116 end;
    3117 
    3118 function GrayscaleToBGRA(lightness: word): TBGRAPixel;
    3119 begin
    3120   result.red := GammaCompressionTab[lightness];
    3121   result.green := result.red;
    3122   result.blue := result.red;
    3123   result.alpha := $ff;
    3124 end;
    3125 
    3126 function MergeBGRA(const colors: array of TBGRAPixel): TBGRAPixel;
    3127 var
    3128   sumR,sumG,sumB,sumA: NativeUInt;
    3129   i: integer;
    3130 begin
    3131   if length(colors)<=0 then
    3132   begin
    3133     result := BGRAPixelTransparent;
    3134     exit;
    3135   end;
    3136   sumR := 0;
    3137   sumG := 0;
    3138   sumB := 0;
    3139   sumA := 0;
    3140   for i := 0 to high(colors) do
    3141   with colors[i] do
    3142   begin
    3143     sumR += red*alpha;
    3144     sumG += green*alpha;
    3145     sumB += blue*alpha;
    3146     sumA += alpha;
    3147   end;
    3148   if sumA > 0 then
    3149   begin
    3150     result.red := (sumR + sumA shr 1) div sumA;
    3151     result.green := (sumG + sumA shr 1) div sumA;
    3152     result.blue := (sumB + sumA shr 1) div sumA;
    3153     result.alpha := sumA div longword(length(colors));
    3154   end
    3155   else
    3156     result := BGRAPixelTransparent;
    3157 end;
    3158 
    3159 { Merge linearly two colors of same importance }
    3160 function MergeBGRA(c1, c2: TBGRAPixel): TBGRAPixel;
    3161 var c12: cardinal;
    3162 begin
    3163   if (c1.alpha = 0) then
    3164     Result := c2
    3165   else
    3166   if (c2.alpha = 0) then
    3167     Result := c1
    3168   else
    3169   begin
    3170     c12 := c1.alpha + c2.alpha;
    3171     Result.red   := (c1.red * c1.alpha + c2.red * c2.alpha + c12 shr 1) div c12;
    3172     Result.green := (c1.green * c1.alpha + c2.green * c2.alpha + c12 shr 1) div c12;
    3173     Result.blue  := (c1.blue * c1.alpha + c2.blue * c2.alpha + c12 shr 1) div c12;
    3174     Result.alpha := (c12 + 1) shr 1;
    3175   end;
    3176 end;
    3177 
    3178 function MergeBGRA(c1: TBGRAPixel; weight1: integer; c2: TBGRAPixel;
    3179   weight2: integer): TBGRAPixel;
    3180 var
    3181     f1,f2,f12: int64;
    3182 begin
    3183   if (weight1 = 0) then
    3184   begin
    3185     if (weight2 = 0) then
    3186       result := BGRAPixelTransparent
    3187     else
    3188       Result := c2
    3189   end
    3190   else
    3191   if (weight2 = 0) then
    3192     Result := c1
    3193   else
    3194   if (weight1+weight2 = 0) then
    3195     Result := BGRAPixelTransparent
    3196   else
    3197   begin
    3198     f1 := int64(c1.alpha)*weight1;
    3199     f2 := int64(c2.alpha)*weight2;
    3200     f12 := f1+f2;
    3201     if f12 = 0 then
    3202       result := BGRAPixelTransparent
    3203     else
    3204     begin
    3205       Result.red   := (c1.red * f1 + c2.red * f2 + f12 shr 1) div f12;
    3206       Result.green := (c1.green * f1 + c2.green * f2 + f12 shr 1) div f12;
    3207       Result.blue  := (c1.blue * f1 + c2.blue * f2 + f12 shr 1) div f12;
    3208       {$hints off}
    3209       Result.alpha := (f12 + ((weight1+weight2) shr 1)) div (weight1+weight2);
    3210       {$hints on}
    3211     end;
    3212   end;
    3213 end;
    3214 
    3215 function MergeBGRAWithGammaCorrection(c1: TBGRAPixel; weight1: byte; c2: TBGRAPixel;
    3216   weight2: byte): TBGRAPixel;
    3217 var
    3218     w1,w2,f1,f2,f12,a: UInt32or64;
    3219 begin
    3220   w1 := weight1;
    3221   w2 := weight2;
    3222   if (w1 = 0) then
    3223   begin
    3224     if (w2 = 0) then
    3225       result := BGRAPixelTransparent
    3226     else
    3227       Result := c2
    3228   end
    3229   else
    3230   if (w2 = 0) then
    3231     Result := c1
    3232   else
    3233   begin
    3234     f1 := c1.alpha*w1;
    3235     f2 := c2.alpha*w2;
    3236     a := (f1+f2 + ((w1+w2) shr 1)) div (w1+w2);
    3237     if a = 0 then
    3238     begin
    3239       result := BGRAPixelTransparent;
    3240       exit;
    3241     end else
    3242       Result.alpha := a;
    3243     {$IFNDEF CPU64}
    3244     if (f1 >= 32768) or (f2 >= 32768) then
    3245     begin
    3246       f1 := f1 shr 1;
    3247       f2 := f2 shr 1;
    3248     end;
    3249     {$ENDIF}
    3250     f12 := f1+f2;
    3251     Result.red   := GammaCompressionTab[(GammaExpansionTab[c1.red] * f1 + GammaExpansionTab[c2.red] * f2) div f12];
    3252     Result.green := GammaCompressionTab[(GammaExpansionTab[c1.green] * f1 + GammaExpansionTab[c2.green] * f2) div f12];
    3253     Result.blue  := GammaCompressionTab[(GammaExpansionTab[c1.blue] * f1 + GammaExpansionTab[c2.blue] * f2) div f12];
    3254   end;
    3255 end;
    3256 
    3257 { Merge two colors of same importance }
    3258 function MergeBGRA(ec1, ec2: TExpandedPixel): TExpandedPixel;
    3259 var c12: cardinal;
    3260 begin
    3261   if (ec1.alpha = 0) then
    3262     Result := ec2
    3263   else
    3264   if (ec2.alpha = 0) then
    3265     Result := ec1
    3266   else
    3267   begin
    3268     c12 := ec1.alpha + ec2.alpha;
    3269     Result.red   := (int64(ec1.red) * ec1.alpha + int64(ec2.red) * ec2.alpha + c12 shr 1) div c12;
    3270     Result.green := (int64(ec1.green) * ec1.alpha + int64(ec2.green) * ec2.alpha + c12 shr 1) div c12;
    3271     Result.blue  := (int64(ec1.blue) * ec1.alpha + int64(ec2.blue) * ec2.alpha + c12 shr 1) div c12;
    3272     Result.alpha := (c12 + 1) shr 1;
    3273   end;
    3274 end;
    3275 
    3276 function BGRA(red, green, blue, alpha: byte): TBGRAPixel;
    3277 begin
    3278   Result.red   := red;
    3279   Result.green := green;
    3280   Result.blue  := blue;
    3281   Result.alpha := alpha;
    3282 end;
    3283 
    3284 function BGRA(red, green, blue: byte): TBGRAPixel; overload;
    3285 begin
    3286   Result.red   := red;
    3287   Result.green := green;
    3288   Result.blue  := blue;
    3289   Result.alpha := 255;
    3290 end;
    3291 
    3292 { Convert a TColor value to a TBGRAPixel value. Note that
    3293   you need to call ColorToRGB first if you use a system
    3294   color identifier like clWindow. }
    3295 {$PUSH}{$R-}
    3296 
    3297 function HSLA(hue, saturation, lightness, alpha: word): THSLAPixel;
    3298 begin
    3299   Result.hue   := hue;
    3300   Result.saturation := saturation;
    3301   Result.lightness  := lightness;
    3302   Result.alpha := alpha;
    3303 end;
    3304 
    3305 function HSLA(hue, saturation, lightness: word): THSLAPixel;
    3306 begin
    3307   Result.hue   := hue;
    3308   Result.saturation := saturation;
    3309   Result.lightness  := lightness;
    3310   Result.alpha := $ffff;
    3311 end;
    3312 
    3313 function ColorToBGRA(color: TColor): TBGRAPixel; overload;
    3314 begin
    3315   Result.red   := color;
    3316   Result.green := color shr 8;
    3317   Result.blue  := color shr 16;
    3318   Result.alpha := 255;
    3319 end;
    3320 
    3321 function ColorToBGRA(color: TColor; opacity: byte): TBGRAPixel; overload;
    3322 begin
    3323   Result.red   := color;
    3324   Result.green := color shr 8;
    3325   Result.blue  := color shr 16;
    3326   Result.alpha := opacity;
    3327 end;
    3328 {$POP}
    3329 
    3330 { Conversion from TFPColor to TBGRAPixel assuming TFPColor
    3331   is already gamma compressed }
    3332 function FPColorToBGRA(AValue: TFPColor): TBGRAPixel;
    3333 begin
    3334   with AValue do
    3335     Result := BGRA(red shr 8, green shr 8, blue shr 8, alpha shr 8);
    3336 end;
    3337 
    3338 function BGRAToFPColor(AValue: TBGRAPixel): TFPColor; inline;
    3339 begin
    3340   result.red := AValue.red shl 8 + AValue.red;
    3341   result.green := AValue.green shl 8 + AValue.green;
    3342   result.blue := AValue.blue shl 8 + AValue.blue;
    3343   result.alpha := AValue.alpha shl 8 + AValue.alpha;
    3344 end;
    3345 
    3346 function BGRAToColor(c: TBGRAPixel): TColor;
    3347 begin
    3348   Result := c.red + (c.green shl 8) + (c.blue shl 16);
    3349 end;
    3350 
    3351 operator = (const c1, c2: TBGRAPixel): boolean;
    3352 begin
    3353   if (c1.alpha = 0) and (c2.alpha = 0) then
    3354     Result := True
    3355   else
    3356     Result := (c1.alpha = c2.alpha) and (c1.red = c2.red) and
    3357       (c1.green = c2.green) and (c1.blue = c2.blue);
    3358 end;
    3359 
    3360 function LessStartSlope65535(value: word): word;
    3361 var factor: word;
    3362 begin
    3363   factor := 4096 - (not value)*3 shr 7;
    3364   result := value*factor shr 12;
    3365 end;
    3366 
    3367 function ExpandedDiff(ec1, ec2: TExpandedPixel): word;
    3368 var
    3369   CompRedAlpha1, CompGreenAlpha1, CompBlueAlpha1, CompRedAlpha2,
    3370   CompGreenAlpha2, CompBlueAlpha2: integer;
    3371   DiffAlpha: word;
    3372   ColorDiff: word;
    3373   TempHueDiff: word;
    3374 begin
    3375   CompRedAlpha1 := ec1.red * ec1.alpha shr 16; //gives 0..65535
    3376   CompGreenAlpha1 := ec1.green * ec1.alpha shr 16;
    3377   CompBlueAlpha1 := ec1.blue * ec1.alpha shr 16;
    3378   CompRedAlpha2 := ec2.red * ec2.alpha shr 16;
    3379   CompGreenAlpha2 := ec2.green * ec2.alpha shr 16;
    3380   CompBlueAlpha2 := ec2.blue * ec2.alpha shr 16;
    3381   Result    := (Abs(CompRedAlpha2 - CompRedAlpha1)*redWeightShl10 +
    3382     Abs(CompBlueAlpha2 - CompBlueAlpha1)*blueWeightShl10 +
    3383     Abs(CompGreenAlpha2 - CompGreenAlpha1)*greenWeightShl10) shr 10;
    3384   ColorDiff := min(ColorImportance(ec1),ColorImportance(ec2));
    3385   if ColorDiff > 0 then
    3386   begin
    3387     TempHueDiff := HueDiff(HtoG(GetHue(ec1)),HtoG(GetHue(ec2)));
    3388     if TempHueDiff < 32768 then
    3389       TempHueDiff := LessStartSlope65535(TempHueDiff shl 1) shr 4
    3390     else
    3391       TempHueDiff := TempHueDiff shr 3;
    3392     Result := ((Result shr 4)* (not ColorDiff) + TempHueDiff*ColorDiff) shr 12;
    3393   end;
    3394   DiffAlpha := Abs(integer(ec2.Alpha) - integer(ec1.Alpha));
    3395   if DiffAlpha > Result then
    3396     Result := DiffAlpha;
    3397 end;
    3398 
    3399 function BGRAWordDiff(c1, c2: TBGRAPixel): word;
    3400 begin
    3401   result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2));
    3402 end;
    3403 
    3404 function BGRADiff(c1,c2: TBGRAPixel): byte;
    3405 begin
    3406   result := ExpandedDiff(GammaExpansion(c1),GammaExpansion(c2)) shr 8;
    3407 end;
    3408 
    3409 operator-(const c1, c2: TColorF): TColorF;
    3410 begin
    3411   result[1] := c1[1]-c2[1];
    3412   result[2] := c1[2]-c2[2];
    3413   result[3] := c1[3]-c2[3];
    3414   result[4] := c1[4]-c2[4];
    3415 end;
    3416 
    3417 operator+(const c1, c2: TColorF): TColorF;
    3418 begin
    3419   result[1] := c1[1]+c2[1];
    3420   result[2] := c1[2]+c2[2];
    3421   result[3] := c1[3]+c2[3];
    3422   result[4] := c1[4]+c2[4];
    3423 end;
    3424 
    3425 operator*(const c1, c2: TColorF): TColorF;
    3426 begin
    3427   result[1] := c1[1]*c2[1];
    3428   result[2] := c1[2]*c2[2];
    3429   result[3] := c1[3]*c2[3];
    3430   result[4] := c1[4]*c2[4];
    3431 end;
    3432 
    3433 operator*(const c1: TColorF; factor: single): TColorF;
    3434 begin
    3435   result[1] := c1[1]*factor;
    3436   result[2] := c1[2]*factor;
    3437   result[3] := c1[3]*factor;
    3438   result[4] := c1[4]*factor;
    3439 end;
    3440 
    3441 function ColorF(red, green, blue, alpha: single): TColorF;
    3442 begin
    3443   result[1] := red;
    3444   result[2] := green;
    3445   result[3] := blue;
    3446   result[4] := alpha;
    3447 end;
    3448 
    3449 { Write a color in hexadecimal format RRGGBBAA or using the name in a color list }
    3450 function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
    3451 var idx: integer;
    3452 begin
    3453   if Assigned(AColorList) then
    3454   begin
    3455     idx := AColorList.IndexOfColor(c, AMaxDiff);
    3456     if idx<> -1 then
    3457     begin
    3458       result := AColorList.Name[idx];
    3459       exit;
    3460     end;
    3461   end;
    3462   result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2);
    3463 end;
    3464 
    3465 type
    3466     arrayOfString = array of string;
    3467 
    3468 function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString;
    3469 var idxOpen,start,cur: integer;
    3470 begin
    3471     result := nil;
    3472     idxOpen := pos('(',str);
    3473     if idxOpen = 0 then
    3474     begin
    3475       start := 1;
    3476       //find first space
    3477       while (start <= length(str)) and (str[start]<>' ') do inc(start);
    3478     end else
    3479       start := idxOpen+1;
    3480     cur := start;
    3481     while cur <= length(str) do
    3482     begin
    3483        if str[cur] in[',',')'] then
    3484        begin
    3485          setlength(result,length(result)+1);
    3486          result[high(result)] := trim(copy(str,start,cur-start));
    3487          start := cur+1;
    3488          if str[cur] = ')' then exit;
    3489        end;
    3490        inc(cur);
    3491     end;
    3492     if idxOpen <> 0 then flagError := true; //should exit on ')'
    3493     if start <= length(str) then
    3494     begin
    3495       setlength(result,length(result)+1);
    3496       result[high(result)] := copy(str,start,length(str)-start+1);
    3497     end;
    3498 end;
    3499 
    3500 function ParseColorValue(str: string; var flagError: boolean): byte;
    3501 var pourcent,unclipped,{%H-}errPos: integer;
    3502 begin
    3503   if str = '' then result := 0 else
    3504   begin
    3505     if str[length(str)]='%' then
    3506     begin
    3507       val(copy(str,1,length(str)-1),pourcent,errPos);
    3508       if errPos <> 0 then flagError := true;
    3509       if pourcent < 0 then result := 0 else
    3510       if pourcent > 100 then result := 255 else
    3511         result := pourcent*255 div 100;
    3512     end else
    3513     begin
    3514       val(str,unclipped,errPos);
    3515       if errPos <> 0 then flagError := true;
    3516       if unclipped < 0 then result := 0 else
    3517       if unclipped > 255 then result := 255 else
    3518         result := unclipped;
    3519     end;
    3520   end;
    3521 end;
    3522 
    3523 //this function returns the parsed value only if it contains no error nor missing values, otherwise
    3524 //it returns BGRAPixelTransparent
    3525 function StrToBGRA(str: string): TBGRAPixel;
    3526 var missingValues, error: boolean;
    3527 begin
    3528   result := BGRABlack;
    3529   TryStrToBGRA(str, result, missingValues, error);
    3530   if missingValues or error then result := BGRAPixelTransparent;
    3531 end;
    3532 
    3533 //this function changes the content of parsedValue depending on available and parsable information.
    3534 //set parsedValue to the fallback values before calling this function.
    3535 //missing values are expressed by empty string or by '?', for example 'rgb(255,?,?,?)' will change only the red value.
    3536 //note that if alpha is not expressed by the string format, it will be opaque. So 'rgb(255,?,?)' will change the red value and the alpha value.
    3537 //the last parameter of rgba() is a floating point number where 1 is opaque and 0 is transparent.
    3538 procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    3539 var errPos: integer;
    3540     values: array of string;
    3541     alphaF: single;
    3542     idx: integer;
    3543 begin
    3544   str := Trim(str);
    3545   error := false;
    3546   if (str = '') or (str = '?') then
    3547   begin
    3548     missingValues := true;
    3549     exit;
    3550   end else
    3551     missingValues := false;
    3552   str := StringReplace(lowerCase(str),'grey','gray',[]);
    3553 
    3554   //VGA color names
    3555   idx := VGAColors.IndexOf(str);
    3556   if idx <> -1 then
    3557   begin
    3558     parsedValue := VGAColors[idx];
    3559     exit;
    3560   end;
    3561   if str='transparent' then parsedValue := BGRAPixelTransparent else
    3562   begin
    3563     //check CSS color
    3564     idx := CSSColors.IndexOf(str);
    3565     if idx <> -1 then
    3566     begin
    3567       parsedValue := CSSColors[idx];
    3568       exit;
    3569     end;
    3570 
    3571     //CSS RGB notation
    3572     if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') or
    3573       (copy(str,1,4)='rgb ') or (copy(str,1,5)='rgba ') then
    3574     begin
    3575       values := SimpleParseFuncParam(str,error);
    3576       if (length(values)=3) or (length(values)=4) then
    3577       begin
    3578         if (values[0] <> '') and (values[0] <> '?') then
    3579            parsedValue.red := ParseColorValue(values[0], error)
    3580         else
    3581            missingValues := true;
    3582         if (values[1] <> '') and (values[1] <> '?') then
    3583            parsedValue.green := ParseColorValue(values[1], error)
    3584         else
    3585            missingValues := true;
    3586         if (values[2] <> '') and (values[2] <> '?') then
    3587            parsedValue.blue := ParseColorValue(values[2], error)
    3588         else
    3589            missingValues := true;
    3590         if length(values)=4 then
    3591         begin
    3592           if (values[3] <> '') and (values[3] <> '?') then
    3593           begin
    3594             val(values[3],alphaF,errPos);
    3595             if errPos <> 0 then
    3596             begin
    3597                parsedValue.alpha := 255;
    3598                error := true;
    3599             end
    3600             else
    3601             begin
    3602               if alphaF < 0 then
    3603                 parsedValue.alpha := 0 else
    3604               if alphaF > 1 then
    3605                 parsedValue.alpha := 255
    3606               else
    3607                 parsedValue.alpha := round(alphaF*255);
    3608             end;
    3609           end else
    3610             missingValues := true;
    3611         end else
    3612           parsedValue.alpha := 255;
    3613       end else
    3614         error := true;
    3615       exit;
    3616     end;
    3617 
    3618     //remove HTML notation header
    3619     if str[1]='#' then delete(str,1,1);
    3620 
    3621     //add alpha if missing (if you want an undefined alpha use '??' or '?')
    3622     if length(str)=6 then str += 'FF';
    3623     if length(str)=3 then str += 'F';
    3624 
    3625     //hex notation
    3626     if length(str)=8 then
    3627     begin
    3628       if copy(str,1,2) <> '??' then
    3629       begin
    3630         val('$'+copy(str,1,2),parsedValue.red,errPos);
    3631         if errPos <> 0 then error := true;
    3632       end else missingValues := true;
    3633       if copy(str,3,2) <> '??' then
    3634       begin
    3635         val('$'+copy(str,3,2),parsedValue.green,errPos);
    3636         if errPos <> 0 then error := true;
    3637       end else missingValues := true;
    3638       if copy(str,5,2) <> '??' then
    3639       begin
    3640         val('$'+copy(str,5,2),parsedValue.blue,errPos);
    3641         if errPos <> 0 then error := true;
    3642       end else missingValues := true;
    3643       if copy(str,7,2) <> '??' then
    3644       begin
    3645         val('$'+copy(str,7,2),parsedValue.alpha,errPos);
    3646         if errPos <> 0 then
    3647         begin
    3648           error := true;
    3649           parsedValue.alpha := 255;
    3650         end;
    3651       end else missingValues := true;
    3652     end else
    3653     if length(str)=4 then
    3654     begin
    3655       if str[1] <> '?' then
    3656       begin
    3657         val('$'+str[1],parsedValue.red,errPos);
    3658         if errPos <> 0 then error := true;
    3659         parsedValue.red *= $11;
    3660       end else missingValues := true;
    3661       if str[2] <> '?' then
    3662       begin
    3663         val('$'+str[2],parsedValue.green,errPos);
    3664         if errPos <> 0 then error := true;
    3665         parsedValue.green *= $11;
    3666       end else missingValues := true;
    3667       if str[3] <> '?' then
    3668       begin
    3669         val('$'+str[3],parsedValue.blue,errPos);
    3670         if errPos <> 0 then error := true;
    3671         parsedValue.blue *= $11;
    3672       end else missingValues := true;
    3673       if str[4] <> '?' then
    3674       begin
    3675         val('$'+str[4],parsedValue.alpha,errPos);
    3676         if errPos <> 0 then
    3677         begin
    3678           error := true;
    3679           parsedValue.alpha := 255;
    3680         end else
    3681           parsedValue.alpha *= $11;
    3682       end else missingValues := true;
    3683     end else
    3684       error := true; //string format not recognised
    3685   end;
    3686 
    3687 end;
    3688 
    3689 //this function returns the values that can be read from the string, otherwise
    3690 //it fills the gaps with the fallback values. The error boolean is True only
    3691 //if there was invalid values, it is not set to True if there was missing values.
    3692 function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out
    3693   error: boolean): TBGRAPixel;
    3694 var missingValues: boolean;
    3695 begin
    3696   result := fallbackValues;
    3697   TryStrToBGRA(str, result, missingValues, error);
    3698 end;
    3699 
    3700 { Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. }
    3701 function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;
    3702 var missingValues, error: boolean;
    3703 begin
    3704   result := BGRABlack;
    3705   TryStrToBGRA(str, result, missingValues, error);
    3706   if missingValues or error then result := DefaultColor;
    3707 end;
    3708 
    3709 function MapHeight(Color: TBGRAPixel): Single;
    3710 var intval: integer;
    3711 begin
    3712   intval := color.Green shl 16 + color.red shl 8 + color.blue;
    3713   result := intval*5.960464832810452e-8;
    3714 end;
    3715 
    3716 function MapHeightToBGRA(Height: Single; Alpha: Byte): TBGRAPixel;
    3717 var intval: integer;
    3718 begin
    3719   if Height >= 1 then result := BGRA(255,255,255,alpha) else
    3720   if Height <= 0 then result := BGRA(0,0,0,alpha) else
    3721   begin
    3722     intval := round(Height*16777215);
    3723     result := BGRA(intval shr 8,intval shr 16,intval,alpha);
    3724   end;
    3725 end;
    3726 
    3727 {********************** Point functions **************************}
    3728 
    3729 function PointF(x, y: single): TPointF;
    3730 begin
    3731   Result.x := x;
    3732   Result.y := y;
    3733 end;
    3734 
    3735 function PointsF(const pts: array of TPointF): ArrayOfTPointF;
    3736 var
    3737   i: Integer;
    3738 begin
    3739   setlength(result, length(pts));
    3740   for i := 0 to high(pts) do result[i] := pts[i];
    3741 end;
    3742 
    3743 operator =(const pt1, pt2: TPointF): boolean;
    3744 begin
    3745   result := (pt1.x = pt2.x) and (pt1.y = pt2.y);
    3746 end;
    3747 
    3748 operator-(const pt1, pt2: TPointF): TPointF;
    3749 begin
    3750   result.x := pt1.x-pt2.x;
    3751   result.y := pt1.y-pt2.y;
    3752 end;
    3753 
    3754 operator-(const pt2: TPointF): TPointF;
    3755 begin
    3756   result.x := -pt2.x;
    3757   result.y := -pt2.y;
    3758 end;
    3759 
    3760 operator+(const pt1, pt2: TPointF): TPointF;
    3761 begin
    3762   result.x := pt1.x+pt2.x;
    3763   result.y := pt1.y+pt2.y;
    3764 end;
    3765 
    3766 operator*(const pt1, pt2: TPointF): single;
    3767 begin
    3768   result := pt1.x*pt2.x + pt1.y*pt2.y;
    3769 end;
    3770 
    3771 operator*(const pt1: TPointF; factor: single): TPointF;
    3772 begin
    3773   result.x := pt1.x*factor;
    3774   result.y := pt1.y*factor;
    3775 end;
    3776 
    3777 operator*(factor: single; const pt1: TPointF): TPointF;
    3778 begin
    3779   result.x := pt1.x*factor;
    3780   result.y := pt1.y*factor;
    3781 end;
    3782 
    3783 function PtInRect(const pt: TPoint; r: TRect): boolean;
    3784 var
    3785   temp: integer;
    3786 begin
    3787   if r.right < r.left then
    3788   begin
    3789     temp    := r.left;
    3790     r.left  := r.right;
    3791     r.Right := temp;
    3792   end;
    3793   if r.bottom < r.top then
    3794   begin
    3795     temp     := r.top;
    3796     r.top    := r.bottom;
    3797     r.bottom := temp;
    3798   end;
    3799   Result := (pt.X >= r.left) and (pt.Y >= r.top) and (pt.X < r.right) and
    3800     (pt.y < r.bottom);
    3801 end;
    3802 
    3803 function RectWithSize(left, top, width, height: integer): TRect;
    3804 begin
    3805   result.left := left;
    3806   result.top := top;
    3807   result.right := left+width;
    3808   result.bottom := top+height;
    3809 end;
    3810 
    3811 function VectLen(dx, dy: single): single;
    3812 begin
    3813   result := sqrt(dx*dx+dy*dy);
    3814 end;
    3815 
    3816 function VectLen(v: TPointF): single;
    3817 begin
    3818   result := sqrt(v.x*v.x+v.y*v.y);
    3819 end;
    3820 {$OPTIMIZATION OFF}  // Modif J.P  5/2013
    3821 function IntersectLine(line1, line2: TLineDef): TPointF;
    3822 var parallel: boolean;
    3823 begin
    3824   result := IntersectLine(line1,line2,parallel);
    3825 end;
    3826 {$OPTIMIZATION ON}
    3827 
    3828 function IntersectLine(line1, line2: TLineDef; out parallel: boolean): TPointF;
    3829 var divFactor: double;
    3830 begin
    3831   parallel := false;
    3832   //if lines are parallel
    3833   if ((line1.dir.x = line2.dir.x) and (line1.dir.y = line2.dir.y)) or
    3834      ((abs(line1.dir.y) < 1e-6) and (abs(line2.dir.y) < 1e-6)) then
    3835   begin
    3836        parallel := true;
    3837        //return the center of the segment between line origins
    3838        result.x := (line1.origin.x+line2.origin.x)/2;
    3839        result.y := (line1.origin.y+line2.origin.y)/2;
    3840   end else
    3841   if abs(line1.dir.y) < 1e-6 then //line1 is horizontal
    3842   begin
    3843        result.y := line1.origin.y;
    3844        result.x := line2.origin.x + (result.y - line2.origin.y)
    3845                /line2.dir.y*line2.dir.x;
    3846   end else
    3847   if abs(line2.dir.y) < 1e-6 then //line2 is horizontal
    3848   begin
    3849        result.y := line2.origin.y;
    3850        result.x := line1.origin.x + (result.y - line1.origin.y)
    3851                /line1.dir.y*line1.dir.x;
    3852   end else
    3853   begin
    3854        divFactor := line1.dir.x/line1.dir.y - line2.dir.x/line2.dir.y;
    3855        if abs(divFactor) < 1e-6 then //almost parallel
    3856        begin
    3857             parallel := true;
    3858             //return the center of the segment between line origins
    3859             result.x := (line1.origin.x+line2.origin.x)/2;
    3860             result.y := (line1.origin.y+line2.origin.y)/2;
    3861        end else
    3862        begin
    3863          result.y := (line2.origin.x - line1.origin.x +
    3864                   line1.origin.y*line1.dir.x/line1.dir.y -
    3865                   line2.origin.y*line2.dir.x/line2.dir.y)
    3866                   / divFactor;
    3867          result.x := line1.origin.x + (result.y - line1.origin.y)
    3868                  /line1.dir.y*line1.dir.x;
    3869        end;
    3870   end;
    3871 end;
    3872 
    3873 { Check if a polygon is convex, i.e. it always turns in the same direction }
    3874 function IsConvex(const pts: array of TPointF; IgnoreAlign: boolean = true): boolean;
    3875 var
    3876   positive,negative,zero: boolean;
    3877   product: single;
    3878   i: Integer;
    3879 begin
    3880   positive := false;
    3881   negative := false;
    3882   zero := false;
    3883   for i := 0 to high(pts) do
    3884   begin
    3885     product := (pts[(i+1) mod length(pts)].x-pts[i].x)*(pts[(i+2) mod length(pts)].y-pts[i].y) -
    3886                (pts[(i+1) mod length(pts)].y-pts[i].y)*(pts[(i+2) mod length(pts)].x-pts[i].x);
    3887     if product > 0 then
    3888     begin
    3889       if negative then
    3890       begin
    3891         result := false;
    3892         exit;
    3893       end;
    3894       positive := true;
    3895     end else
    3896     if product < 0 then
    3897     begin
    3898       if positive then
    3899       begin
    3900         result := false;
    3901         exit;
    3902       end;
    3903       negative := true;
    3904     end else
    3905       zero := true;
    3906   end;
    3907   if not IgnoreAlign and zero then
    3908     result := false
    3909   else
    3910     result := true;
    3911 end;
    3912 
    3913 { Check if two segments intersect }
    3914 function DoesSegmentIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    3915 var
    3916   seg1: TLineDef;
    3917   seg1len: single;
    3918   seg2: TLineDef;
    3919   seg2len: single;
    3920   inter: TPointF;
    3921   pos1,pos2: single;
    3922   para: boolean;
    3923 
    3924 begin
    3925   { Determine line definitions }
    3926   seg1.origin := pt1;
    3927   seg1.dir := pt2-pt1;
    3928   seg1len := sqrt(sqr(seg1.dir.X)+sqr(seg1.dir.Y));
    3929   if seg1len = 0 then
    3930   begin
    3931     result := false;
    3932     exit;
    3933   end;
    3934   seg1.dir *= 1/seg1len;
    3935 
    3936   seg2.origin := pt3;
    3937   seg2.dir := pt4-pt3;
    3938   seg2len := sqrt(sqr(seg2.dir.X)+sqr(seg2.dir.Y));
    3939   if seg2len = 0 then
    3940   begin
    3941     result := false;
    3942     exit;
    3943   end;
    3944   seg2.dir *= 1/seg2len;
    3945 
    3946   //obviously parallel
    3947   if seg1.dir = seg2.dir then
    3948     result := false
    3949   else
    3950   begin
    3951     //try to compute intersection
    3952     inter := IntersectLine(seg1,seg2,para);
    3953     if para then
    3954       result := false
    3955     else
    3956     begin
    3957       //check if intersections are inside the segments
    3958       pos1 := (inter-seg1.origin)*seg1.dir;
    3959       pos2 := (inter-seg2.origin)*seg2.dir;
    3960       if (pos1 >= 0) and (pos1 <= seg1len) and
    3961          (pos2 >= 0) and (pos2 <= seg2len) then
    3962         result := true
    3963       else
    3964         result := false;
    3965     end;
    3966   end;
    3967 end;
    3968 
    3969 { Check if a quaduadrilateral intersects itself }
    3970 function DoesQuadIntersect(pt1,pt2,pt3,pt4: TPointF): boolean;
    3971 begin
    3972   result := DoesSegmentIntersect(pt1,pt2,pt3,pt4) or DoesSegmentIntersect(pt2,pt3,pt4,pt1);
    3973612end;
    3974613
     
    4110749        begin
    4111750          for i := 0 to 6 do dwords[i] := LEtoN(dwords[i]);
    4112           if (dwords[0] = 0) and (dwords[1] <= expectedFileSize) and (dwords[5] <= expectedFileSize) and
    4113              (dwords[9] <= expectedFileSize) and
     751          if (dwords[0] = 0) and (dwords[1] <= maxFileSize) and (dwords[5] <= maxFileSize) and
     752             (dwords[9] <= maxFileSize) and
    4114753            (dwords[6] = 0) then inc(scores[ifLazPaint],2);
    4115754        end;
     
    4199838    end;
    4200839
     840    if (copy(magicAsText,1,4) = 'oXo ') then
     841    begin
     842      inc(scores[ifPhoxo],1);
     843      if (magic[4] = 1) and (magic[5] = 0) and (magic[6] = 0) and (magic[7] = 0) then
     844        inc(scores[ifPhoxo],1);
     845    end;
     846
    4201847    DetectLazPaint;
    4202848
     
    4233879
    4234880  ASuggestedExtensionUTF8:= UTF8LowerCase(ASuggestedExtensionUTF8);
    4235   if (ASuggestedExtensionUTF8 <> '') and (UTF8Copy(ASuggestedExtensionUTF8,1,1) <> '.') then
     881  if (ASuggestedExtensionUTF8 <> '') and (ASuggestedExtensionUTF8[1] <> '.') then //first UTF8 char is in first pos
    4236882    ASuggestedExtensionUTF8 := '.'+ASuggestedExtensionUTF8;
    4237883
     
    4254900function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
    4255901var ext: string;
     902  posDot: integer;
    4256903begin
    4257904  result := ifUnknown;
    4258905
    4259906  ext := ExtractFileName(AFilenameOrExtensionUTF8);
    4260   if pos('.', ext) <> 0 then ext := ExtractFileExt(ext) else ext := '.'+ext;
     907  posDot := LastDelimiter('.', ext);
     908  if posDot <> 0 then ext := copy(ext,posDot,length(ext)-posDot+1)
     909  else ext := '.'+ext;
    4261910  ext := UTF8LowerCase(ext);
    4262911
     
    4274923  if (ext = '.tif') or (ext = '.tiff') then result := ifTiff else
    4275924  if (ext = '.xwd') then result := ifXwd else
    4276   if (ext = '.xpm') then result := ifXPixMap;
     925  if (ext = '.xpm') then result := ifXPixMap else
     926  if (ext = '.oxo') then result := ifPhoxo;
     927end;
     928
     929function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
     930begin
     931  case AFormat of
     932    ifJpeg: result := 'jpg';
     933    ifPng: result := 'png';
     934    ifGif: result := 'gif';
     935    ifBmp: result := 'bmp';
     936    ifIco: result := 'ico';
     937    ifPcx: result := 'pcx';
     938    ifPaintDotNet: result := 'pdn';
     939    ifLazPaint: result := 'lzp';
     940    ifOpenRaster: result := 'ora';
     941    ifPsd: result := 'psd';
     942    ifTarga: result := 'tga';
     943    ifTiff: result := 'tif';
     944    ifXwd: result := 'xwd';
     945    ifXPixMap: result := 'xpm';
     946    ifBmpMioMap: result := 'bmp';
     947    else result := '?';
     948  end;
    4277949end;
    4278950
     
    4306978  if AFormat = ifPng then
    4307979  begin
    4308     result := TFPWriterPNG.Create;
    4309     TFPWriterPNG(result).Indexed := false;
    4310     TFPWriterPNG(result).WordSized := false;
    4311     TFPWriterPNG(result).UseAlpha := AHasTransparentPixels;
     980    result := TBGRAWriterPNG.Create;
     981    TBGRAWriterPNG(result).UseAlpha := AHasTransparentPixels;
    4312982  end else
    4313983  if AFormat = ifBmp then
     
    4328998initialization
    4329999
    4330   InitGamma;
    4331   {$DEFINE INCLUDE_COLOR_LIST}
     1000  {$DEFINE INCLUDE_INIT}
     1001  {$I bgrapixel.inc}
     1002
     1003  {$DEFINE INCLUDE_INIT}
    43321004  {$I csscolorconst.inc}
     1005 
    43331006  DefaultBGRAImageWriter[ifJpeg] := TFPWriterJPEG;
    4334   DefaultBGRAImageWriter[ifPng] := TFPWriterPNG;
     1007  DefaultBGRAImageWriter[ifPng] := TBGRAWriterPNG;
    43351008  DefaultBGRAImageWriter[ifBmp] := TFPWriterBMP;
    43361009  DefaultBGRAImageWriter[ifPcx] := TFPWriterPCX;
     
    43461019finalization
    43471020
    4348   CSSColors.Free;
    4349   VGAColors.Free;
    4350 
     1021  {$DEFINE INCLUDE_FINAL}
     1022  {$I csscolorconst.inc}
     1023
     1024  {$DEFINE INCLUDE_FINAL}
     1025  {$I bgrapixel.inc}
    43511026end.
  • GraphicTest/Packages/bgrabitmap/bgrablend.pas

    r472 r494  
    2020procedure DrawExpandedPixelInlineNoAlphaCheck(dest: PBGRAPixel; const ec: TExpandedPixel; calpha: byte); inline; overload;
    2121procedure ClearTypeDrawPixel(pdest: PBGRAPixel; Cr, Cg, Cb: byte; Color: TBGRAPixel); inline;
     22procedure InterpolateBilinear(pUpLeft,pUpRight,pDownLeft,pDownRight: PBGRAPixel;
     23                iFactX,iFactY: Integer; ADest: PBGRAPixel);
    2224
    2325procedure CopyPixelsWithOpacity(dest,src: PBGRAPixel; opacity: byte; Count: integer); inline;
     
    100102procedure ScreenPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
    101103procedure SoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
     104procedure SvgSoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
    102105procedure HardLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
    103106procedure BlendXorPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
     107procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
     108procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x, y: integer;
     109  mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner;
     110  KeepRGBOrder: boolean);
     111procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
    104112
    105113implementation
     114
     115procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
     116var
     117  pdest: PBGRAPixel;
     118  ClearTypePixel: array[0..2] of byte;
     119  curThird: integer;
     120
     121  procedure OutputPixel; inline;
     122  begin
     123    if texture <> nil then
     124      color := texture.ScanNextPixel;
     125    if RGBOrder then
     126      ClearTypeDrawPixel(pdest, ClearTypePixel[0],ClearTypePixel[1],ClearTypePixel[2], color)
     127    else
     128      ClearTypeDrawPixel(pdest, ClearTypePixel[2],ClearTypePixel[1],ClearTypePixel[0], color);
     129  end;
     130
     131  procedure NextAlpha(alphaValue: byte); inline;
     132  begin
     133    ClearTypePixel[curThird] := alphaValue;
     134    inc(curThird);
     135    if curThird = 3 then
     136    begin
     137      OutputPixel;
     138      curThird := 0;
     139      Fillchar(ClearTypePixel, sizeof(ClearTypePixel),0);
     140      inc(pdest);
     141    end;
     142  end;
     143
     144  procedure EndRow; inline;
     145  begin
     146    if curThird > 0 then OutputPixel;
     147  end;
     148
     149var
     150  yMask,n: integer;
     151  a: byte;
     152  pmask: PByte;
     153  dx:integer;
     154  miny,maxy,minx,minxThird,maxx,alphaMinX,alphaMaxX,alphaLineLen: integer;
     155  leftOnSide, rightOnSide: boolean;
     156  countBetween: integer;
     157  v1,v2,v3: byte;
     158
     159  procedure StartRow; inline;
     160  begin
     161    pdest := dest.Scanline[yMask+y]+minx;
     162    if texture <> nil then
     163      texture.ScanMoveTo(minx,yMask+y);
     164
     165    curThird := minxThird;
     166    ClearTypePixel[0] := 0;
     167    ClearTypePixel[1] := 0;
     168    ClearTypePixel[2] := 0;
     169  end;
     170
     171begin
     172  alphaLineLen := maskWidth+2;
     173
     174  xThird -= 1; //for first subpixel
     175
     176  if xThird >= 0 then dx := xThird div 3
     177   else dx := -((-xThird+2) div 3);
     178  x += dx;
     179  xThird -= dx*3;
     180
     181  if y >= dest.ClipRect.Top then miny := 0
     182    else miny := dest.ClipRect.Top-y;
     183  if y+maskHeight-1 < dest.ClipRect.Bottom then
     184    maxy := maskHeight-1 else
     185      maxy := dest.ClipRect.Bottom-1-y;
     186
     187  if x >= dest.ClipRect.Left then
     188  begin
     189    minx := x;
     190    minxThird := xThird;
     191    alphaMinX := 0;
     192    leftOnSide := false;
     193  end else
     194  begin
     195    minx := dest.ClipRect.Left;
     196    minxThird := 0;
     197    alphaMinX := (dest.ClipRect.Left-x)*3 - xThird;
     198    leftOnSide := true;
     199  end;
     200
     201  if x*3+xThird+maskWidth-1 < dest.ClipRect.Right*3 then
     202  begin
     203    maxx := (x*3+xThird+maskWidth-1) div 3;
     204    alphaMaxX := alphaLineLen-1;
     205    rightOnSide := false;
     206  end else
     207  begin
     208    maxx := dest.ClipRect.Right-1;
     209    alphaMaxX := maxx*3+2 - (x*3+xThird);
     210    rightOnSide := true;
     211  end;
     212
     213  countBetween := alphaMaxX-alphaMinX-1;
     214
     215  if (alphaMinX <= alphaMaxX) then
     216  begin
     217    for yMask := miny to maxy do
     218    begin
     219      StartRow;
     220
     221      if leftOnSide then
     222      begin
     223        pmask := maskData + (yMask*maskRowSize)+ (alphaMinX-1)*maskPixelSize;
     224        a := pmask^ div 3;
     225        v1 := a+a;
     226        v2 := a;
     227        v3 := 0;
     228        inc(pmask, maskPixelSize);
     229      end else
     230      begin
     231        pmask := maskData + (yMask*maskRowSize);
     232        v1 := 0;
     233        v2 := 0;
     234        v3 := 0;
     235      end;
     236
     237      for n := countBetween-1 downto 0 do
     238      begin
     239        a := pmask^ div 3;
     240        v1 += a;
     241        v2 += a;
     242        v3 += a;
     243        inc(pmask, maskPixelSize);
     244
     245        NextAlpha(v1);
     246        v1 := v2;
     247        v2 := v3;
     248        v3 := 0;
     249      end;
     250
     251      if rightOnSide then
     252      begin
     253        a := pmask^ div 3;
     254        v1 += a;
     255        v2 += a+a;
     256      end;
     257
     258      NextAlpha(v1);
     259      NextAlpha(v2);
     260
     261      EndRow;
     262    end;
     263  end;
     264end;
     265
     266procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
     267var delta: NativeInt;
     268begin
     269  delta := mask.Width*sizeof(TBGRAPixel);
     270  if mask.LineOrder = riloBottomToTop then
     271    delta := -delta;
     272  BGRAFillClearTypeMaskPtr(dest,x,y,xThird,pbyte(mask.ScanLine[0])+1,sizeof(TBGRAPixel),delta,mask.Width,mask.Height,color,texture,RGBOrder);
     273end;
     274
     275procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x, y: integer;
     276  mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner;
     277  KeepRGBOrder: boolean);
     278var
     279  minx,miny,maxx,maxy,countx,n,yb: integer;
     280  pdest,psrc: PBGRAPixel;
     281begin
     282  if y >= dest.ClipRect.Top then miny := 0
     283    else miny := dest.ClipRect.Top-y;
     284  if y+mask.Height-1 < dest.ClipRect.Bottom then
     285    maxy := mask.Height-1 else
     286      maxy := dest.ClipRect.Bottom-1-y;
     287
     288  if x >= dest.ClipRect.Left then minx := 0
     289    else minx := dest.ClipRect.Left-x;
     290  if x+mask.Width-1 < dest.ClipRect.Right then
     291    maxx := mask.Width-1 else
     292      maxx := dest.ClipRect.Right-1-x;
     293
     294  countx := maxx-minx+1;
     295  if countx <= 0 then exit;
     296
     297  for yb := miny to maxy do
     298  begin
     299    pdest := dest.ScanLine[y+yb]+(x+minx);
     300    psrc := mask.ScanLine[yb]+minx;
     301    if texture <> nil then
     302      texture.ScanMoveTo(x+minx, y+yb);
     303    if KeepRGBOrder then
     304    begin
     305      for n := countx-1 downto 0 do
     306      begin
     307        if texture <> nil then color := texture.ScanNextPixel;
     308        ClearTypeDrawPixel(pdest, psrc^.red, psrc^.green, psrc^.blue, color);
     309        inc(pdest);
     310        inc(psrc);
     311      end;
     312    end else
     313    begin
     314      for n := countx-1 downto 0 do
     315      begin
     316        if texture <> nil then color := texture.ScanNextPixel;
     317        ClearTypeDrawPixel(pdest, psrc^.blue, psrc^.green, psrc^.red, color);
     318        inc(pdest);
     319        inc(psrc);
     320      end;
     321    end;
     322  end;
     323end;
    106324
    107325procedure ClearTypeDrawPixel(pdest: PBGRAPixel; Cr, Cg, Cb: byte; Color: TBGRAPixel);
     
    141359    end;
    142360    pdest^ := merge;
     361  end;
     362end;
     363
     364procedure InterpolateBilinear(pUpLeft, pUpRight, pDownLeft,
     365  pDownRight: PBGRAPixel; iFactX,iFactY: Integer; ADest: PBGRAPixel);
     366var
     367  w1,w2,w3,w4,alphaW: cardinal;
     368  rSum, gSum, bSum: cardinal; //rgbDiv = aSum
     369  aSum, aDiv: cardinal;
     370begin
     371  rSum   := 0;
     372  gSum   := 0;
     373  bSum   := 0;
     374  aSum   := 0;
     375  aDiv   := 0;
     376
     377  w4 := (iFactX*iFactY+127) shr 8;
     378  w3 := iFactY-w4;
     379  {$PUSH}{$HINTS OFF}
     380  w1 := (256-iFactX)-w3;
     381  {$POP}
     382  w2 := iFactX-w4;
     383
     384  { For each pixel around the coordinate, compute
     385    the weight for it and multiply values by it before
     386    adding to the sum }
     387  if pUpLeft <> nil then
     388  with pUpLeft^ do
     389  begin
     390    alphaW := alpha * w1;
     391    aDiv   += w1;
     392    aSum   += alphaW;
     393    rSum   += red * alphaW;
     394    gSum   += green * alphaW;
     395    bSum   += blue * alphaW;
     396  end;
     397  if pUpRight <> nil then
     398  with pUpRight^ do
     399  begin
     400    alphaW := alpha * w2;
     401    aDiv   += w2;
     402    aSum   += alphaW;
     403    rSum   += red * alphaW;
     404    gSum   += green * alphaW;
     405    bSum   += blue * alphaW;
     406  end;
     407  if pDownLeft <> nil then
     408  with pDownLeft^ do
     409  begin
     410    alphaW := alpha * w3;
     411    aDiv   += w3;
     412    aSum   += alphaW;
     413    rSum   += red * alphaW;
     414    gSum   += green * alphaW;
     415    bSum   += blue * alphaW;
     416  end;
     417  if pDownRight <> nil then
     418  with pDownRight^ do
     419  begin
     420    alphaW := alpha * w4;
     421    aDiv   += w4;
     422    aSum   += alphaW;
     423    rSum   += red * alphaW;
     424    gSum   += green * alphaW;
     425    bSum   += blue * alphaW;
     426  end;
     427
     428  if aSum < 128 then //if there is no alpha
     429    ADest^ := BGRAPixelTransparent
     430  else
     431  with ADest^ do
     432  begin
     433    red   := (rSum + aSum shr 1) div aSum;
     434    green := (gSum + aSum shr 1) div aSum;
     435    blue  := (bSum + aSum shr 1) div aSum;
     436    if aDiv = 256 then
     437      alpha := (aSum + 128) shr 8
     438    else
     439      alpha := (aSum + aDiv shr 1) div aDiv;
    143440  end;
    144441end;
     
    475772procedure DrawPixelInlineNoAlphaCheck(dest: PBGRAPixel; const c: TBGRAPixel);
    476773var
    477   p: PByte;
    478774  a1f, a2f, a12, a12m: cardinal;
    479775begin
     
    486782  a2f := (c.alpha shl 8) - c.alpha;
    487783
    488   p := PByte(dest);
    489 
    490   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f +
    491     GammaExpansionTab[c.blue] * a2f + a12m) div a12];
    492   Inc(p);
    493   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f +
    494     GammaExpansionTab[c.green] * a2f + a12m) div a12];
    495   Inc(p);
    496   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f +
    497     GammaExpansionTab[c.red] * a2f + a12m) div a12];
    498   Inc(p);
    499 
    500   p^ := (a12 + a12 shr 7) shr 8;
     784  PDWord(dest)^ := ((GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f +
     785                     GammaExpansionTab[c.red] * a2f + a12m) div a12]) shl TBGRAPixel_RedShift) or
     786                   ((GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f +
     787                     GammaExpansionTab[c.green] * a2f + a12m) div a12]) shl TBGRAPixel_GreenShift) or
     788                   ((GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f +
     789                     GammaExpansionTab[c.blue] * a2f + a12m) div a12]) shl TBGRAPixel_BlueShift) or
     790                   (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift);
    501791end;
    502792
     
    504794  const ec: TExpandedPixel; calpha: byte);
    505795var
    506   p: PByte;
    507796  a1f, a2f, a12, a12m: cardinal;
    508797begin
     
    515804  a2f := (calpha shl 8) - calpha;
    516805
    517   p := PByte(dest);
    518 
    519   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f +
    520     ec.blue * a2f + a12m) div a12];
    521   Inc(p);
    522   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f +
    523     ec.green * a2f + a12m) div a12];
    524   Inc(p);
    525   p^ := GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f +
    526     ec.red * a2f + a12m) div a12];
    527   Inc(p);
    528 
    529   p^ := (a12 + a12 shr 7) shr 8;
     806  PDWord(dest)^ := ((GammaCompressionTab[(GammaExpansionTab[dest^.red] * a1f +
     807                     ec.red * a2f + a12m) div a12]) shl TBGRAPixel_RedShift) or
     808                   ((GammaCompressionTab[(GammaExpansionTab[dest^.green] * a1f +
     809                     ec.green * a2f + a12m) div a12]) shl TBGRAPixel_GreenShift) or
     810                   ((GammaCompressionTab[(GammaExpansionTab[dest^.blue] * a1f +
     811                     ec.blue * a2f + a12m) div a12]) shl TBGRAPixel_BlueShift) or
     812                   (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift);
    530813end;
    531814
    532815procedure FastBlendPixelInline(dest: PBGRAPixel; const c: TBGRAPixel);
    533816var
    534   p: PByte;
    535817  a1f, a2f, a12, a12m: cardinal;
    536818begin
     
    551833  a2f := (c.alpha shl 8) - c.alpha;
    552834
    553   p := PByte(dest);
    554 
    555   p^ := (dest^.blue * a1f + c.blue * a2f + a12m) div a12;
    556   Inc(p);
    557   p^ := (dest^.green * a1f + c.green * a2f + a12m) div a12;
    558   Inc(p);
    559   p^ := (dest^.red * a1f + c.red * a2f + a12m) div a12;
    560   Inc(p);
    561 
    562   p^ := (a12 + a12 shr 7) shr 8;
     835  PDWord(dest)^ := (((dest^.red * a1f + c.red * a2f + a12m) div a12) shl TBGRAPixel_RedShift) or
     836                   (((dest^.green * a1f + c.green * a2f + a12m) div a12) shl TBGRAPixel_GreenShift) or
     837                   (((dest^.blue * a1f + c.blue * a2f + a12m) div a12) shl TBGRAPixel_BlueShift) or
     838                   (((a12 + a12 shr 7) shr 8) shl TBGRAPixel_AlphaShift);
    563839end;
    564840
     
    572848procedure DrawPixelInlineDiff(dest: PBGRAPixel; c, compare: TBGRAPixel;
    573849  maxDiff: byte); inline;
    574 begin
    575   DrawPixelInlineWithAlphaCheck(dest, BGRA(c.red, c.green, c.blue,
    576     (c.alpha * (maxDiff + 1 - BGRADiff(dest^, compare)) + (maxDiff + 1) shr 1) div
    577     (maxDiff + 1)));
     850var alpha: NativeInt;
     851begin
     852  alpha := (c.alpha * (maxDiff + 1 - BGRADiff(dest^, compare)) + (maxDiff + 1) shr 1) div
     853    (maxDiff + 1);
     854  if alpha > 0 then
     855    DrawPixelInlineWithAlphaCheck(dest, BGRA(c.red, c.green, c.blue, alpha));
    578856end;
    579857
  • GraphicTest/Packages/bgrabitmap/bgracanvas.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, FPCanvas, Graphics, GraphType, Types, FPImage, BGRABitmapTypes;
     8  Classes, SysUtils, FPCanvas, BGRAGraphics, Types, FPImage, BGRABitmapTypes;
    99
    1010type
     
    10991099  Style: TGraphicsBevelCut);
    11001100begin
    1101   Frame3D(bounds,width,style,ColorToBGRA(ColorToRGB(clBtnHighlight)),ColorToBGRA(ColorToRGB(clBtnShadow)));
     1101  Frame3D(bounds,width,style,ColorToBGRA(clRgbBtnHighlight),ColorToBGRA(clRgbBtnShadow));
    11021102end;
    11031103
     
    11501150    RStop,RStart: Byte;
    11511151  begin
    1152     RedGreenBlue(ColorToRGB(AStart), RStart, GStart, BStart);
     1152      RedGreenBlue(ColorToRGB(AStart), RStart, GStart, BStart);
    11531153      RedGreenBlue(ColorToRGB(AStop),  RStop,  GStop,  BStop);
    11541154
  • GraphicTest/Packages/bgrabitmap/bgracanvas2d.pas

    r472 r494  
    1818
    1919uses
    20   Classes, SysUtils, Graphics, BGRABitmapTypes, BGRATransform, BGRAGradientScanner, BGRAPath;
     20  Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform,
     21  BGRAGradientScanner, BGRAPath, BGRAPen;
    2122
    2223type
     
    4243
    4344  TBGRACanvasState2D = class
     45  private
     46    FClipMask: TBGRACustomBitmap;
     47    FClipMaskOwned: boolean;
     48    function GetClipMaskReadWrite: TBGRACustomBitmap;
     49  public
    4450    strokeColor: TBGRAPixel;
    4551    strokeTextureProvider: IBGRACanvasTextureProvider2D;
     
    5561
    5662    lineWidth: single;
    57     lineCap: TPenEndCap;
    58     lineJoin: TPenJoinStyle;
    59     lineStyle: TBGRAPenStyle;
    60     miterLimit: single;
     63    penStroker: TBGRAPenStroker;
    6164
    6265    shadowOffsetX,shadowOffsetY,shadowBlur: single;
     
    6568
    6669    matrix: TAffineMatrix;
    67     clipMask: TBGRACustomBitmap;
    68     constructor Create(AMatrix: TAffineMatrix; AClipMask: TBGRACustomBitmap);
     70    constructor Create(AMatrix: TAffineMatrix; AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean);
    6971    function Duplicate: TBGRACanvasState2D;
    7072    destructor Destroy; override;
     73    procedure SetClipMask(AClipMask: TBGRACustomBitmap; AOwned: boolean);
     74    property clipMaskReadOnly: TBGRACustomBitmap read FClipMask;
     75    property clipMaskReadWrite: TBGRACustomBitmap read GetClipMaskReadWrite;
    7176  end;
    7277
     
    8893    FFontRenderer: TBGRACustomFontRenderer;
    8994    FLastCoord, FStartCoord: TPointF;
    90     function GetCurrentPath: ArrayOfTPointF;
     95    function GetCurrentPathAsPoints: ArrayOfTPointF;
    9196    function GetFontName: string;
    9297    function GetFontRenderer: TBGRACustomFontRenderer;
     
    110115    function GetShadowOffsetX: single;
    111116    function GetShadowOffsetY: single;
     117    function GetStrokeMatrix: TAffineMatrix;
    112118    function GetTextAlign: string;
    113119    function GetTextAlignLCL: TAlignment;
     
    135141    procedure SetShadowOffsetX(const AValue: single);
    136142    procedure SetShadowOffsetY(const AValue: single);
     143    procedure SetStrokeMatrix(AValue: TAffineMatrix);
    137144    procedure SetTextAlign(AValue: string);
    138145    procedure SetTextAlignLCL(AValue: TAlignment);
     
    152159    function GetDrawMode: TDrawMode;
    153160    procedure copyTo({%H-}dest: IBGRAPath); //IBGRAPath
     161    function getPoints: ArrayOfTPointF; //IBGRAPath
     162    function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF; //IBGRAPath
     163    function getCursor: TBGRACustomPathCursor; //IBGRAPath
    154164  public
    155165    antialiasing, linearBlend: boolean;
     
    165175    procedure rotate(angleRadCW: single);
    166176    procedure translate(x,y: single);
    167     procedure transform(a,b,c,d,e,f: single); overload;
     177    procedure skewx(angleRadCW: single);
     178    procedure skewy(angleRadCW: single);
     179    procedure transform(m11,m21, m12,m22, m13,m23: single); overload;
    168180    procedure transform(AMatrix: TAffineMatrix); overload;
    169     procedure setTransform(a,b,c,d,e,f: single);
     181    procedure setTransform(m11,m21, m12,m22, m13,m23: single);
    170182    procedure resetTransform;
     183
     184    procedure strokeScale(x,y: single);
     185    procedure strokeSkewx(angleRadCW: single);
     186    procedure strokeSkewy(angleRadCW: single);
     187    procedure strokeResetTransform;
     188
    171189    procedure strokeStyle(color: TBGRAPixel); overload;
    172190    procedure strokeStyle(color: TColor); overload;
     
    214232    procedure roundRect(x,y,w,h,radius: single); overload;
    215233    procedure roundRect(x,y,w,h,rx,ry: single); overload;
     234    procedure openedSpline(const pts: array of TPointF; style: TSplineStyle);
     235    procedure closedSpline(const pts: array of TPointF; style: TSplineStyle);
    216236    procedure spline(const pts: array of TPointF; style: TSplineStyle= ssOutside);
    217237    procedure splineTo(const pts: array of TPointF; style: TSplineStyle= ssOutside);
     
    254274    property globalAlpha: single read GetGlobalAlpha write SetGlobalAlpha;
    255275    property matrix: TAffineMatrix read GetMatrix write SetMatrix;
     276    property strokeMatrix: TAffineMatrix read GetStrokeMatrix write SetStrokeMatrix;
    256277
    257278    property lineWidth: single read GetLineWidth write SetLineWidth;
     
    277298    property textBaseline: string read GetTextBaseline write SetTextBaseine;
    278299
    279     property currentPath: ArrayOfTPointF read GetCurrentPath;
     300    property currentPath: ArrayOfTPointF read GetCurrentPathAsPoints;
    280301    property fontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;
    281302
     
    288309implementation
    289310
    290 uses Types, Math, BGRAPen, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
     311uses Types, Math, BGRAFillInfo, BGRAPolygon, BGRABlend, FPWriteJPEG, FPWriteBMP, base64;
    291312
    292313type
     
    522543{ TBGRACanvasState2D }
    523544
     545function TBGRACanvasState2D.GetClipMaskReadWrite: TBGRACustomBitmap;
     546begin
     547  if not FClipMaskOwned then
     548  begin
     549    if FClipMask <> nil then
     550      FClipMask := FClipMask.Duplicate;
     551    FClipMaskOwned := true;
     552  end;
     553  result := FClipMask;
     554end;
     555
    524556constructor TBGRACanvasState2D.Create(AMatrix: TAffineMatrix;
    525   AClipMask: TBGRACustomBitmap);
     557  AClipMask: TBGRACustomBitmap; AClipMaskOwned: boolean);
    526558begin
    527559  strokeColor := BGRABlack;
     
    536568
    537569  lineWidth := 1;
    538   lineCap := pecFlat;
    539   lineJoin := pjsMiter;
    540   lineStyle := DuplicatePenStyle(SolidPenStyle);
    541   miterLimit := 10;
     570  penStroker := TBGRAPenStroker.Create;
     571  penStroker.LineCap := pecFlat;
     572  penStroker.JoinStyle := pjsMiter;
     573  penStroker.CustomPenStyle := DuplicatePenStyle(SolidPenStyle);
     574  penStroker.MiterLimit := 10;
     575  penStroker.StrokeMatrix := AffineMatrixIdentity;
    542576
    543577  shadowOffsetX := 0;
     
    548582
    549583  matrix := AMatrix;
    550   if AClipMask = nil then
    551     clipMask := nil
    552   else
    553     clipMask := AClipMask.Duplicate;
     584  FClipMask := nil;
     585  FClipMaskOwned := true;
     586  SetClipMask(AClipMask,AClipMaskOwned);
    554587end;
    555588
    556589function TBGRACanvasState2D.Duplicate: TBGRACanvasState2D;
    557590begin
    558   result := TBGRACanvasState2D.Create(matrix,clipMask);
     591  result := TBGRACanvasState2D.Create(matrix,clipMaskReadOnly,false);
    559592  result.strokeColor := strokeColor;
    560593  result.strokeTextureProvider := strokeTextureProvider;
     
    568601
    569602  result.lineWidth := lineWidth;
    570   result.lineCap := lineCap;
    571   result.lineJoin := lineJoin;
    572   result.lineStyle := DuplicatePenStyle(lineStyle);
    573   result.miterLimit := miterLimit;
     603  result.penStroker.LineCap := penStroker.LineCap;
     604  result.penStroker.JoinStyle := penStroker.JoinStyle;
     605  result.penStroker.CustomPenStyle := DuplicatePenStyle(penStroker.CustomPenStyle);
     606  result.penStroker.MiterLimit := penStroker.MiterLimit;
     607  result.penStroker.StrokeMatrix := penStroker.StrokeMatrix;
    574608
    575609  result.shadowOffsetX := shadowOffsetX;
     
    582616destructor TBGRACanvasState2D.Destroy;
    583617begin
    584   clipMask.Free;
     618  if FClipMaskOwned and Assigned(FClipMask) then
     619    FClipMask.Free;
     620  penStroker.Free;
    585621  inherited Destroy;
     622end;
     623
     624procedure TBGRACanvasState2D.SetClipMask(AClipMask: TBGRACustomBitmap;
     625  AOwned: boolean);
     626begin
     627  if FClipMaskOwned and Assigned(FClipMask) then FreeAndNil(FClipMask);
     628  FClipMask := AClipMask;
     629  FClipMaskOwned := AOwned;
    586630end;
    587631
     
    598642function TBGRACanvas2D.GetLineCap: string;
    599643begin
    600   case currentState.lineCap of
     644  case currentState.penStroker.LineCap of
    601645    pecRound: result := 'round';
    602646    pecSquare: result := 'square';
     
    607651function TBGRACanvas2D.GetLineCapLCL: TPenEndCap;
    608652begin
    609   result := currentState.lineCap;
     653  result := currentState.penStroker.LineCap;
    610654end;
    611655
    612656function TBGRACanvas2D.GetlineJoin: string;
    613657begin
    614   case currentState.lineJoin of
     658  case currentState.penStroker.JoinStyle of
    615659    pjsBevel: result := 'bevel';
    616660    pjsRound: result := 'round';
     
    621665function TBGRACanvas2D.GetlineJoinLCL: TPenJoinStyle;
    622666begin
    623   result := currentState.lineJoin;
     667  result := currentState.penStroker.JoinStyle;
    624668end;
    625669
    626670function TBGRACanvas2D.getLineStyle: TBGRAPenStyle;
    627671begin
    628   result := DuplicatePenStyle(currentState.lineStyle);
     672  result := DuplicatePenStyle(currentState.penStroker.CustomPenStyle);
    629673end;
    630674
     
    641685function TBGRACanvas2D.GetMiterLimit: single;
    642686begin
    643   result := currentState.miterLimit;
     687  result := currentState.penStroker.MiterLimit;
    644688end;
    645689
     
    672716begin
    673717  result := currentState.shadowOffsetY;
     718end;
     719
     720function TBGRACanvas2D.GetStrokeMatrix: TAffineMatrix;
     721begin
     722  result := currentState.penStroker.StrokeMatrix;
    674723end;
    675724
     
    699748end;
    700749
    701 function TBGRACanvas2D.GetCurrentPath: ArrayOfTPointF;
     750function TBGRACanvas2D.GetCurrentPathAsPoints: ArrayOfTPointF;
    702751var i: integer;
    703752begin
     
    872921begin
    873922  if CompareText(AValue,'round')=0 then
    874     currentState.lineCap := pecRound else
     923    currentState.penStroker.LineCap := pecRound else
    875924  if CompareText(AValue,'square')=0 then
    876     currentState.lineCap := pecSquare
     925    currentState.penStroker.LineCap := pecSquare
    877926  else
    878     currentState.lineCap := pecFlat;
     927    currentState.penStroker.LineCap := pecFlat;
    879928end;
    880929
    881930procedure TBGRACanvas2D.SetLineCapLCL(AValue: TPenEndCap);
    882931begin
    883   currentState.lineCap := AValue;
     932  currentState.penStroker.LineCap := AValue;
    884933end;
    885934
     
    887936begin
    888937  if CompareText(AValue,'round')=0 then
    889     currentState.lineJoin := pjsRound else
     938    currentState.penStroker.JoinStyle := pjsRound else
    890939  if CompareText(AValue,'bevel')=0 then
    891     currentState.lineJoin := pjsBevel
     940    currentState.penStroker.JoinStyle := pjsBevel
    892941  else
    893     currentState.lineJoin := pjsMiter;
     942    currentState.penStroker.JoinStyle := pjsMiter;
    894943end;
    895944
     
    900949  if (length(points) = 0) or (surface = nil) then exit;
    901950  If hasShadow then DrawShadow(points,[]);
    902   if currentState.clipMask <> nil then
     951  if currentState.clipMaskReadOnly <> nil then
    903952  begin
    904953    if currentState.fillTextureProvider <> nil then
    905       tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
     954      tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
    906955    else
    907       tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
     956      tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
    908957    if self.antialiasing then
    909958      BGRAPolygon.FillPolyAntialiasWithTexture(surface, points, tempScan, true, linearBlend)
     
    9541003  multi := TBGRAMultishapeFiller.Create;
    9551004  multi.FillMode := fmWinding;
    956   if currentState.clipMask <> nil then
     1005  if currentState.clipMaskReadOnly <> nil then
    9571006  begin
    9581007    if currentState.fillTextureProvider <> nil then
    959       tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
     1008      tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.fillTextureProvider.texture,currentState.globalAlpha)
    9601009    else
    961       tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
     1010      tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.fillColor));
    9621011    multi.AddPolygon(points, tempScan);
    9631012  end else
     
    9781027  if currentState.lineWidth > 0 then
    9791028  begin
    980     contour := ComputeWidePolylinePoints(points,currentState.lineWidth,BGRAPixelTransparent,
    981         currentState.lineCap,currentState.lineJoin,currentState.lineStyle,[plAutoCycle],miterLimit);
    982 
    983     if currentState.clipMask <> nil then
     1029    contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth);
     1030
     1031    if currentState.clipMaskReadOnly <> nil then
    9841032    begin
    9851033      if currentState.strokeTextureProvider <> nil then
    986         tempScan2 := TBGRATextureMaskScanner.Create(currentState.clipMask,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
     1034        tempScan2 := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
    9871035      else
    988         tempScan2 := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
     1036        tempScan2 := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
    9891037      multi.AddPolygon(contour,tempScan);
    9901038    end else
     
    10121060procedure TBGRACanvas2D.SetLineJoinLCL(AValue: TPenJoinStyle);
    10131061begin
    1014   currentState.lineJoin := AValue;
     1062  currentState.penStroker.JoinStyle := AValue;
    10151063end;
    10161064
    10171065procedure TBGRACanvas2D.lineStyle(const AValue: array of single);
    10181066begin
    1019   currentState.lineStyle := DuplicatePenStyle(AValue);
     1067  currentState.penStroker.CustomPenStyle := DuplicatePenStyle(AValue);
    10201068end;
    10211069
     
    10631111procedure TBGRACanvas2D.SetMiterLimit(const AValue: single);
    10641112begin
    1065   currentState.miterLimit := AValue;
     1113  currentState.penStroker.MiterLimit := AValue;
    10661114end;
    10671115
     
    10991147begin
    11001148  currentState.shadowOffsetY := AValue;
     1149end;
     1150
     1151procedure TBGRACanvas2D.SetStrokeMatrix(AValue: TAffineMatrix);
     1152begin
     1153  currentState.penStroker.strokeMatrix := AValue;
    11011154end;
    11021155
     
    11291182begin
    11301183  if (length(points)= 0) or (currentState.lineWidth = 0) or (surface = nil) then exit;
    1131   contour := ComputeWidePolylinePoints(points,currentState.lineWidth,BGRAPixelTransparent,
    1132       currentState.lineCap,currentState.lineJoin,currentState.lineStyle,[plAutoCycle],miterLimit);
     1184  contour := currentState.penStroker.ComputePolylineAutocycle(points,currentState.lineWidth);
    11331185
    11341186  If hasShadow then DrawShadow(contour,[]);
    1135   if currentState.clipMask <> nil then
     1187  if currentState.clipMaskReadOnly <> nil then
    11361188  begin
    11371189    if currentState.strokeTextureProvider <> nil then
    1138       tempScan := TBGRATextureMaskScanner.Create(currentState.clipMask,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
     1190      tempScan := TBGRATextureMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),currentState.strokeTextureProvider.texture,currentState.globalAlpha)
    11391191    else
    1140       tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMask,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
     1192      tempScan := TBGRASolidColorMaskScanner.Create(currentState.clipMaskReadOnly,Point(0,0),ApplyGlobalAlpha(currentState.strokeColor));
    11411193    if self.antialiasing then
    11421194      BGRAPolygon.FillPolyAntialiasWithTexture(Surface,contour,tempScan,True, linearBlend)
     
    12061258
    12071259  maxRect := Types.Rect(0,0,width,height);
    1208   if currentState.clipMask <> nil then
     1260  if currentState.clipMaskReadOnly <> nil then
    12091261    foundRect := maxRect
    12101262  else
    12111263  begin
    12121264    firstFound := true;
     1265    foundRect := EmptyRect;
    12131266    for i := 0 to high(ofsPts) do
    12141267      AddPt(ofsPts[i]);
     
    12501303    end;
    12511304  end;
    1252   if currentState.clipMask <> nil then
    1253     tempBmp.ApplyMask(currentState.clipMask);
     1305  if currentState.clipMaskReadOnly <> nil then
     1306    tempBmp.ApplyMask(currentState.clipMaskReadOnly);
    12541307  surface.PutImage(foundRect.Left,foundRect.Top,tempBmp,GetDrawMode,currentState.globalAlpha);
    12551308  tempBmp.Free;
     
    13561409end;
    13571410
     1411function TBGRACanvas2D.getPoints: ArrayOfTPointF;
     1412begin
     1413  result := GetCurrentPathAsPoints;
     1414end;
     1415
     1416function TBGRACanvas2D.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF;
     1417begin
     1418  result := GetCurrentPathAsPoints;
     1419  if not IsAffineMatrixIdentity(AMatrix) then
     1420    result := AMatrix*result;
     1421end;
     1422
     1423function TBGRACanvas2D.getCursor: TBGRACustomPathCursor;
     1424begin
     1425  result := nil;
     1426end;
     1427
    13581428constructor TBGRACanvas2D.Create(ASurface: TBGRACustomBitmap);
    13591429begin
     
    13631433  FLastCoord := EmptyPointF;
    13641434  FStartCoord := EmptyPointF;
    1365   currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil);
     1435  currentState := TBGRACanvasState2D.Create(AffineMatrixIdentity,nil,true);
    13661436  pixelCenteredCoordinates := false;
    13671437  antialiasing := true;
     
    14511521procedure TBGRACanvas2D.translate(x, y: single);
    14521522begin
     1523  if (x = 0) and (y = 0) then exit;
    14531524  currentState.matrix *= AffineMatrixTranslation(x,y);
    14541525end;
    14551526
    1456 procedure TBGRACanvas2D.transform(a, b, c, d, e, f: single);
    1457 begin
    1458   currentState.matrix *= AffineMatrix(a,c,e,b,d,f);
     1527procedure TBGRACanvas2D.skewx(angleRadCW: single);
     1528begin
     1529  currentState.matrix *= AffineMatrixSkewXRad(-angleRadCW);
     1530end;
     1531
     1532procedure TBGRACanvas2D.skewy(angleRadCW: single);
     1533begin
     1534  currentState.matrix *= AffineMatrixSkewYRad(-angleRadCW);
     1535end;
     1536
     1537procedure TBGRACanvas2D.transform(m11,m21, m12,m22, m13,m23: single);
     1538begin
     1539  currentState.matrix *= AffineMatrix(m11,m12,m13,
     1540                                      m21,m22,m23);
    14591541end;
    14601542
     
    14641546end;
    14651547
    1466 procedure TBGRACanvas2D.setTransform(a, b, c, d, e, f: single);
    1467 begin
    1468   currentState.matrix := AffineMatrix(a,c,e,b,d,f);
     1548procedure TBGRACanvas2D.setTransform(m11,m21, m12,m22, m13,m23: single);
     1549begin
     1550  currentState.matrix := AffineMatrix(m11,m12,m13,
     1551                                      m21,m22,m23);
    14691552end;
    14701553
     
    14721555begin
    14731556  currentState.matrix := AffineMatrixIdentity;
     1557end;
     1558
     1559procedure TBGRACanvas2D.strokeScale(x, y: single);
     1560begin
     1561  currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixScale(x,y);
     1562end;
     1563
     1564procedure TBGRACanvas2D.strokeSkewx(angleRadCW: single);
     1565begin
     1566  currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewXRad(-angleRadCW);
     1567end;
     1568
     1569procedure TBGRACanvas2D.strokeSkewy(angleRadCW: single);
     1570begin
     1571  currentState.penStroker.strokeMatrix := currentState.penStroker.strokeMatrix * AffineMatrixSkewYRad(-angleRadCW);
     1572end;
     1573
     1574procedure TBGRACanvas2D.strokeResetTransform;
     1575begin
     1576  currentState.penStroker.strokeMatrix := AffineMatrixIdentity;
    14741577end;
    14751578
     
    18201923  arcTo(rx,ry,0,false,false,x+rx,y);
    18211924  closePath;
     1925end;
     1926
     1927procedure TBGRACanvas2D.openedSpline(const pts: array of TPointF;
     1928  style: TSplineStyle);
     1929var transf: array of TPointF;
     1930begin
     1931  if length(pts)=0 then exit;
     1932  transf := ApplyTransform(pts);
     1933  transf := BGRAPath.ComputeOpenedSpline(transf,style);
     1934  AddPoints(transf);
     1935  FLastCoord := pts[high(pts)];
     1936end;
     1937
     1938procedure TBGRACanvas2D.closedSpline(const pts: array of TPointF;
     1939  style: TSplineStyle);
     1940var transf: array of TPointF;
     1941begin
     1942  if length(pts)=0 then exit;
     1943  transf := ApplyTransform(pts);
     1944  transf := BGRAPath.ComputeClosedSpline(slice(transf, length(transf)-1),style);
     1945  AddPoints(transf);
     1946  FLastCoord := pts[high(pts)];
    18221947end;
    18231948
     
    20562181  if FPathPointCount = 0 then
    20572182  begin
    2058     currentState.clipMask.Fill(BGRABlack);
     2183    currentState.clipMaskReadWrite.Fill(BGRABlack);
    20592184    exit;
    20602185  end;
    2061   if currentState.clipMask = nil then
    2062     currentState.clipMask := surface.NewBitmap(width,height,BGRAWhite);
     2186  if currentState.clipMaskReadOnly = nil then
     2187    currentState.SetClipMask(surface.NewBitmap(width,height,BGRAWhite),True);
    20632188  tempBmp := surface.NewBitmap(width,height,BGRABlack);
    20642189  if antialiasing then
     
    20662191  else
    20672192    tempBmp.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
    2068   currentState.clipMask.BlendImage(0,0,tempBmp,boDarken);
     2193  currentState.clipMaskReadWrite.BlendImage(0,0,tempBmp,boDarken);
    20692194  tempBmp.Free;
    20702195end;
     
    20732198begin
    20742199  if FPathPointCount = 0 then exit;
    2075   if currentState.clipMask = nil then exit;
     2200  if currentState.clipMaskReadOnly = nil then exit;
    20762201  if antialiasing then
    2077     currentState.clipMask.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
     2202    currentState.clipMaskReadWrite.FillPolyAntialias(slice(FPathPoints,FPathPointCount),BGRAWhite)
    20782203  else
    2079     currentState.clipMask.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
    2080   if currentState.clipMask.Equals(BGRAWhite) then
    2081     FreeAndNil(currentState.clipMask);
     2204    currentState.clipMaskReadWrite.FillPoly(slice(FPathPoints,FPathPointCount),BGRAWhite,dmSet);
     2205  if currentState.clipMaskReadOnly.Equals(BGRAWhite) then
     2206    currentState.SetClipMask(nil,true);
    20822207end;
    20832208
  • GraphicTest/Packages/bgrabitmap/bgracolorint.pas

    r472 r494  
    2626operator *(const color1,color2: TColorInt65536): TColorInt65536;
    2727operator *(const color1: TColorInt65536; factor65536: integer): TColorInt65536;
    28 function ColorIntToBGRA(const AColor: TColorInt65536): TBGRAPixel;
    29 function BGRAToColorInt(const AColor: TBGRAPixel): TColorInt65536;
     28function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean = false): TBGRAPixel;
     29function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean = false): TColorInt65536;
    3030function BGRAToColorIntMultiply(const color1: TBGRAPixel; const color2: TColorInt65536): TColorInt65536;
    3131
     
    184184{$endif}
    185185
    186 function BGRAToColorInt(const AColor: TBGRAPixel): TColorInt65536;
    187 begin
    188   result.r := AColor.red shl 8 + AColor.red + (AColor.red shr 7);
    189   result.g := AColor.green shl 8 + AColor.green + (AColor.green shr 7);
    190   result.b := AColor.blue shl 8 + AColor.blue + (AColor.blue shr 7);
     186function BGRAToColorInt(const AColor: TBGRAPixel; AGammaExpansion: boolean): TColorInt65536;
     187begin
     188  if AGammaExpansion then
     189  begin
     190    result.r := GammaExpansionTab[AColor.red] + (AColor.red shr 7);
     191    result.g := GammaExpansionTab[AColor.green] + (AColor.green shr 7);
     192    result.b := GammaExpansionTab[AColor.blue] + (AColor.blue shr 7);
     193  end else
     194  begin
     195    result.r := AColor.red shl 8 + AColor.red + (AColor.red shr 7);
     196    result.g := AColor.green shl 8 + AColor.green + (AColor.green shr 7);
     197    result.b := AColor.blue shl 8 + AColor.blue + (AColor.blue shr 7);
     198  end;
    191199  result.a := AColor.alpha shl 8 + AColor.alpha+ (AColor.alpha shr 7);
    192200end;
     
    203211    mov ecx, [Color1]
    204212
    205     movzx eax, cl //b
     213    mov eax, ecx
     214    shr eax, TBGRAPixel_RedShift
     215    and eax, 255
     216    mov edx, eax
     217    shr edx, 7
     218    add eax, edx
     219    imul [esi]
     220    shl edx, 24
     221    shr eax, 8
     222    or edx, eax
     223    mov [ebx], edx
     224
     225    mov eax, ecx
     226    shr eax, TBGRAPixel_GreenShift
     227    and eax, 255
     228    mov edx, eax
     229    shr edx, 7
     230    add eax, edx
     231    imul [esi+4]
     232    shl edx, 24
     233    shr eax, 8
     234    or edx, eax
     235    mov [ebx+4], edx
     236
     237    mov eax, ecx
     238    shr eax, TBGRAPixel_BlueShift
     239    and eax, 255
    206240    mov edx, eax
    207241    shr edx, 7
     
    212246    or edx, eax
    213247    mov [ebx+8], edx
    214     shr ecx, 8
    215 
    216     movzx eax, cl //g
    217     mov edx, eax
    218     shr edx, 7
    219     add eax, edx
    220     imul [esi+4]
    221     shl edx, 24
    222     shr eax, 8
    223     or edx, eax
    224     mov [ebx+4], edx
    225     shr ecx, 8
    226 
    227     movzx eax, cl //r
    228     mov edx, eax
    229     shr edx, 7
    230     add eax, edx
    231     imul [esi]
    232     shl edx, 24
    233     shr eax, 8
    234     or edx, eax
    235     mov [ebx], edx
    236     shr ecx, 8
    237 
    238     movzx eax, cl //a
     248
     249    mov eax, ecx
     250    shr eax, TBGRAPixel_AlphaShift
     251    and eax, 255
    239252    mov edx, eax
    240253    shr edx, 7
     
    258271{$ENDIF}
    259272
    260 function ColorIntToBGRA(const AColor: TColorInt65536): TBGRAPixel;
     273function ColorIntToBGRA(const AColor: TColorInt65536; AGammaCompression: boolean): TBGRAPixel;
    261274var maxValue,invMaxValue,r,g,b: integer;
    262275begin
     
    280293  end;
    281294
    282   if maxValue <= 65535 then
    283   begin
    284     if AColor.r <= 0 then result.red := 0 else
    285       result.red := AColor.r shr 8 - (AColor.r shr 15);
    286 
    287     if AColor.g <= 0 then result.green := 0 else
    288       result.green := AColor.g shr 8 - (AColor.g shr 15);
    289 
    290     if AColor.b <= 0 then result.blue := 0 else
    291       result.blue := AColor.b shr 8 - (AColor.b shr 15);
    292     exit;
    293   end;
    294 
    295   invMaxValue := (1073741824+maxValue-1) div maxValue;
    296   maxValue := (maxValue-65535) shr 9;
    297   if AColor.r < 0 then r := 0 else
    298     r := AColor.r*invMaxValue shr 22 + maxValue;
    299   if AColor.g < 0 then g := 0 else
    300     g := AColor.g*invMaxValue shr 22 + maxValue;
    301   if AColor.b < 0 then b := 0 else
    302     b := AColor.b*invMaxValue shr 22 + maxValue;
    303 
    304   if r >= 255 then result.red := 255 else
    305     result.red := r;
    306   if g >= 255 then result.green := 255 else
    307       result.green := g;
    308   if b >= 255 then result.blue := 255 else
    309     result.blue := b;
     295  if AGammaCompression then
     296  begin
     297    if maxValue <= 65535 then
     298    begin
     299      if AColor.r <= 0 then result.red := 0 else
     300        result.red := GammaCompressionTab[AColor.r - (AColor.r shr 15)];
     301
     302      if AColor.g <= 0 then result.green := 0 else
     303        result.green :=GammaCompressionTab[AColor.g - (AColor.g shr 15)];
     304
     305      if AColor.b <= 0 then result.blue := 0 else
     306        result.blue := GammaCompressionTab[AColor.b - (AColor.b shr 15)];
     307      exit;
     308    end;
     309
     310    invMaxValue := (1073741824+maxValue-1) div maxValue;
     311
     312    maxValue := (maxValue-65535) shr 1;
     313    if AColor.r < 0 then r := maxValue else
     314      r := AColor.r*invMaxValue shr 14 + maxValue;
     315    if AColor.g < 0 then g := maxValue else
     316      g := AColor.g*invMaxValue shr 14 + maxValue;
     317    if AColor.b < 0 then b := maxValue else
     318      b := AColor.b*invMaxValue shr 14 + maxValue;
     319
     320    if r >= 65535 then result.red := 255 else
     321      result.red := GammaCompressionTab[r];
     322    if g >= 65535 then result.green := 255 else
     323        result.green := GammaCompressionTab[g];
     324    if b >= 65535 then result.blue := 255 else
     325      result.blue := GammaCompressionTab[b];
     326  end else
     327  begin
     328    if maxValue <= 65535 then
     329    begin
     330      if AColor.r <= 0 then result.red := 0 else
     331        result.red := AColor.r shr 8 - (AColor.r shr 15);
     332
     333      if AColor.g <= 0 then result.green := 0 else
     334        result.green := AColor.g shr 8 - (AColor.g shr 15);
     335
     336      if AColor.b <= 0 then result.blue := 0 else
     337        result.blue := AColor.b shr 8 - (AColor.b shr 15);
     338      exit;
     339    end;
     340
     341    invMaxValue := (1073741824+maxValue-1) div maxValue;
     342
     343    maxValue := (maxValue-65535) shr 9;
     344    if AColor.r < 0 then r := maxValue else
     345      r := AColor.r*invMaxValue shr 22 + maxValue;
     346    if AColor.g < 0 then g := maxValue else
     347      g := AColor.g*invMaxValue shr 22 + maxValue;
     348    if AColor.b < 0 then b := maxValue else
     349      b := AColor.b*invMaxValue shr 22 + maxValue;
     350
     351    if r >= 255 then result.red := 255 else
     352      result.red := r;
     353    if g >= 255 then result.green := 255 else
     354        result.green := g;
     355    if b >= 255 then result.blue := 255 else
     356      result.blue := b;
     357  end;
    310358end;
    311359
  • GraphicTest/Packages/bgrabitmap/bgracolorquantization.pas

    r472 r494  
    3333  { TBGRAColorQuantizer }
    3434
    35   TBGRAColorQuantizer = class
     35  TBGRAColorQuantizer = class(TBGRACustomColorQuantizer)
    3636  private
    3737    FColors: ArrayOfWeightedColor;
     
    4040    FReductionKeepContrast: boolean;
    4141    FSeparateAlphaChannel: boolean;
    42     function GetPalette: TBGRAApproxPalette;
    43     function GetSourceColor(AIndex: integer): TBGRAPixel;
    44     function GetSourceColorCount: Integer;
    4542    procedure Init(ABox: TBGRAColorBox);
    46     procedure SetReductionColorCount(AValue: Integer);
    4743    procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; ARedBounds, AGreenBounds, ABlueBounds, AAlphaBounds: TDimensionMinMax; AUniform: boolean);
    4844    procedure NormalizeArrayOfColors(AColors: ArrayOfTBGRAPixel; AColorBounds, AAlphaBounds: TDimensionMinMax);
     45  protected
     46    function GetPalette: TBGRACustomApproxPalette; override;
     47    function GetSourceColor(AIndex: integer): TBGRAPixel; override;
     48    function GetSourceColorCount: Integer; override;
     49    function GetReductionColorCount: integer; override;
     50    procedure SetReductionColorCount(AValue: Integer); override;
    4951  public
    50     constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); overload;
    51     constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload;
    52     constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); overload;
    53     constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); overload;
     52    constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); override;
     53    constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); override;
     54    constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); override;
     55    constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); override;
    5456    destructor Destroy; override;
    55     procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect);
    56     procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap);
    57     function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
    58     function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
    59     procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string);
    60     procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string; AFormat: TBGRAImageFormat);
    61     procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat);
    62     property SourceColorCount: Integer read GetSourceColorCount;
    63     property SourceColor[AIndex: integer]: TBGRAPixel read GetSourceColor;
    64     property ReductionColorCount: Integer read FReductionColorCount write SetReductionColorCount;
    65     property ReducedPalette: TBGRAApproxPalette read GetPalette;
     57    procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); override;
     58    function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; override;
     59    function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm;
     60      ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; override;
     61    procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm;
     62      ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); override;
    6663  end;
    6764
     
    7168  private
    7269    FTree: TBGRAColorTree;
    73     FColors: ArrayOfTBGRAPixel;
     70    FColors: ArrayOfWeightedColor;
    7471  protected
    7572    function GetCount: integer; override;
    7673    function GetColorByIndex(AIndex: integer): TBGRAPixel; override;
     74    function GetWeightByIndex(AIndex: Integer): UInt32; override;
    7775    procedure Init(const AColors: ArrayOfTBGRAPixel);
    7876  public
     
    9997    end;
    10098    FLargerOwned: boolean;
     99    FTransparentColorIndex: integer;
    101100  protected
    102101    function FindNearestLargerColorIndex(AValue: TBGRAPixel): integer; virtual;
     
    107106    function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override;
    108107    function FindNearestColorIndex(AValue: TBGRAPixel): integer; override;
    109   end;
    110 
    111   TIsChannelStrictlyGreaterFunc = function (p1,p2 : PBGRAPixel): boolean;
     108    function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
     109  end;
     110
     111  TIsChannelStrictlyGreaterFunc = TBGRAPixelComparer;
    112112  TIsChannelGreaterThanOrEqualToValueFunc = function (p : PBGRAPixel; v: UInt32): boolean;
    113113
     
    122122    FColors: ArrayOfWeightedColor;
    123123    FDimensions: TColorDimensions;
    124     FHasPureTransparentColor: boolean;
     124    FPureTransparentColorCount: integer;
    125125    function GetApparentInterval(ADimension: TColorDimension): UInt32;
    126126    function GetAverageColor: TBGRAPixel;
     
    128128    function GetBounds(ADimension: TColorDimension): TDimensionMinMax;
    129129    function GetColorCount(ACountPureTransparent: boolean): integer;
     130    function GetHasPureTransparentColor: boolean;
    130131    function GetInferiorColor: TBGRAPixel;
    131132    function GetLargestApparentDimension: TColorDimension;
     
    135136    procedure Init(AColors: ArrayOfWeightedColor; AOwner: boolean);
    136137    procedure SortBy(ADimension: TColorDimension);
    137     procedure InsertionSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, AMaxIndex: NativeInt);
    138     procedure QuickSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex, AMaxIndex: NativeInt);
    139138    function GetMedianIndex(ADimension : TColorDimension; AMinValue, AMaxValue: UInt32): integer;
    140139  public
    141140    constructor Create(ADimensions: TColorDimensions; AColors: ArrayOfWeightedColor; AOwner: boolean); overload;
    142     constructor Create(ADimensions: TColorDimensions; AColors: ArrayOfTBGRAPixel); overload;
     141    constructor Create(ADimensions: TColorDimensions; const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette); overload;
    143142    constructor Create(ADimensions: TColorDimensions; ABounds: TColorBoxBounds); overload;
    144143    constructor Create(ADimensions: TColorDimensions; APalette: TBGRACustomPalette); overload;
    145144    constructor Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); overload;
     145    constructor Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption); overload;
    146146    function BoundsContain(AColor: TBGRAPixel): boolean;
    147147    function MedianCut(ADimension: TColorDimension; out SuperiorMiddle: UInt32): TBGRAColorBox;
     
    159159    property TotalWeight: UInt32 read FTotalWeight;
    160160    property ColorCount[ACountPureTransparent: boolean]: integer read GetColorCount;
    161     property HasPureTransparentColor: boolean read FHasPureTransparentColor;
     161    property HasPureTransparentColor: boolean read GetHasPureTransparentColor;
     162    property PureTransparentColorCount: integer read FPureTransparentColorCount;
    162163  end;
    163164
     
    180181    FAverageColor: TBGRAPixel;
    181182
    182     FHasPureTransparentColor: boolean;
     183    FPureTransparentColorCount: integer;
    183184    FPureTransparentColorIndex: integer;
    184185    FDimension: TColorDimension;
     
    187188    FInferiorBranch, FSuperiorBranch: TBGRAColorTree;
    188189    function GetApproximatedColorCount: integer;
     190    function GetHasPureTransparentColor: boolean;
    189191    function GetLeafCount: integer;
    190192    procedure Init(ALeaf: TBGRAColorBox; AOwned: boolean);
     
    206208    function ApproximateColorIndex(AColor: TBGRAPixel): integer;
    207209    function GetAsArrayOfApproximatedColors: ArrayOfTBGRAPixel;
     210    function GetAsArrayOfWeightedColors: ArrayOfWeightedColor;
    208211    procedure SplitIntoPalette(ACount: integer; AMethod: TBiggestLeafMethod;
    209212      ALeafColor: TBGRALeafColorMode);
     
    212215    property LeafCount: integer read GetLeafCount;
    213216    property ApproximatedColorCount: integer read GetApproximatedColorCount;
    214     property HasPureTransparentColor: boolean read FHasPureTransparentColor;
     217    property HasPureTransparentColor: boolean read GetHasPureTransparentColor;
     218    property PureTransparentColorCount: integer read FPureTransparentColorCount;
    215219  end;
    216220
     
    224228implementation
    225229
    226 uses BGRADithering, lazutf8classes, FPimage, FPWriteBMP, FPWritePNG;
     230uses BGRADithering, FPimage, FPWriteBMP, BGRAWritePNG;
    227231
    228232const MedianMinPercentage = 0.2;
     
    533537
    534538const
    535   InsertionSortLimit = 10;
    536539  ApproxPaletteDimensions = [cdAlpha,cdRInvG,cdGInvB,cdRInvB,cdRInvGB,cdGInvRB,cdBInvRG,cdRGB];
    537540
     
    551554  if AValue.alpha = 0 then
    552555  begin
    553     result := -1;
     556    result := FTransparentColorIndex;
    554557    exit;
    555558  end;
    556   diff := BGRAWordDiff(AValue, FColors[0]);
     559  diff := BGRAWordDiff(AValue, FColors[0].Color);
    557560  result := 0;
    558561  for i := 0 to high(FColors) do
    559562  begin
    560     curDiff := BGRAWordDiff(AValue, FColors[i]);
     563    curDiff := BGRAWordDiff(AValue, FColors[i].Color);
    561564    if curDiff < diff then
    562565    begin
     
    570573  const AColors: ArrayOfTBGRAPixel; ALarger: TBGRACustomApproxPalette; ALargerOwned: boolean);
    571574var i: integer;
     575  largeWeighted: ArrayOfWeightedColor;
    572576begin
    573577  inherited Create(AColors);
     578  FTransparentColorIndex:= -1;
     579  for i := 0 to high(FColors) do
     580  begin
     581    FColors[i].Weight := 0;
     582    if FColors[i].Color.alpha = 0 then FTransparentColorIndex:= i;
     583  end;
    574584  FLarger := ALarger;
    575585  FLargerOwned := ALargerOwned;
    576   setlength(FLargerColors, FLarger.Count);
     586  largeWeighted := FLarger.GetAsArrayOfWeightedColor;
     587  setlength(FLargerColors, length(largeWeighted));
    577588  for i := 0 to high(FLargerColors) do
    578589  with FLargerColors[i] do
    579590  begin
    580     approxColorIndex := SlowFindNearestColorIndex(FLarger.Color[i]);
     591    approxColorIndex := SlowFindNearestColorIndex(largeWeighted[i].Color);
    581592    if approxColorIndex = -1 then
    582593      approxColor := BGRAPixelTransparent
    583594    else
    584       approxColor := FColors[approxColorIndex];
     595    begin
     596      approxColor := FColors[approxColorIndex].Color;
     597      inc(FColors[approxColorIndex].Weight, largeWeighted[i].Weight);
     598    end;
    585599  end;
    586600end;
     
    614628end;
    615629
     630function TBGRAApproxPaletteViaLargerPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
     631var
     632  i: Integer;
     633begin
     634  setlength(result, length(FColors));
     635  for i := 0 to high(FColors) do
     636    result[i] := FColors[i];
     637end;
     638
    616639{ TBGRAApproxPalette }
    617640
     
    625648  if (AIndex < 0) or (AIndex >= length(FColors)) then
    626649    raise ERangeError.Create('Index out of bounds');
    627   result := FColors[AIndex];
     650  result := FColors[AIndex].Color;
     651end;
     652
     653function TBGRAApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32;
     654begin
     655  if (AIndex < 0) or (AIndex >= length(FColors)) then
     656    raise ERangeError.Create('Index out of bounds');
     657  result := FColors[AIndex].Weight;
    628658end;
    629659
     
    643673  FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage);
    644674
    645   FColors := FTree.GetAsArrayOfApproximatedColors;
     675  FColors := FTree.GetAsArrayOfWeightedColors;
    646676end;
    647677
     
    656686  FTree.SplitIntoPalette(length(AColors),blApparentInterval,lcAverage);
    657687
    658   FColors := FTree.GetAsArrayOfApproximatedColors;
     688  FColors := FTree.GetAsArrayOfWeightedColors;
    659689end;
    660690
     
    662692begin
    663693  FTree := AOwnedSplitTree;
    664   FColors := FTree.GetAsArrayOfApproximatedColors;
     694  FColors := FTree.GetAsArrayOfWeightedColors;
    665695end;
    666696
     
    679709begin
    680710  result := FTree.ApproximateColorIndex(AValue);
    681   if (result <> -1) and not (DWord(FColors[result]) = DWord(AValue)) then result := -1;
     711  if (result <> -1) and not (DWord(FColors[result].Color) = DWord(AValue)) then result := -1;
    682712end;
    683713
     
    698728  setlength(result, length(FColors));
    699729  for i := 0 to high(result) do
    700     result[i] := FColors[i];
     730    result[i] := FColors[i].Color;
    701731end;
    702732
     
    705735  i: NativeInt;
    706736begin
    707   setlength(result, length(FColors));
    708   for i := 0 to high(result) do
    709   with result[i] do
    710   begin
    711     Color := FColors[i];
    712     Weight:= 1;
     737  if Assigned(FTree) then
     738    result := FTree.GetAsArrayOfWeightedColors
     739  else
     740  begin
     741    setlength(result, length(FColors));
     742    for i := 0 to high(result) do
     743      result[i] := FColors[i];
    713744  end;
    714745end;
     
    719750begin
    720751  FColors := ABox.FColors;
    721   if ABox.FHasPureTransparentColor then
     752  if ABox.HasPureTransparentColor then
    722753  begin
    723754    setlength(FColors,length(FColors)+1);
     
    725756    begin
    726757      Color := BGRAPixelTransparent;
    727       Weight:= 1;
     758      Weight:= ABox.PureTransparentColorCount;
    728759    end;
    729760  end;
     
    853884end;
    854885
    855 function TBGRAColorQuantizer.GetPalette: TBGRAApproxPalette;
     886function TBGRAColorQuantizer.GetReductionColorCount: integer;
     887begin
     888  result := FReductionColorCount;
     889end;
     890
     891function TBGRAColorQuantizer.GetPalette: TBGRACustomApproxPalette;
    856892var
    857893  tree: TBGRAColorTree;
    858894
    859895  procedure MakeTreeErrorDiffusionFriendly;
    860   var moreColors: ArrayOfTBGRAPixel;
     896  var moreColors: ArrayOfWeightedColor;
    861897    box: TBGRAColorBox;
    862898  begin
    863     moreColors := tree.GetAsArrayOfApproximatedColors;
     899    moreColors := tree.GetAsArrayOfWeightedColors;
    864900    tree.free;
    865     box := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],moreColors);
     901    box := TBGRAColorBox.Create([cdRed,cdGreen,cdBlue,cdAlpha],moreColors,True);
    866902    tree := TBGRAColorTree.Create(box,True);
    867903    tree.SplitIntoPalette(box.ColorCount[true], blApparentInterval, lcAverage);
     
    889925    bounds[cdBlue] := originalBox.Bounds[cdBlue];
    890926    bounds[cdAlpha] := originalBox.Bounds[cdAlpha];
    891     if originalBox.FHasPureTransparentColor then bounds[cdAlpha].Minimum := 0;
     927    if originalBox.HasPureTransparentColor then bounds[cdAlpha].Minimum := 0;
    892928    if FReductionColorCount = 1 then
    893929    begin
     
    9711007end;
    9721008
    973 procedure TBGRAColorQuantizer.ApplyDitheringInplace(
    974   AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap);
    975 begin
    976   ApplyDitheringInplace(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));
    977 end;
    978 
    9791009function TBGRAColorQuantizer.GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm;
    9801010  ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     
    9861016end;
    9871017
    988 function TBGRAColorQuantizer.GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm;
    989   ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
    990 var task: TDitheringTask;
    991 begin
    992   task := CreateDitheringTask(AAlgorithm, ABitmap, ReducedPalette, FSeparateAlphaChannel);
    993   result := task.Execute;
    994   task.Free;
    995 end;
    996 
    997 procedure TBGRAColorQuantizer.SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
    998   AFilenameUTF8: string);
    999 begin
    1000   SaveBitmapToFile(AAlgorithm, ABitmap, AFilenameUTF8, SuggestImageFormat(AFilenameUTF8));
    1001 end;
    1002 
    1003 procedure TBGRAColorQuantizer.SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
    1004   AFilenameUTF8: string; AFormat: TBGRAImageFormat);
     1018function TBGRAColorQuantizer.GetDitheredBitmapIndexedData(
     1019  ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
     1020  out AScanlineSize: PtrInt): Pointer;
    10051021var
    1006   stream: TFileStreamUTF8;
    1007 begin
    1008    stream := TFileStreamUTF8.Create(AFilenameUTF8,fmCreate);
    1009    try
    1010      SaveBitmapToStream(AAlgorithm, ABitmap, stream, AFormat);
    1011    finally
    1012      stream.Free;
    1013    end;
     1022  indexer: TDitheringToIndexedImage;
     1023begin
     1024  indexer := TDitheringToIndexedImage.Create(ReducedPalette, FSeparateAlphaChannel, ABitDepth, AByteOrder);
     1025  indexer.DefaultTransparentColorIndex := ReducedPalette.IndexOfColor(BGRAPixelTransparent);
     1026  AScanlineSize:= indexer.ComputeMinimumScanlineSize(ABitmap.Width);
     1027  result := indexer.DitherImage(AAlgorithm, ABitmap, AScanlineSize);
     1028  indexer.Free;
    10141029end;
    10151030
     
    10281043    writer := CreateBGRAImageWriter(AFormat, hasTransp);
    10291044    try
    1030       if writer is TFPWriterPNG then TFPWriterPNG(writer).Indexed := true else
     1045      if writer is TBGRAWriterPNG then TBGRAWriterPNG(writer).Indexed := true else
    10311046      if writer is TFPWriterBMP then
    10321047      begin
     
    10921107begin
    10931108  index := 0;
    1094   if FHasPureTransparentColor then
     1109  if HasPureTransparentColor then
    10951110  begin
    10961111    FPureTransparentColorIndex:= index;
     
    12291244  begin
    12301245    CheckColorComputed;
    1231     setlength(result,1+byte(FHasPureTransparentColor));
     1246    setlength(result,1+byte(HasPureTransparentColor));
    12321247    idx := 0;
    1233     if FHasPureTransparentColor then
     1248    if HasPureTransparentColor then
    12341249    begin
    12351250      result[idx] := BGRAPixelTransparent;
     
    12411256    a := FInferiorBranch.GetAsArrayOfApproximatedColors;
    12421257    b := FSuperiorBranch.GetAsArrayOfApproximatedColors;
    1243     setlength(result, length(a)+length(b)+byte(FHasPureTransparentColor));
     1258    setlength(result, length(a)+length(b)+byte(HasPureTransparentColor));
    12441259    idx := 0;
    1245     if FHasPureTransparentColor then
     1260    if HasPureTransparentColor then
    12461261    begin
    12471262      result[idx] := BGRAPixelTransparent;
     1263      inc(idx);
     1264    end;
     1265    for i := 0 to high(a) do
     1266    begin
     1267      result[idx] := a[i];
     1268      inc(idx);
     1269    end;
     1270    for i := 0 to high(b) do
     1271    begin
     1272      result[idx] := b[i];
     1273      inc(idx);
     1274    end;
     1275  end;
     1276end;
     1277
     1278function TBGRAColorTree.GetAsArrayOfWeightedColors: ArrayOfWeightedColor;
     1279var a,b: ArrayOfWeightedColor;
     1280  idx,i: integer;
     1281begin
     1282  if IsLeaf then
     1283  begin
     1284    CheckColorComputed;
     1285    setlength(result,1+byte(HasPureTransparentColor));
     1286    idx := 0;
     1287    if HasPureTransparentColor then
     1288    begin
     1289      result[idx].Color := BGRAPixelTransparent;
     1290      result[idx].Weight := PureTransparentColorCount;
     1291      inc(idx);
     1292    end;
     1293    result[idx].Color := FLeafColor;
     1294    result[idx].Weight := Weight;
     1295  end else
     1296  begin
     1297    a := FInferiorBranch.GetAsArrayOfWeightedColors;
     1298    b := FSuperiorBranch.GetAsArrayOfWeightedColors;
     1299    setlength(result, length(a)+length(b)+byte(HasPureTransparentColor));
     1300    idx := 0;
     1301    if HasPureTransparentColor then
     1302    begin
     1303      result[idx].Color := BGRAPixelTransparent;
     1304      result[idx].Weight := PureTransparentColorCount;
    12481305      inc(idx);
    12491306    end;
     
    13281385    if Assigned(FSuperiorBranch) then result += FSuperiorBranch.ApproximatedColorCount;
    13291386  end;
    1330   if FHasPureTransparentColor then inc(result);
     1387  if HasPureTransparentColor then inc(result);
     1388end;
     1389
     1390function TBGRAColorTree.GetHasPureTransparentColor: boolean;
     1391begin
     1392  result := FPureTransparentColorCount > 0;
    13311393end;
    13321394
     
    13471409    FMaxBorder[c] := true;
    13481410  end;
    1349   FHasPureTransparentColor:= FLeaf.HasPureTransparentColor;
     1411  FPureTransparentColorCount:= FLeaf.PureTransparentColorCount;
    13501412  FPureTransparentColorIndex:= -1;
    13511413end;
     
    14061468      else
    14071469        result := supLeaf;
    1408     blMix:
     1470    else{blMix:}
    14091471      if (sqrt(infLeaf.Weight/FWeight)*(infLeaf.LargestApparentInterval/LargestApparentInterval) >=
    14101472          sqrt(supLeaf.Weight/FWeight)*(supLeaf.LargestApparentInterval/LargestApparentInterval) ) then
     
    15331595begin
    15341596  result := length(FColors);
    1535   if ACountPureTransparent and FHasPureTransparentColor then inc(result);
     1597  if ACountPureTransparent and HasPureTransparentColor then inc(result);
     1598end;
     1599
     1600function TBGRAColorBox.GetHasPureTransparentColor: boolean;
     1601begin
     1602  result := FPureTransparentColorCount > 0;
    15361603end;
    15371604
     
    16671734  c: TColorDimension;
    16681735begin
    1669   FHasPureTransparentColor:= false;
     1736  FPureTransparentColorCount:= 0;
    16701737  FTotalWeight:= 0;
    16711738  for c := low(TColorDimension) to high(TColorDimension) do
     
    16971764      inc(idx);
    16981765    end else
    1699       FHasPureTransparentColor:= true;
     1766      inc(FPureTransparentColorCount, Weight);
    17001767  end;
    17011768  setlength(FColors,idx);
     
    17071774  comparer := GetPixelStrictComparer(ADimension);
    17081775  if comparer = nil then exit;
    1709   if Length(FColors) > InsertionSortLimit then
    1710     QuickSort(comparer,0,high(FColors))
    1711   else
    1712     InsertionSort(comparer,0,high(FColors));
    1713 end;
    1714 
    1715 procedure TBGRAColorBox.InsertionSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex,
    1716   AMaxIndex: NativeInt);
    1717 var i,j,insertPos: NativeInt;
    1718   compared: TBGRAWeightedPaletteEntry;
    1719 begin
    1720   for i := AMinIndex+1 to AMaxIndex do
    1721   begin
    1722     insertPos := i;
    1723     compared := FColors[i];
    1724     while (insertPos > AMinIndex) and AComparer(@FColors[insertPos-1].Color,@compared.Color) do
    1725       dec(insertPos);
    1726     if insertPos <> i then
    1727     begin
    1728       for j := i downto insertPos+1 do
    1729         FColors[j] := FColors[j-1];
    1730       FColors[insertPos] := compared;
    1731     end;
    1732   end;
    1733 end;
    1734 
    1735 procedure TBGRAColorBox.QuickSort(AComparer: TIsChannelStrictlyGreaterFunc; AMinIndex,
    1736   AMaxIndex: NativeInt);
    1737 var Pivot: TBGRAPixel;
    1738   CurMin,CurMax,i : NativeInt;
    1739 
    1740   procedure Swap(a,b: NativeInt);
    1741   var Temp: TBGRAWeightedPaletteEntry;
    1742   begin
    1743     if a = b then exit;
    1744     Temp := FColors[a];
    1745     FColors[a] := FColors[b];
    1746     FColors[b] := Temp;
    1747   end;
    1748 begin
    1749   if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then
    1750   begin
    1751     InsertionSort(AComparer,AMinIndex,AMaxIndex);
    1752     exit;
    1753   end;
    1754   Pivot := FColors[(AMinIndex+AMaxIndex) shr 1].Color;
    1755   CurMin := AMinIndex;
    1756   CurMax := AMaxIndex;
    1757   i := CurMin;
    1758   while i < CurMax do
    1759   begin
    1760     if AComparer(@FColors[i].Color, @Pivot) then
    1761     begin
    1762       Swap(i, CurMax);
    1763       dec(CurMax);
    1764     end else
    1765     begin
    1766       if AComparer(@Pivot, @FColors[i].Color) then
    1767       begin
    1768         Swap(i, CurMin);
    1769         inc(CurMin);
    1770       end;
    1771       inc(i);
    1772     end;
    1773   end;
    1774   if AComparer(@Pivot, @FColors[i].Color) then
    1775   begin
    1776     Swap(i, CurMin);
    1777     inc(CurMin);
    1778   end;
    1779   if CurMin > AMinIndex then QuickSort(AComparer,AMinIndex,CurMin);
    1780   if CurMax < AMaxIndex then QuickSort(AComparer,CurMax,AMaxIndex);
     1776  ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),comparer)
    17811777end;
    17821778
     
    18421838
    18431839constructor TBGRAColorBox.Create(ADimensions: TColorDimensions;
    1844   AColors: ArrayOfTBGRAPixel);
     1840  const AColors: ArrayOfTBGRAPixel; AAlpha: TAlphaChannelPaletteOption = acFullChannelInPalette);
    18451841var weightedColors: ArrayOfWeightedColor;
    18461842  i: Integer;
    18471843begin
    1848   FDimensions:= ADimensions;
    1849   setlength(weightedColors, length(AColors));
    1850   for i := 0 to high(weightedColors) do
    1851   with weightedColors[i] do
    1852   begin
    1853     color := AColors[i];
    1854     Weight:= 1;
    1855   end;
    1856   Init(weightedColors,True);
     1844  if AAlpha = acFullChannelInPalette then
     1845  begin
     1846    FDimensions:= ADimensions;
     1847    setlength(weightedColors, length(AColors));
     1848    for i := 0 to high(weightedColors) do
     1849    with weightedColors[i] do
     1850    begin
     1851      color := AColors[i];
     1852      Weight:= 1;
     1853    end;
     1854    Init(weightedColors,True);
     1855  end else
     1856    Create(ADimensions, @AColors[0], length(AColors), AAlpha);
    18571857end;
    18581858
     
    18621862  FBounds := ABounds;
    18631863  FTotalWeight:= 0;
    1864   FHasPureTransparentColor:= false;
     1864  FPureTransparentColorCount:= 0;
    18651865end;
    18661866
     
    18711871end;
    18721872
    1873 constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption);
     1873constructor TBGRAColorBox.Create(ADimensions: TColorDimensions;
     1874  ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption);
     1875begin
     1876  Create(ADimensions, ABitmap.Data, ABitmap.NbPixels, AAlpha);
     1877end;
     1878
     1879constructor TBGRAColorBox.Create(ADimensions: TColorDimensions; AColors: PBGRAPixel; ANbPixels: integer; AAlpha: TAlphaChannelPaletteOption);
    18741880var i,j,prev,idx: integer;
    18751881  p: PBGRAPixel;
    18761882  skip: boolean;
    18771883  alphaMask: DWord;
    1878   transp: boolean;
     1884  transpIndex: integer;
    18791885begin
    18801886  if AAlpha <> acFullChannelInPalette then
     
    18831889    alphaMask := 0;
    18841890  FDimensions:= ADimensions;
    1885   transp := false;
    1886   SetLength(FColors,ABitmap.NbPixels);
     1891  transpIndex := -1;
     1892  SetLength(FColors,ANbPixels);
    18871893  if length(FColors)>0 then
    18881894  begin
    1889     p := ABitmap.Data;
     1895    p := AColors;
    18901896    idx := 0;
    1891     for i := 0 to ABitmap.NbPixels-1 do
     1897    for i := 0 to ANbPixels-1 do
    18921898    begin
    18931899      if (p^.alpha = 0) or ((AAlpha = acTransparentEntry) and (p^.alpha < 128)) then
    18941900      begin
    18951901        skip := true;
    1896         if not transp and not (AAlpha = acIgnore) then
     1902        if not (AAlpha = acIgnore) then
    18971903        begin
    1898           with FColors[idx] do
     1904          if (transpIndex=-1) then
    18991905          begin
    1900             Color := BGRAPixelTransparent;
    1901             Weight:= 1;
    1902           end;
    1903           transp := true;
    1904           inc(idx);
     1906            transpIndex := idx;
     1907            with FColors[idx] do
     1908            begin
     1909              Color := BGRAPixelTransparent;
     1910              Weight:= 1;
     1911            end;
     1912            inc(idx);
     1913          end else
     1914            inc(FColors[transpIndex].Weight);
    19051915        end;
    19061916        if (p^.alpha = 0) then
     
    19381948    setLength(FColors, idx);
    19391949
    1940     QuickSort(@IsDWordGreater,0,high(FColors));
     1950    ArrayOfWeightedColor_QuickSort(FColors,0,high(FColors),@IsDWordGreater);
    19411951    prev := 0;
    19421952    for i := 1 to high(FColors) do
     
    20062016var i,idx: integer;
    20072017begin
    2008   if AIncludePureTransparent and FHasPureTransparentColor then
     2018  if AIncludePureTransparent and HasPureTransparentColor then
    20092019  begin
    20102020    setlength(result, length(FColors)+1);
  • GraphicTest/Packages/bgrabitmap/bgracompressablebitmap.pas

    r472 r494  
    2727
    2828uses
    29   Classes, SysUtils, BGRABitmap, GraphType, zstream;
     29  Classes, SysUtils, BGRABitmapTypes, BGRABitmap, zstream;
    3030
    3131type
     
    6969implementation
    7070
    71 uses BGRABitmapTypes;
    72 
    7371// size of each chunk treated by Compress function
    7472const maxPartSize = 524288;
     
    113111      FUncompressedData.Read(UsedPart.Data^,UsedPart.NbPixels*Sizeof(TBGRAPixel));
    114112      if UsedPart.LineOrder <> FLineOrder then UsedPart.VerticalFlip;
     113      If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue;
    115114      result.PutImage(FBounds.Left,FBounds.Top,UsedPart,dmSet);
    116115      UsedPart.Free;
    117116    end;
    118117  end else
     118  begin
    119119    FUncompressedData.Read(result.Data^,result.NbPixels*Sizeof(TBGRAPixel));
     120    If TBGRAPixel_RGBAOrder then result.SwapRedBlue;
     121  end;
    120122end;
    121123
     
    305307  begin
    306308    UsedPart := Source.GetPart(FBounds) as TBGRABitmap;
     309    If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue;
    307310    FUncompressedData.Write(UsedPart.Data^,NbUsedPixels*Sizeof(TBGRAPixel));
    308311    FLineOrder := UsedPart.LineOrder;
     
    310313  end else
    311314  begin
     315    If TBGRAPixel_RGBAOrder then Source.SwapRedBlue;
    312316    FUncompressedData.Write(Source.Data^,Source.NbPixels*Sizeof(TBGRAPixel));
     317    If TBGRAPixel_RGBAOrder then Source.SwapRedBlue;
    313318    FLineOrder := Source.LineOrder;
    314319  end;
  • GraphicTest/Packages/bgrabitmap/bgracoordpool3d.pas

    r472 r494  
    212212  P := PBGRACoordData3D(FPoolData.Data);
    213213  {$IFDEF CPUI386}
     214  {$IFDEF BGRASSE_AVAILABLE}
    214215  {$asmmode intel}
    215216  if UseSSE then
     
    304305  else
    305306  {$ENDIF}
     307  {$ENDIF}
    306308  begin
    307309    i := UsedCapacity;
     
    385387  P := PBGRANormalData3D(FPoolData.Data);
    386388  {$IFDEF CPUI386}
     389  {$IFDEF BGRASSE_AVAILABLE}
    387390  {$asmmode intel}
    388391  if UseSSE then
     
    414417  else
    415418  {$ENDIF}
     419  {$ENDIF}
    416420  begin
    417421    i := UsedCapacity;
  • GraphicTest/Packages/bgrabitmap/bgradefaultbitmap.pas

    r472 r494  
    3333
    3434uses
    35   Classes, SysUtils, Types, FPImage, Graphics, BGRABitmapTypes, GraphType, FPImgCanv,
    36   BGRACanvas, BGRACanvas2D, FPWritePng, BGRAArrow, BGRAPen;
     35  SysUtils, Classes, Types, FPImage, BGRAGraphics, BGRABitmapTypes, FPImgCanv,
     36  BGRACanvas, BGRACanvas2D, BGRAArrow, BGRAPen, BGRATransform;
    3737
    3838type
     39  TBGRAPtrBitmap = class;
     40  {=== TBGRABitmap reference ===}
    3941  { TBGRADefaultBitmap }
    40 
     42  {* This class is the base for all ''TBGRABitmap'' classes. It implements most
     43     function to the exception from implementations specific to the
     44     widgetset }{ in the doc, it is presented as
     45  TBGRABitmap = class(TBGRACustomBitmap)
     46  }
    4147  TBGRADefaultBitmap = class(TBGRACustomBitmap)
    4248  private
     
    4753    function CheckVertLineBounds(var x, y, y2: int32or64; out delta: int32or64): boolean; inline;
    4854    function CheckRectBounds(var x,y,x2,y2: integer; minsize: integer): boolean; inline;
    49     function CheckClippedRectBounds(var x,y,x2,y2: integer): boolean; inline;
    5055    function CheckAntialiasRectBounds(var x,y,x2,y2: single; w: single): boolean;
    5156    function GetCanvasBGRA: TBGRACanvas;
    5257    function GetCanvas2D: TBGRACanvas2D;
     58    procedure GradientFillDithered(x, y, x2, y2: integer; c1, c2: TBGRAPixel;
     59      gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
     60      gammaColorCorrection: boolean = True; Sinus: Boolean=False;
     61      ditherAlgo: TDitheringAlgorithm = daFloydSteinberg);
     62    procedure GradientFillDithered(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
     63      gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
     64      Sinus: Boolean=False;
     65      ditherAlgo: TDitheringAlgorithm = daFloydSteinberg);
    5366  protected
    5467    FRefCount: integer; //reference counter (not related to interface reference counter)
     
    5770    FData:      PBGRAPixel;              //pointer to pixels
    5871    FWidth, FHeight, FNbPixels: integer; //dimensions
     72    FScanWidth, FScanHeight: integer;    //possibility to reduce the zone being scanned
    5973    FDataModified: boolean;              //if data image has changed so TBitmap should be updated
    6074    FLineOrder: TRawImageLineOrder;
     
    6579    FScanCurX,FScanCurY: integer;   //current scan coordinates
    6680
    67     //LCL bitmap object
     81    //GUI bitmap object
    6882    FBitmap:   TBitmap;
    6983    FBitmapModified: boolean;         //if TBitmap has changed so pixel data should be updated
     
    86100    FFontRenderer: TBGRACustomFontRenderer;
    87101
    88     { Pen style can be defined by PenStyle property of by CustomPenStyle property.
    89       When PenStyle property is assigned, CustomPenStyle property is assigned the actual
    90       pen pattern. }
    91     FCustomPenStyle:  TBGRAPenStyle;
    92     FPenStyle: TPenStyle;
    93     FArrow: TBGRAArrow;
    94     FLineCap: TPenEndCap;
     102    FPenStroker: TBGRAPenStroker;
    95103
    96104    //Pixel data
     
    98106    function GetScanLine(y: integer): PBGRAPixel; override; //don't forget to call InvalidateBitmap after modifications
    99107    function LoadFromRawImage(ARawImage: TRawImage; DefaultOpacity: byte;
    100       AlwaysReplaceAlpha: boolean = False; RaiseErrorOnInvalidPixelFormat: boolean = True): boolean;
     108      AlwaysReplaceAlpha: boolean = False; RaiseErrorOnInvalidPixelFormat: boolean = True): boolean; virtual; abstract;
    101109    function GetDataPtr: PBGRAPixel; override;
    102110    procedure ClearTransparentPixels; override;
    103111    function GetScanlineFast(y: integer): PBGRAPixel; inline;
    104112    function GetLineOrder: TRawImageLineOrder; override;
     113    procedure SetLineOrder(AValue: TRawImageLineOrder); virtual;
    105114    function GetNbPixels: integer; override;
    106115    function GetWidth: integer; override;
    107116    function GetHeight: integer; override;
    108117
    109     //LCL bitmap object
     118    //GUI bitmap object
    110119    function GetBitmap: TBitmap; override;
    111120    function GetCanvas: TCanvas; override;
     
    116125    function GetCanvasAlphaCorrection: boolean; override;
    117126    procedure SetCanvasAlphaCorrection(const AValue: boolean); override;
     127    procedure DoLoadFromBitmap; virtual;
    118128
    119129    //FreePascal drawing routines
     
    125135    procedure ReallocData; virtual;
    126136    procedure FreeData; virtual;
    127 
    128     procedure RebuildBitmap; virtual;
     137    function CreatePtrBitmap(AWidth,AHeight: integer; AData: PBGRAPixel): TBGRAPtrBitmap; virtual;
     138
     139    procedure RebuildBitmap; virtual; abstract;
    129140    procedure FreeBitmap; virtual;
    130141
     
    144155    function GetAverageColor: TColor; override;
    145156    function GetAveragePixel: TBGRAPixel; override;
    146     function CreateAdaptedPngWriter: TFPWriterPNG;
    147157
    148158    //drawing
     159    function GetPenJoinStyle: TPenJoinStyle; override;
     160    procedure SetPenJoinStyle(const AValue: TPenJoinStyle); override;
     161    function GetPenMiterLimit: single; override;
     162    procedure SetPenMiterLimit(const AValue: single); override;
    149163    function GetCustomPenStyle: TBGRAPenStyle; override;
    150164    procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); override;
     
    153167    function GetLineCap: TPenEndCap; override;
    154168    procedure SetLineCap(AValue: TPenEndCap); override;
     169    function GetPenStroker: TBGRACustomPenStroker; override;
     170
    155171    function GetArrowEndSize: TPointF; override;
    156172    function GetArrowStartSize: TPointF; override;
     
    173189    function GetFontRenderer: TBGRACustomFontRenderer; override;
    174190    procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); override;
     191    function CreateDefaultFontRenderer: TBGRACustomFontRenderer; virtual; abstract;
     192    function GetFontAnchorVerticalOffset: single;
     193    function GetFontAnchorRotatedOffset: TPointF;
     194    function GetFontAnchorRotatedOffset(ACustomOrientation: integer): TPointF;
    175195
    176196    function GetClipRect: TRect; override;
     
    179199    function InternalGetPixelCycle256(ix,iy: int32or64; iFactX,iFactY: int32or64): TBGRAPixel;
    180200    function InternalGetPixel256(ix,iy: int32or64; iFactX,iFactY: int32or64; smoothBorder: boolean): TBGRAPixel;
    181     function GetPolyLineOption: TBGRAPolyLineOptions;
    182201    function GetArrow: TBGRAArrow;
    183     procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override;
    184     procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); override;
     202    procedure InternalTextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single);
     203
     204    function CheckClippedRectBounds(var x,y,x2,y2: integer): boolean;
     205    procedure InternalArc(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single;
     206      AFillColor: TBGRAPixel; AOptions: TArcOptions; ADrawChord: boolean = false; ATexture: IBGRAScanner = nil); override;
    185207
    186208  public
    187     {Reference counter functions}
     209    {** Provides a canvas with opacity and antialiasing }
     210    property CanvasBGRA: TBGRACanvas read GetCanvasBGRA;
     211    {** Provides a canvas with 2d transformation and similar to HTML5. }
     212    property Canvas2D: TBGRACanvas2D read GetCanvas2D;
     213    {** For more properties, see parent class [[TBGRACustomBitmap and IBGRAScanner#TBGRACustomBitmap|TBGRACustomBitmap]] }
     214
     215    {==== Reference counting ====}
     216
     217    {** Adds a reference (this reference count is not the same as
     218        the reference count of an interface, it changes only by
     219        explicit calls }
    188220    function NewReference: TBGRACustomBitmap;
     221    {** Free a reference. When the resulting reference count gets
     222        to zero, the image is freed. The initial reference count
     223        is equal to 1 }
    189224    procedure FreeReference;
     225    {** Returns an object with a reference count equal to 1. Duplicate
     226        this bitmap if necessary }
    190227    function GetUnique: TBGRACustomBitmap;
    191228
     229    {==== Constructors ====}
     230
    192231    {------------------------- Constructors from TFPCustomImage----------------}
    193     constructor Create(AWidth, AHeight: integer); override; //Creates a new bitmap, initialize properties and bitmap data
    194     procedure SetSize(AWidth, AHeight: integer); override;  //Can only be called with an existing instance of TBGRABitmap.
    195                                                             //Sets the dimensions of an existing TBGRABitmap instance.
     232    {** Creates a new bitmap, initialize properties and bitmap data }
     233    constructor Create(AWidth, AHeight: integer); override;
     234    {** Can only be called with an existing instance of ''TBGRABitmap''.
     235        Sets the dimensions of an existing ''TBGRABitmap'' instance. }
     236    procedure SetSize(AWidth, AHeight: integer); override;
    196237
    197238    {------------------------- Constructors from TBGRACustomBitmap-------------}
    198     constructor Create; override;                    //Creates an image of width and height equal to zero.
    199     constructor Create(ABitmap: TBitmap); override;  //Creates an image of dimensions AWidth and AHeight and filled with transparent pixels.
    200     constructor Create(AWidth, AHeight: integer; Color: TColor); override;      //Creates an image of dimensions AWidth and AHeight and fills it with the opaque color Color.
    201     constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override;  //Creates an image of dimensions AWidth and AHeight and fills it with Color.
    202 
    203     constructor Create(AFilename: string); override; // Creates an image by loading its content from the file AFilename.
    204                                                      // The encoding of the string is the default one for the operating system.
    205                                                      // It is recommended to use the next constructor and UTF8 encoding.
    206 
    207     constructor Create(AFilename: string; AIsUtf8: boolean); override; //Creates an image by loading its content from the file AFilename.
    208                                                                        //The boolean AIsUtf8Filename specifies if UTF8 encoding is assumed for the filename.
    209 
    210     constructor Create(AStream: TStream); override;  // Creates an image by loading its content from the stream AStream.
    211     destructor Destroy; override;                    // Free the object and all its resources
     239    {** Creates an image of width and height equal to zero. In this case,
     240        ''Data'' = '''nil''' }
     241    constructor Create; override;
     242    {** Creates an image by copying the content of a ''TFPCustomImage'' }
     243    constructor Create(AFPImage: TFPCustomImage); override;
     244    {** Creates an image by copying the content of a ''TBitmap'' }
     245    constructor Create(ABitmap: TBitmap; AUseTransparent: boolean = true); override;
     246    {** Creates an image of dimensions ''AWidth'' and ''AHeight'' and fills it with the opaque color ''Color'' }
     247    constructor Create(AWidth, AHeight: integer; Color: TColor); override;
     248    {** Creates an image of dimensions ''AWidth'' and ''AHeight'' and fills it with ''Color'' }
     249    constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); override;
     250
     251    {** Creates an image by loading its content from the file ''AFilename''.
     252        The encoding of the string is the default one for the operating system.
     253        It is recommended to use the next constructor and UTF8 encoding }
     254    constructor Create(AFilename: string); override;
     255
     256    {** Creates an image by loading its content from the file ''AFilename''.
     257        The boolean ''AIsUtf8Filename'' specifies if UTF8 encoding is assumed
     258        for the filename }
     259    constructor Create(AFilename: string; AIsUtf8: boolean); override;
     260    constructor Create(AFilename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions); override;
     261
     262    {** Creates an image by loading its content from the stream ''AStream'' }
     263    constructor Create(AStream: TStream); override;
     264    {** Free the object and all its resources }
     265    destructor Destroy; override;
    212266
    213267    {------------------------- Quasi-constructors -----------------------------}
    214     function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override;  //Can only be called from an existing instance of TBGRABitmap.
    215                                                                                 //Creates a new instance with dimensions AWidth and AHeight,
    216                                                                                 //containing transparent pixels.
    217 
    218     function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override;  //Can only be called from an existing instance of TBGRABitmap.
    219                                                                                 //Creates a new instance with dimensions AWidth and AHeight,
    220                                                                                 //and fills it with Color.
    221 
    222     function NewBitmap(Filename: string): TBGRACustomBitmap; override;          //Can only be called from an existing instance of TBGRABitmap.
    223                                                                                 //Creates a new instance with by loading its content
    224                                                                                 //from the file Filename. The encoding of the string
    225                                                                                 //is the default one for the operating system.
    226 
    227     function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; override; //Can only be called from an existing instance of TBGRABitmap.
    228                                                                                 //Creates a new instance with by loading its content
    229                                                                                 //from the file Filename.
    230 
    231     procedure SaveToFile(const filename: string); override;
    232     procedure SaveToStreamAsPng(Str: TStream); override;
    233     procedure Assign(ARaster: TRasterImage); override; overload;
    234     procedure Assign(MemBitmap: TBGRACustomBitmap);override; overload;
     268    {** Can only be called from an existing instance of ''TBGRABitmap''.
     269        Creates a new instance with dimensions ''AWidth'' and ''AHeight'',
     270        containing transparent pixels. }
     271    function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; override;
     272
     273    {** Can only be called from an existing instance of ''TBGRABitmap''.
     274        Creates a new instance with dimensions ''AWidth'' and ''AHeight'',
     275        and fills it with Color }
     276    function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; override;
     277
     278    {** Can only be called from an existing instance of ''TBGRABitmap''.
     279        Creates a new instance with by loading its content
     280        from the file ''Filename''. The encoding of the string
     281        is the default one for the operating system }
     282    function NewBitmap(Filename: string): TBGRACustomBitmap; override;
     283
     284    {** Can only be called from an existing instance of ''TBGRABitmap''.
     285        Creates a new instance with by loading its content
     286        from the file ''Filename'' }
     287    function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; override;
     288    function NewBitmap(Filename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions): TBGRACustomBitmap; override;
     289
     290    {** Can only be called from an existing instance of ''TBGRABitmap''.
     291        Creates an image by copying the content of a ''TFPCustomImage'' }
     292    function NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap; override;
     293
     294    {** Load image from a stream. The specified image reader is used }
     295    procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader; AOptions: TBGRALoadingOptions); override;
     296
     297    {** Assign the content of the specified ''Source''. It can be a ''TBGRACustomBitmap'' or
     298        a ''TFPCustomImage'' }
     299    procedure Assign(Source: TPersistent); override;
     300    procedure Assign(Source: TBitmap; AUseTransparent: boolean); overload;
     301    {** Stores the image in the stream without compression nor header }
    235302    procedure Serialize(AStream: TStream); override;
     303    {** Reads the image in a stream that was previously serialized }
    236304    procedure Deserialize(AStream: TStream); override;
     305    {** Stores an empty image (of size zero) }
    237306    class procedure SerializeEmpty(AStream: TStream);
    238307
    239     {Pixel functions}
     308    {* Example:
     309       <syntaxhighlight>
     310     * var bmp1, bmp2: TBGRABitmap;
     311     * begin
     312     *   bmp1 := TBGRABitmap.Create(100,100);
     313     *   bmp2 := bmp1.NewBitmap(100,100) as TBGRABitmap;
     314     *   ...
     315     * end;</syntaxhighlight>
     316       See tutorial 2 on [[BGRABitmap_tutorial_2|how to load and display an image]].
     317     * See reference on [[TBGRACustomBitmap_and_IBGRAScanner#Load_and_save_files|loading and saving files]] }
     318
     319    {==== Pixel functions ====}
     320    {** Checks if the specified point is in the clipping rectangle ''ClipRect'' }
    240321    function PtInClipRect(x, y: int32or64): boolean; inline;
     322    {** Sets the pixel by replacing the content at (''x'',''y'') with the specified color.
     323        Alpha value is set to 255 (opaque) }
    241324    procedure SetPixel(x, y: int32or64; c: TColor); override;
     325    {** Sets the pixel at (''x'',''y'') with the specified content }
    242326    procedure SetPixel(x, y: int32or64; c: TBGRAPixel); override;
     327    {** Applies a logical '''xor''' to the content of the pixel with the specified value.
     328        This includes the alpha channel, so if you want to preserve the opacity, provide
     329        a color ''c'' with alpha channel equal to zero }
    243330    procedure XorPixel(x, y: int32or64; c: TBGRAPixel); override;
     331    {** Draws a pixel with gamma correction at (''x'',''y''). Pixel is supplied
     332        in sRGB colorspace }
    244333    procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); override;
     334    {** Draws a pixel with the specified ''ADrawMode'' at (''x'',''y'').
     335        Pixel is supplied in sRGB colorspace. Gamma correction may be applied
     336        depending on the draw mode }{inherited
     337    procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
     338  }{** Draws a pixel with gamma correction at (''x'',''y''). Pixel is supplied
     339        in gamma expanded colorspace }
    245340    procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); override;
     341    {** Draws a pixel without gamma correction at (''x'',''y''). Pixel is supplied
     342        in sRGB colorspace }
    246343    procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); override;
     344    {** Erase the content of the pixel by reducing the value of the
     345        alpha channel. ''alpha'' specifies how much to decrease.
     346        If the resulting alpha reaches zero, the content
     347        is replaced by ''BGRAPixelTransparent'' }
    247348    procedure ErasePixel(x, y: int32or64; alpha: byte); override;
     349    {** Sets the alpha value at (''x'',''y''). If ''alpha'' = 0, the
     350        pixel is replaced by ''BGRAPixelTransparent'' }
    248351    procedure AlphaPixel(x, y: int32or64; alpha: byte); override;
     352    {** Returns the content of the specified pixel. If it is out of the
     353        bounds of the picture, the result is ''BGRAPixelTransparent'' }
    249354    function GetPixel(x, y: int32or64): TBGRAPixel; override;
     355    {** Computes the value of the pixel at a floating point coordiante
     356        by interpolating the values of the pixels around it.
     357      * There is a one pixel wide margin around the pixel where the pixels are
     358        still considered inside. If ''smoothBorder'' is set to true, pixel fade
     359        to transparent.
     360      * If it is more out of the bounds, the result is ''BGRAPixelTransparent''.
     361      * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted
     362        values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' }
     363    function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override;
     364    {** Similar to previous ''GetPixel'' function, but the fractional part of
     365        the coordinate is supplied with a number from 0 to 255. The actual
     366        coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) }
    250367    function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override;
    251     function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; override;
     368    {** Computes the value of the pixel at a floating point coordiante
     369        by interpolating the values of the pixels around it. If the pixel
     370        is out of bounds, the image is repeated.
     371      * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted
     372        values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' }
    252373    function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
     374    {** Similar to previous ''GetPixel'' function, but the fractional part of
     375        the coordinate is supplied with a number from 0 to 255. The actual
     376        coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) }
     377    function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
     378    {** Computes the value of the pixel at a floating point coordiante
     379        by interpolating the values of the pixels around it. ''repeatX'' and
     380        ''repeatY'' specifies if the image is to be repeated or not.
     381      * ''AResampleFilter'' specifies how pixels must be interpolated. Accepted
     382        values are ''rfBox'', ''rfLinear'', ''rfHalfCosine'' and ''rfCosine'' }
    253383    function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override;
    254     function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; override;
     384    {** Similar to previous ''GetPixel'' function, but the fractional part of
     385        the coordinate is supplied with a number from 0 to 255. The actual
     386        coordinate is (''x'' + ''fracX256''/256, ''y'' + ''fracY256''/256) }
    255387    function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; override;
    256388
    257     {Line primitives}
     389    {==== Drawing lines and polylines (integer coordinates) ====}
     390    {* These functions do not take into account current pen style/cap/join.
     391       See [[BGRABitmap tutorial 13|coordinate system]]. }
     392
     393    {** Replaces the content of the pixels at line ''y'' and
     394        at columns ''x'' to ''x2'' included, using specified color }
    258395    procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     396    {** Applies xor to the pixels at line ''y'' and
     397        at columns ''x'' to ''x2'' included, using specified color.
     398        This includes the alpha channel, so if you want to preserve the
     399        opacity, provide a color ''c'' with alpha channel equal to zero }
    259400    procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     401    {** Draws an horizontal line with gamma correction at line ''y'' and
     402        at columns ''x'' to ''x2'' included, using specified color }
    260403    procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     404    {** Draws an horizontal line with gamma correction at line ''y'' and
     405        at columns ''x'' to ''x2'' included, using specified color }
    261406    procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); override;
     407    {** Draws an horizontal line with gamma correction at line ''y'' and
     408        at columns ''x'' to ''x2'' included, using specified scanner
     409        to get the source colors }{inherited
     410    procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload;
     411   }{** Draws an horizontal line without gamma correction at line ''y'' and
     412        at columns ''x'' to ''x2'' included, using specified color }
     413    procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     414    {** Draws an horizontal line at line ''y'' and
     415        at columns ''x'' to ''x2'' included, using specified scanner
     416        and the specified ''ADrawMode'' }
    262417    procedure HorizLine(x, y, x2: int32or64; texture: IBGRAScanner; ADrawMode : TDrawMode); override;
    263 
    264     procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); override;
     418    {** Draws an horizontal line at line ''y'' and
     419        at columns ''x'' to ''x2'' included, using specified color
     420        and the specified ''ADrawMode'' }{inherited
     421    procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
     422    }
     423    {** Replaces the alpha value of the pixels at line ''y'' and
     424        at columns ''x'' to ''x2'' included }
    265425    procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); override;
    266     procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
    267     procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
    268     procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
    269     procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); override;
    270     procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     426    {** Draws an horizontal line with gamma correction at line ''y'' and
     427        at columns ''x'' to ''x2'' included, using specified color,
     428        and with a transparency that increases with the color difference
     429        with ''compare''. If the difference is greater than ''maxDiff'',
     430        pixels are not changed }
    271431    procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel;
    272432      maxDiff: byte); override;
    273433
    274     {Shapes}
    275     procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); override;
    276     procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); override;
    277 
     434    {** Replaces a vertical line at column ''x'' and at row ''y'' to ''y2'' }
     435    procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     436    {** Xors a vertical line at column ''x'' and at row ''y'' to ''y2'' }
     437    procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     438    {** Draws a vertical line with gamma correction at column ''x'' and at row ''y'' to ''y2'' }
     439    procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     440    {** Draws a vertical line without gamma correction at column ''x'' and at row ''y'' to ''y2'' }
     441    procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); override;
     442    {** Replace alpha values in a vertical line at column ''x'' and at row ''y'' to ''y2'' }
     443    procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); override;
     444    {** Draws a vertical line with the specified draw mode at column ''x'' and at row ''y'' to ''y2'' }{inherited
     445    procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode);
     446    }
     447
     448    {** Draws an aliased line from (x1,y1) to (x2,y2) using Bresenham's algorithm
     449        ''c'' specifies the color. ''DrawLastPixel'' specifies if (x2,y2) must be drawn.
     450        ''ADrawMode'' specifies the mode to use when drawing the pixels }
    278451    procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     452    {** Draws an antialiased line from (x1,y1) to (x2,y2) using an improved version of Bresenham's algorithm
     453        ''c'' specifies the color. ''DrawLastPixel'' specifies if (x2,y2) must be drawn }
    279454    procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); override;
     455    {** Draws an antialiased line with two colors ''c1'' and ''c2'' as dashes of lenght ''dashLen'' }
    280456    procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); override;
     457    {** Draws an antialiased line with two colors ''c1'' and ''c2'' as dashes of lenght ''dashLen''.
     458        ''DashPos'' can be used to specify the start dash position and to retrieve the dash position at the end
     459        of the line, in order to draw a polyline with consistent dashes }
    281460    procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); override;
     461
     462    {** Erases the line from (x1,y1) to (x2,y2) using Bresenham's algorithm.
     463        ''alpha'' specifies how much to decrease. If ''alpha'' = 0, nothing
     464        is changed and if ''alpha'' = 255, all pixels become transparent.
     465        ''DrawListPixel'' specifies if (x2,y2) must be changed }
     466    procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override;
     467    {** Erases the line from (x1,y1) to (x2,y2) width antialiasing.
     468        ''alpha'' specifies how much to decrease. If ''alpha'' = 0, nothing
     469        is changed and if ''alpha'' = 255, all pixels become transparent.
     470        ''DrawListPixel'' specifies if (x2,y2) must be changed }
     471    procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override;
     472
     473    {==== Drawing lines and polylines (floating point coordinates) ====}
     474    {* These functions use the current pen style/cap/join. The parameter ''w''
     475       specifies the width of the line and the base unit for dashes.
     476       See [[BGRABitmap tutorial 13|coordinate system]]. }
     477
     478    {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join }
    282479    procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); override;
     480    {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join.
     481        ''texture'' specifies the source color to use when filling the line }
    283482    procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); override;
    284     procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); override;
    285     procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); override;
    286 
     483    {** Draws a line from (x1,y1) to (x2,y2) using current pen style/cap/join.
     484        ''Closed'' specifies if the end of the line is closed. If it is not closed,
     485        a space is left so that the next line can fit }
     486    procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; ClosedCap: boolean); override;
     487    {** Same as above with ''texture'' specifying the source color to use when filling the line }
     488    procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; ClosedCap: boolean); override;
     489
     490    {** Draws a polyline using current pen style/cap/join }
    287491    procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override;
     492    {** Draws a polyline using current pen style/cap/join.
     493        ''texture'' specifies the source color to use when filling the line }
    288494    procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override;
    289     procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); override;
     495    {** Draws a polyline using current pen style/cap/join.
     496        ''Closed'' specifies if the end of the line is closed. If it is not closed,
     497        a space is left so that the next line can fit }
     498    procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; ClosedCap: boolean); override;
     499    procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single; ClosedCap: boolean); override;
     500    {** Draws a polyline using current pen style/cap/join.
     501        ''fillcolor'' specifies a color to fill the polygon formed by the points }
    290502    procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override;
     503    {** Draws a polyline using current pen style/cap/join.
     504        The last point considered as a join with the first point if it has
     505        the same coordinate }
     506    procedure DrawPolyLineAntialiasAutocycle(const points: array of TPointF; c: TBGRAPixel; w: single); override;
     507    procedure DrawPolyLineAntialiasAutocycle(const points: array of TPointF; texture: IBGRAScanner; w: single); override;
     508    {** Draws a polygon using current pen style/cap/join.
     509        The polygon is always closed. You don't need to set the last point
     510        to be the same as the first point }
    291511    procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); override;
     512    {** Draws a polygon using current pen style/cap/join.
     513        The polygon is always closed. You don't need to set the last point
     514        to be the same as the first point }
    292515    procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); override;
     516    {** Draws a filled polygon using current pen style/cap/join.
     517        The polygon is always closed. You don't need to set the last point
     518        to be the same as the first point. }
    293519    procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); override;
    294520
    295     procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override;
    296     procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); override;
     521    {** Erases a line from (x1,y1) to (x2,y2) using current pen style/cap/join }
    297522    procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); override;
     523    {** Erases a line from (x1,y1) to (x2,y2) using current pen style/cap/join.
     524        ''Closed'' specifies if the end of the line is closed. If it is not closed,
     525        a space is left so that the next line can fit }
    298526    procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); override;
     527    {** Erases a polyline using current pen style/cap/join }
    299528    procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); override;
    300529
    301     procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); override;
    302     procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); override;
    303 
    304     procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override;
    305     procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override;
    306     procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); override;
    307     procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); override;
    308     procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); override;
    309 
    310     procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override;
    311     procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override;
    312     procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True); override;
    313     procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); override;
    314     procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
    315     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
    316     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override;
    317     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
    318     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override;
    319 
    320     procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); override;
    321     procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); override;
    322     procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel); override;
    323     procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
    324     procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
    325 
     530    {==== Rectangles (integer coordinates) ====}
     531    {* The integer coordinates of rectangles interpreted such that
     532       that the bottom/right pixels are not drawn. The width is equal
     533       to x2-x, and pixels are drawn from x to x2-1. If x = x2, then nothing
     534       is drawn. See [[BGRABitmap tutorial 13|coordinate system]].
     535     * These functions do not take into account current pen style/cap/join.
     536       They draw a continuous 1-pixel width border }
     537
     538    {** Draw a size border of a rectangle,
     539        using the specified ''mode'' }
     540    procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override;
     541    {** Draw a filled rectangle with a border of color ''BorderColor'',
     542        using the specified ''mode'' }
     543    procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); override;
     544    {** Fills completely a rectangle, without any border, with the specified ''mode'' }
     545    procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; overload;
     546    {** Fills completely a rectangle, without any border, with the specified ''texture'' and
     547        with the specified ''mode'' }
     548    procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint); override; overload;
     549    procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm); override; overload;
     550    {** Sets the alpha value within the specified rectangle }
     551    procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); override;
     552    {** Draws a filled round rectangle, with corners having an elliptical diameter of ''DX'' and ''DY'' }
     553    procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     554    {** Draws a round rectangle, with corners having an elliptical diameter of ''DX'' and ''DY'' }
     555    procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     556
     557    {==== Rectangles and ellipses (floating point coordinates) ====}
     558    {* These functions use the current pen style/cap/join. The parameter ''w''
     559       specifies the width of the line and the base unit for dashes
     560     * The coordinates are pixel-centered, so that when filling a rectangle,
     561       if the supplied values are integers, the border will be half transparent.
     562       If you want the border to be completely filled, you can subtract/add
     563       0.5 to the coordinates to include the remaining thin border.
     564       See [[BGRABitmap tutorial 13|coordinate system]]. }
     565
     566    {** Draws a rectangle with antialiasing and fills it with color ''back''.
     567        Note that the pixel (x2,y2) is included contrary to integer coordinates }
     568    procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override;
     569    {** Draws a rectangle with antialiasing. Note that the pixel (x2,y2) is
     570        included contrary to integer coordinates }
     571    procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); override;
     572    {** Fills a rectangle with antialiasing. For example (-0.5,-0.5,0.5,0.5)
     573        fills one pixel }
     574    procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel; pixelCenteredCoordinates: boolean = true); override;
     575    {** Fills a rectangle with a texture }
     576    procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner; pixelCenteredCoordinates: boolean = true); override;
     577    {** Erases the content of a rectangle with antialiasing }
     578    procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte; pixelCenteredCoordinates: boolean = true); override;
     579
     580    {** Draws a rounded rectangle border with antialiasing. The corners have an
     581        elliptical radius of ''rx'' and ''ry''. ''options'' specifies how to
     582        draw the corners. See [[BGRABitmap Geometry types|geometry types]] }
     583    procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); override;
     584    {** Draws a rounded rectangle border with the specified texture.
     585        The corners have an elliptical radius of ''rx'' and ''ry''.
     586        ''options'' specifies how to draw the corners.
     587        See [[BGRABitmap Geometry types|geometry types]] }
     588    procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); override;
     589    {** Draws and fills a round rectangle }
     590    procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); override;
     591    {** Draws and fills a round rectangle with textures }
     592    procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); override;
     593
     594    {** Fills a rounded rectangle with antialiasing. The corners have an
     595        elliptical radius of ''rx'' and ''ry''. ''options'' specifies how to
     596        draw the corners. See [[BGRABitmap Geometry types|geometry types]] }
     597    procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override;
     598    {** Fills a rounded rectangle with a texture }
     599    procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override;
     600    {** Erases the content of a rounded rectangle with a texture }
     601    procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []; pixelCenteredCoordinates: boolean = true); override;
     602
     603    {** Draws an ellipse with antialising. ''rx'' is the horizontal radius and
     604        ''ry'' the vertical radius }
     605    procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); override;
     606    {** Draws an ellipse border with a ''texture'' }
     607    procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); override;
     608    {** Draws and fills an ellipse }
     609    procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override;
     610    {** Fills an ellipse }
     611    procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); override;
     612    {** Fills an ellipse with a ''texture'' }
     613    procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); override;
     614    {** Fills an ellipse with a gradient of color. ''outercolor'' specifies
     615        the end color of the gradient on the border of the ellipse and
     616        ''innercolor'' the end color of the gradient at the center of the
     617        ellipse }
     618    procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); override;
     619    {** Erases the content of an ellipse }
     620    procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); override;
     621
     622    {==== Polygons and path ====}
    326623    procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); override;
    327624    procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); override;
     
    331628    procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); override;
    332629
     630    procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override;
     631    procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); override;
     632    procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); override;
     633    procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); override;
     634    procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); override;
     635
     636    procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override;
     637    procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); override;
     638    procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True; ACulling: TFaceCulling = fcNone); override;
     639    procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word; TextureInterpolation: Boolean= True); override;
     640    procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACulling: TFaceCulling = fcNone); override;
     641    procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     642    procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     643    procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); override;
     644    procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); override;
     645    procedure FillQuadAffineMapping(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; ADrawMode: TDrawMode = dmDrawWithTransparency; AOpacity: byte = 255); override;
     646    procedure FillQuadAffineMappingAntialias(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; AOpacity: byte = 255); override;
     647
     648    procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); override;
     649    procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); override;
     650    procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel); override;
     651    procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
     652    procedure FillPolyPerspectiveMappingLightness(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean; zbuffer: psingle = nil); override;
     653
    333654    procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); override;
    334655    procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); override;
     
    338659    procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); override;
    339660
    340     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); override;
    341     procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); override;
    342     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override;
    343     procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); override;
    344     procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); override;
    345     procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); override;
    346     procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); override;
    347 
    348     procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override;
    349     procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); override;
    350     procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); override;
    351     procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); override;
    352 
    353     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); override;
    354     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); override;
    355     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); override;
    356     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); override;
    357 
    358     procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); override; overload;
    359     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); override; overload;
    360     procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); override;
    361     procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); override;
    362     procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); override;
    363     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); override;
    364     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); override;
    365     procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); override;
    366     procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); override;
    367     procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer;
    368       BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
    369     procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer;
    370       BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); override;
     661    procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); override;
     662    procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); override;
     663    procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); override;
     664    procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); override;
     665    procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single); override;
     666    procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single); override;
     667    procedure FillPath(APath: IBGRAPath; AFillColor: TBGRAPixel); override;
     668    procedure FillPath(APath: IBGRAPath; AFillTexture: IBGRAScanner); override;
     669    procedure ErasePath(APath: IBGRAPath; alpha: byte); override;
     670
     671    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); override;
     672    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); override;
     673    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); override;
     674    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); override;
     675    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single); override;
     676    procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single); override;
     677    procedure FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AFillColor: TBGRAPixel); override;
     678    procedure FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AFillTexture: IBGRAScanner); override;
     679    procedure ErasePath(APath: IBGRAPath; AMatrix: TAffineMatrix; alpha: byte); override;
     680
     681    procedure ArrowStartAsNone; override;
     682    procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
     683    procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
     684    procedure ArrowStartAsTail; override;
     685
     686    procedure ArrowEndAsNone; override;
     687    procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1); override;
     688    procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5); override;
     689    procedure ArrowEndAsTail; override;
    371690
    372691    { Draws the UTF8 encoded string, with color c.
     
    385704    procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); override; overload;
    386705
     706    procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); override; overload;
     707    procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); override; overload;
     708
    387709    { Draw the UTF8 encoded string at the coordinate (x,y), clipped inside the rectangle ARect.
    388710      Additional style information is provided by the style parameter.
     
    405727
    406728    function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; override;
    407     function ComputeWidePolyline(const points: array of TPointF; w: single; Closed: boolean): ArrayOfTPointF; override;
     729    function ComputeWidePolyline(const points: array of TPointF; w: single; ClosedCap: boolean): ArrayOfTPointF; override;
    408730    function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; override;
    409731
     
    425747    procedure AlphaFill(alpha: byte; start, Count: integer); override;
    426748    procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); override;
    427     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); override;
     749    procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode; AOpacity: byte = 255); override;
    428750    procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); override;
    429751    procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); override;
    430752    procedure ReplaceColor(before, after: TColor); override;
    431753    procedure ReplaceColor(before, after: TBGRAPixel); override;
     754    procedure ReplaceColor(ABounds: TRect; before, after: TColor); override;
     755    procedure ReplaceColor(ABounds: TRect; before, after: TBGRAPixel); override;
    432756    procedure ReplaceTransparent(after: TBGRAPixel); override;
     757    procedure ReplaceTransparent(ABounds: TRect; after: TBGRAPixel); override;
    433758    procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel;
    434759      mode: TFloodfillMode; Tolerance: byte = 0); override;
    435760    procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel;
    436761      gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    437       gammaColorCorrection: boolean = True; Sinus: Boolean=False); override;
     762      gammaColorCorrection: boolean = True; Sinus: Boolean=False;
     763      ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); override;
    438764    procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
    439765      gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    440       Sinus: Boolean=False); override;
     766      Sinus: Boolean=False; ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); override;
    441767    function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
    442768                AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; override;
     
    449775
    450776    {Canvas drawing functions}
    451     procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
    452       AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override;
    453     procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
    454       ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override;
    455     procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); override;
    456777    procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); override;
    457778    procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override;
     
    463784    procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); override;
    464785    procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override;
    465     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); override;
     786    procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); override; overload;
     787    function GetImageAffineBounds(AMatrix: TAffineMatrix; ASourceBounds: TRect; AClipOutput: boolean = true): TRect; override; overload;
     788    function IsAffineRoughlyTranslation(AMatrix: TAffineMatrix; ASourceBounds: TRect): boolean; override;
     789
    466790    procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); override;
    467791
     
    476800    function Equals(comp: TBGRACustomBitmap): boolean; override;
    477801    function Equals(comp: TBGRAPixel): boolean; override;
    478     function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; override;
    479     function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; override;
    480802    function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; override;
    481803    function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; override;
     
    483805    function Resample(newWidth, newHeight: integer;
    484806      mode: TResampleMode = rmFineResample): TBGRACustomBitmap; override;
    485     procedure VerticalFlip(ARect: TRect); override;
    486     procedure HorizontalFlip(ARect: TRect); override;
     807    procedure VerticalFlip(ARect: TRect); override; overload;
     808    procedure HorizontalFlip(ARect: TRect); override; overload;
    487809    function RotateCW: TBGRACustomBitmap; override;
    488810    function RotateCCW: TBGRACustomBitmap; override;
     
    491813    procedure LinearNegative; override;
    492814    procedure LinearNegativeRect(ABounds: TRect); override;
    493     procedure InplaceGrayscale; override;
    494     procedure InplaceGrayscale(ABounds: TRect); override;
     815    procedure InplaceGrayscale(AGammaCorrection: boolean = true); override;
     816    procedure InplaceGrayscale(ABounds: TRect; AGammaCorrection: boolean = true); override;
     817    procedure InplaceNormalize(AEachChannel: boolean = True); override;
     818    procedure InplaceNormalize(ABounds: TRect; AEachChannel: boolean = True); override;
    495819    procedure SwapRedBlue; override;
     820    procedure SwapRedBlue(ARect: TRect); override;
    496821    procedure GrayscaleToAlpha; override;
    497822    procedure AlphaToGrayscale; override;
    498     procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); override;
     823    procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); override; overload;
    499824    procedure ApplyGlobalOpacity(alpha: byte); override;
     825    procedure ApplyGlobalOpacity(ABounds: TRect; alpha: byte); override;
    500826    procedure ConvertToLinearRGB; override;
    501827    procedure ConvertFromLinearRGB; override;
     
    510836    function FilterContour: TBGRACustomBitmap; override;
    511837    function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; override;
    512     function FilterBlurRadial(radius: integer;
    513       blurType: TRadialBlurType): TBGRACustomBitmap; override;
    514     function FilterBlurRadial(ABounds: TRect; radius: integer;
    515       blurType: TRadialBlurType): TBGRACustomBitmap; override;
    516     function FilterBlurMotion(distance: integer; angle: single;
    517       oriented: boolean): TBGRACustomBitmap; override;
    518     function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
    519       oriented: boolean): TBGRACustomBitmap; override;
     838    function FilterBlurRadial(radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; override;
     839    function FilterBlurRadial(ABounds: TRect; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap; override;
     840    function FilterBlurRadial(radiusX, radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; override;
     841    function FilterBlurRadial(ABounds: TRect; radiusX, radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap; override;
     842    function FilterBlurMotion(distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; override;
     843    function FilterBlurMotion(ABounds: TRect; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap; override;
    520844    function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; override;
    521845    function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; override;
    522     function FilterEmboss(angle: single): TBGRACustomBitmap; override;
    523     function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; override;
     846    function FilterEmboss(angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; override;
     847    function FilterEmboss(angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap; override;
    524848    function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; override;
    525849    function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; override;
     
    530854    function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; override;
    531855    function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; override;
     856    function FilterAffine(AMatrix: TAffineMatrix; correctBlur: boolean = false): TBGRACustomBitmap; override;
    532857    function FilterSphere: TBGRACustomBitmap; override;
    533858    function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; override;
     
    535860    function FilterCylinder: TBGRACustomBitmap; override;
    536861    function FilterPlane: TBGRACustomBitmap; override;
    537 
    538     property CanvasBGRA: TBGRACanvas read GetCanvasBGRA;
    539     property Canvas2D: TBGRACanvas2D read GetCanvas2D;
    540862  end;
    541863
     
    544866  TBGRAPtrBitmap = class(TBGRADefaultBitmap)
    545867  protected
     868    function GetLineOrder: TRawImageLineOrder; override;
     869    procedure SetLineOrder(AValue: TRawImageLineOrder); override;
    546870    procedure ReallocData; override;
    547871    procedure FreeData; override;
     872    procedure CannotResize;
     873    procedure NotImplemented;
     874    procedure RebuildBitmap; override;
     875
     876    function CreateDefaultFontRenderer: TBGRACustomFontRenderer; override; //to override
     877    function LoadFromRawImage({%H-}ARawImage: TRawImage; {%H-}DefaultOpacity: byte;
     878      {%H-}AlwaysReplaceAlpha: boolean=False; {%H-}RaiseErrorOnInvalidPixelFormat: boolean
     879      =True): boolean; override; //to override
    548880  public
    549881    constructor Create(AWidth, AHeight: integer; AData: Pointer); overload;
    550882    function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; override;
    551883    procedure SetDataPtr(AData: Pointer);
    552     property LineOrder: TRawImageLineOrder Read FLineOrder Write FLineOrder;
     884    property LineOrder: TRawImageLineOrder Read GetLineOrder Write SetLineOrder;
     885
     886    procedure DataDrawTransparent({%H-}ACanvas: TCanvas; {%H-}Rect: TRect; {%H-}AData: Pointer;
     887      {%H-}ALineOrder: TRawImageLineOrder; {%H-}AWidth, {%H-}AHeight: integer); override; //to override
     888    procedure DataDrawOpaque({%H-}ACanvas: TCanvas; {%H-}Rect: TRect; {%H-}AData: Pointer;
     889      {%H-}ALineOrder: TRawImageLineOrder; {%H-}AWidth, {%H-}AHeight: integer); override; //to override
     890    procedure GetImageFromCanvas({%H-}CanvasSource: TCanvas; {%H-}x, {%H-}y: integer); override; //to override
     891
     892    procedure Assign({%H-}Source: TPersistent); override;
     893    procedure TakeScreenshot({%H-}ARect: TRect); override;
     894    procedure TakeScreenshotOfPrimaryMonitor; override;
     895    procedure LoadFromDevice({%H-}DC: System.THandle); override;
     896    procedure LoadFromDevice({%H-}DC: System.THandle; {%H-}ARect: TRect); override;
    553897  end;
    554898
     
    560904  gammaColorCorrection: boolean = True; Sinus: Boolean=False);
    561905
    562 implementation
    563 
    564 uses Math, LCLIntf, LCLType,
    565   BGRABlend, BGRAFilters, BGRAText, BGRATextFX, BGRAGradientScanner,
    566   BGRAResample, BGRATransform, BGRAPolygon, BGRAPolygonAliased,
    567   BGRAPath, FPReadPcx, FPWritePcx, FPReadXPM, FPWriteXPM;
    568 
    569906type
     907
     908  { TBitmapTracker }
     909
    570910  TBitmapTracker = class(TBitmap)
    571911  protected
     
    576916  end;
    577917
     918implementation
     919
     920uses Math, BGRAUTF8, BGRABlend, BGRAFilters, BGRAGradientScanner,
     921  BGRAResample, BGRAPolygon, BGRAPolygonAliased,
     922  BGRAPath, FPReadPcx, FPWritePcx, FPReadXPM, FPWriteXPM,
     923  BGRAReadBMP, BGRAReadJpeg,
     924  BGRADithering, BGRAFilterScanner;
     925
     926{ TBitmapTracker }
     927
    578928constructor TBitmapTracker.Create(AUser: TBGRADefaultBitmap);
    579929begin
     
    592942
    593943function TBGRADefaultBitmap.CheckEmpty: boolean;
     944const
     945  alphaMask = $ff shl TBGRAPixel_AlphaShift;
    594946var
    595947  i: integer;
     
    597949begin
    598950  p := Data;
    599   for i := NbPixels - 1 downto 0 do
    600   begin
    601     if p^.alpha <> 0 then
     951  for i := (NbPixels shr 1) - 1 downto 0 do
     952  begin
     953    if PInt64(p)^ and (alphaMask or (alphaMask shl 32)) <> 0 then
    602954    begin
    603955      Result := False;
    604956      exit;
    605957    end;
    606     Inc(p);
     958    Inc(p,2);
     959  end;
     960  if Odd(NbPixels) and (p^.alpha <> 0) then
     961  begin
     962    Result := false;
     963    exit;
    607964  end;
    608965  Result := True;
     
    616973function TBGRADefaultBitmap.GetCustomPenStyle: TBGRAPenStyle;
    617974begin
    618   result := DuplicatePenStyle(FCustomPenStyle);
     975  result := DuplicatePenStyle(FPenStroker.CustomPenStyle);
    619976end;
    620977
     
    628985  else
    629986    FCanvasOpacity := 0;
     987end;
     988
     989procedure TBGRADefaultBitmap.DoLoadFromBitmap;
     990begin
     991  //nothing
    630992end;
    631993
     
    6481010procedure TBGRADefaultBitmap.SetCustomPenStyle(const AValue: TBGRAPenStyle);
    6491011begin
    650   FCustomPenStyle := DuplicatePenStyle(AValue);
     1012  FPenStroker.CustomPenStyle := DuplicatePenStyle(AValue);
    6511013end;
    6521014
    6531015procedure TBGRADefaultBitmap.SetPenStyle(const AValue: TPenStyle);
    6541016begin
    655   Case AValue of
    656   psSolid: CustomPenStyle := SolidPenStyle;
    657   psDash: CustomPenStyle := DashPenStyle;
    658   psDot: CustomPenStyle := DotPenStyle;
    659   psDashDot: CustomPenStyle := DashDotPenStyle;
    660   psDashDotDot: CustomPenStyle := DashDotDotPenStyle;
    661   else CustomPenStyle := ClearPenStyle;
    662   end;
    663   FPenStyle := AValue;
     1017  FPenStroker.Style := AValue;
    6641018end;
    6651019
    6661020function TBGRADefaultBitmap.GetPenStyle: TPenStyle;
    6671021begin
    668   Result:= FPenStyle;
     1022  Result:= FPenStroker.Style;
    6691023end;
    6701024
    6711025function TBGRADefaultBitmap.GetLineCap: TPenEndCap;
    6721026begin
    673   result := FLineCap;
     1027  result := FPenStroker.LineCap;
    6741028end;
    6751029
    6761030procedure TBGRADefaultBitmap.SetLineCap(AValue: TPenEndCap);
    6771031begin
    678   if AValue <> FLineCap then
    679   begin
    680     FLineCap:= AValue;
    681     if Assigned(FArrow) then FArrow.LineCap := AValue;
    682   end;
     1032  if AValue <> FPenStroker.LineCap then
     1033  begin
     1034    FPenStroker.LineCap := AValue;
     1035    if Assigned(FPenStroker.Arrow) then
     1036      FPenStroker.Arrow.LineCap := AValue;
     1037  end;
     1038end;
     1039
     1040function TBGRADefaultBitmap.GetPenStroker: TBGRACustomPenStroker;
     1041begin
     1042  result := FPenStroker;
    6831043end;
    6841044
     
    7711131function TBGRADefaultBitmap.GetFontRenderer: TBGRACustomFontRenderer;
    7721132begin
    773   if FFontRenderer = nil then FFontRenderer := TLCLFontRenderer.Create;
     1133  if FFontRenderer = nil then FFontRenderer := CreateDefaultFontRenderer;
     1134  if FFontRenderer = nil then raise exception.Create('No font renderer');
    7741135  result := FFontRenderer;
    7751136  result.FontName := FontName;
     
    7871148end;
    7881149
     1150function TBGRADefaultBitmap.GetFontAnchorVerticalOffset: single;
     1151begin
     1152  case FontVerticalAnchor of
     1153  fvaTop: result := 0;
     1154  fvaCenter: result := FontFullHeight*0.5;
     1155  fvaCapLine: result := FontPixelMetric.CapLine;
     1156  fvaCapCenter: result := (FontPixelMetric.CapLine+FontPixelMetric.Baseline)*0.5;
     1157  fvaXLine: result := FontPixelMetric.xLine;
     1158  fvaXCenter: result := (FontPixelMetric.xLine+FontPixelMetric.Baseline)*0.5;
     1159  fvaBaseline: result := FontPixelMetric.Baseline;
     1160  fvaDescentLine: result := FontPixelMetric.DescentLine;
     1161  fvaBottom: result := FontFullHeight;
     1162  else
     1163    result := 0;
     1164  end;
     1165end;
     1166
     1167function TBGRADefaultBitmap.GetFontAnchorRotatedOffset: TPointF;
     1168begin
     1169  result := GetFontAnchorRotatedOffset(FontOrientation);
     1170end;
     1171
     1172function TBGRADefaultBitmap.GetFontAnchorRotatedOffset(
     1173  ACustomOrientation: integer): TPointF;
     1174begin
     1175  result := PointF(0, GetFontAnchorVerticalOffset);
     1176  if ACustomOrientation <> 0 then
     1177    result := AffineMatrixRotationDeg(-ACustomOrientation*0.1)*result;
     1178end;
     1179
    7891180{ Get scanline without checking bounds nor updated from TBitmap }
    7901181function TBGRADefaultBitmap.GetScanlineFast(y: integer): PBGRAPixel; inline;
     
    8941285  BGRAClass := TBGRABitmapAny(self.ClassType);
    8951286  Result    := BGRAClass.Create(Filename,AIsUtf8);
     1287end;
     1288
     1289function TBGRADefaultBitmap.NewBitmap(Filename: string; AIsUtf8: boolean;
     1290  AOptions: TBGRALoadingOptions): TBGRACustomBitmap;
     1291var
     1292  BGRAClass: TBGRABitmapAny;
     1293begin
     1294  BGRAClass := TBGRABitmapAny(self.ClassType);
     1295  Result    := BGRAClass.Create(Filename,AIsUtf8,AOptions);
     1296end;
     1297
     1298function TBGRADefaultBitmap.NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap;
     1299var
     1300  BGRAClass: TBGRABitmapAny;
     1301begin
     1302  BGRAClass := TBGRABitmapAny(self.ClassType);
     1303  Result    := BGRAClass.Create(AFPImage);
     1304end;
     1305
     1306procedure TBGRADefaultBitmap.LoadFromStream(Str: TStream;
     1307  Handler: TFPCustomImageReader; AOptions: TBGRALoadingOptions);
     1308var OldBmpOption: TBMPTransparencyOption;
     1309  OldJpegPerf: TJPEGReadPerformance;
     1310begin
     1311  if (loBmpAutoOpaque in AOptions) and (Handler is TBGRAReaderBMP) then
     1312  begin
     1313    OldBmpOption := TBGRAReaderBMP(Handler).TransparencyOption;
     1314    TBGRAReaderBMP(Handler).TransparencyOption := toAuto;
     1315    inherited LoadFromStream(Str, Handler, AOptions);
     1316    TBGRAReaderBMP(Handler).TransparencyOption := OldBmpOption;
     1317  end else
     1318  if (loJpegQuick in AOptions) and (Handler is TBGRAReaderJpeg) then
     1319  begin
     1320    OldJpegPerf := TBGRAReaderJpeg(Handler).Performance;
     1321    TBGRAReaderJpeg(Handler).Performance := jpBestSpeed;
     1322    inherited LoadFromStream(Str, Handler, AOptions);
     1323    TBGRAReaderJpeg(Handler).Performance := OldJpegPerf;
     1324  end else
     1325    inherited LoadFromStream(Str, Handler, AOptions);
    8961326end;
    8971327
     
    9191349  FWidth    := AWidth;
    9201350  FHeight   := AHeight;
     1351  FScanWidth := FWidth;
     1352  FScanHeight:= FHeight;
    9211353  FNbPixels := AWidth * AHeight;
    9221354  if FNbPixels < 0 then // 2 Go limit
     
    9361368end;
    9371369
     1370constructor TBGRADefaultBitmap.Create(AFPImage: TFPCustomImage);
     1371begin
     1372  Init;
     1373  inherited Create(AFPImage.Width, AFPImage.Height);
     1374  Assign(AFPImage);
     1375end;
     1376
    9381377{ Creates an image of dimensions AWidth and AHeight and filled with transparent pixels. }
    939 constructor TBGRADefaultBitmap.Create(ABitmap: TBitmap);
     1378constructor TBGRADefaultBitmap.Create(ABitmap: TBitmap; AUseTransparent: boolean);
    9401379begin
    9411380  Init;
    9421381  inherited Create(ABitmap.Width, ABitmap.Height);
    943   Assign(ABitmap);
     1382  Assign(ABitmap, AUseTransparent);
    9441383end;
    9451384
     
    9731412destructor TBGRADefaultBitmap.Destroy;
    9741413begin
    975   FreeData;
     1414  FPenStroker.Free;
    9761415  FFontRenderer.Free;
    977   FBitmap.Free;
    9781416  FCanvasFP.Free;
    9791417  FCanvasBGRA.Free;
    9801418  FCanvas2D.Free;
    981   FArrow.Free;
     1419  FreeData;
     1420  FreeBitmap;
    9821421  inherited Destroy;
    9831422end;
     
    9971436end;
    9981437
     1438constructor TBGRADefaultBitmap.Create(AFilename: string; AIsUtf8: boolean;
     1439  AOptions: TBGRALoadingOptions);
     1440begin
     1441  Init;
     1442  inherited Create(0, 0);
     1443  if AIsUtf8 then
     1444    LoadFromFileUTF8(Afilename, AOptions)
     1445  else
     1446    LoadFromFile(Afilename, AOptions);
     1447end;
     1448
    9991449{ Creates an image by loading its content from the stream AStream. }
    10001450constructor TBGRADefaultBitmap.Create(AStream: TStream);
     
    10031453  inherited Create(0, 0); 
    10041454  LoadFromStream(AStream);
    1005 end;
    1006 
    1007 procedure TBGRADefaultBitmap.Assign(ARaster: TRasterImage);
    1008 var TempBmp: TBitmap;
    1009     ConvertOk: boolean;
    1010 begin
    1011   DiscardBitmapChange;
    1012   SetSize(ARaster.Width, ARaster.Height);
    1013   if not LoadFromRawImage(ARaster.RawImage,0,False,False) then
    1014   if ARaster is TBitmap then
    1015   begin //try to convert
    1016     TempBmp := TBitmap.Create;
    1017     TempBmp.Width := ARaster.Width;
    1018     TempBmp.Height := ARaster.Height;
    1019     TempBmp.Canvas.Draw(0,0,ARaster);
    1020     ConvertOk := LoadFromRawImage(TempBmp.RawImage,0,False,False);
    1021     TempBmp.Free;
    1022     if not ConvertOk then
    1023       raise Exception.Create('Unable to convert image to 24 bit');
    1024   end else
    1025     raise Exception.Create('Unable to convert image to 24 bit');
    1026   If Empty then AlphaFill(255); // if bitmap seems to be empty, assume
    1027                                 // it is an opaque bitmap without alpha channel
    1028 end;
    1029 
    1030 procedure TBGRADefaultBitmap.Assign(MemBitmap: TBGRACustomBitmap);
    1031 begin
    1032   DiscardBitmapChange;
    1033   SetSize(MemBitmap.Width, MemBitmap.Height);
    1034   PutImage(0, 0, MemBitmap, dmSet);
    10351455end;
    10361456
     
    10421462  AStream.Write(lWidth,sizeof(lWidth));
    10431463  AStream.Write(lHeight,sizeof(lHeight));
     1464  If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False);
    10441465  for y := 0 to Height-1 do
    10451466    AStream.Write(ScanLine[y]^, Width*sizeof(TBGRAPixel));
    1046 end;
    1047 
    1048 {$hints off}
     1467  If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False);
     1468end;
     1469
    10491470procedure TBGRADefaultBitmap.Deserialize(AStream: TStream);
    10501471var lWidth,lHeight,y: integer;
    10511472begin
    1052   AStream.Read(lWidth,sizeof(lWidth));
    1053   AStream.Read(lHeight,sizeof(lHeight));
     1473  AStream.Read({%H-}lWidth,sizeof(lWidth));
     1474  AStream.Read({%H-}lHeight,sizeof(lHeight));
    10541475  lWidth := LEtoN(lWidth);
    10551476  lHeight := LEtoN(lHeight);
     
    10571478  for y := 0 to Height-1 do
    10581479    AStream.Read(ScanLine[y]^, Width*sizeof(TBGRAPixel));
    1059 end;
    1060 {$hints on}
     1480  If TBGRAPixel_RGBAOrder then TBGRAFilterScannerSwapRedBlue.ComputeFilterAt(FData,FData,FNbPixels,False);
     1481  InvalidateBitmap;
     1482end;
    10611483
    10621484class procedure TBGRADefaultBitmap.SerializeEmpty(AStream: TStream);
     
    10681490end;
    10691491
    1070 procedure TBGRADefaultBitmap.SaveToFile(const filename: string);
    1071 var
    1072   ext:    string;
    1073   writer: TFPCustomImageWriter;
    1074 begin
    1075   ext := AnsiLowerCase(ExtractFileExt(filename));
    1076 
    1077   { When saving to PNG, define some parameters so that the
    1078     image be readable by most programs }
    1079   if ext = '.png' then
    1080     writer := CreateAdaptedPngWriter
    1081   else
    1082   if (ext='.xpm') and (Width*Height > 32768) then //xpm is slow so avoid big images
    1083     raise exception.Create('Image is too big to be saved as XPM') else
    1084       writer := nil;
    1085 
    1086   if writer <> nil then //use custom writer if defined
    1087   begin
    1088     inherited SaveToFile(Filename, writer);
    1089     writer.Free;
    1090   end
    1091   else
    1092     inherited SaveToFile(Filename);
    1093 end;
    1094 
    1095 procedure TBGRADefaultBitmap.SaveToStreamAsPng(Str: TStream);
    1096 var writer: TFPWriterPNG;
    1097 begin
    1098   writer := CreateAdaptedPngWriter;
    1099   SaveToStream(Str,writer);
    1100   writer.Free;
     1492procedure TBGRADefaultBitmap.Assign(Source: TPersistent);
     1493var pdest: PBGRAPixel;
     1494  x,y: NativeInt;
     1495begin
     1496  if Source is TBGRACustomBitmap then
     1497  begin
     1498    DiscardBitmapChange;
     1499    SetSize(TBGRACustomBitmap(Source).Width, TBGRACustomBitmap(Source).Height);
     1500    PutImage(0, 0, TBGRACustomBitmap(Source), dmSet);
     1501  end else
     1502  if Source is TFPCustomImage then
     1503  begin
     1504    DiscardBitmapChange;
     1505    SetSize(TFPCustomImage(Source).Width, TFPCustomImage(Source).Height);
     1506    for y := 0 to TFPCustomImage(Source).Height-1 do
     1507    begin
     1508      pdest := ScanLine[y];
     1509      for x := 0 to TFPCustomImage(Source).Width-1 do
     1510      begin
     1511        pdest^ := FPColorToBGRA(TFPCustomImage(Source).Colors[x,y]);
     1512        inc(pdest);
     1513      end;
     1514    end;
     1515  end else
     1516    inherited Assign(Source);
     1517end;
     1518
     1519procedure TBGRADefaultBitmap.Assign(Source: TBitmap; AUseTransparent: boolean);
     1520var
     1521  transpColor: TBGRAPixel;
     1522begin
     1523  Assign(Source);
     1524  if AUseTransparent and TBitmap(Source).Transparent then
     1525  begin
     1526    if TBitmap(Source).TransparentMode = tmFixed then
     1527      transpColor := ColorToBGRA(TBitmap(Source).TransparentColor)
     1528    else
     1529      transpColor := GetPixel(0,Height-1);
     1530    ReplaceColor(transpColor, BGRAPixelTransparent);
     1531  end;
    11011532end;
    11021533
     
    11321563  iFactY: int32or64): TBGRAPixel;
    11331564var
    1134   ixMod1,ixMod2: int32or64;
    1135   w1,w2,w3,w4,alphaW: UInt32or64;
    1136   bSum, gSum, rSum: UInt32or64;
    1137   aSum: UInt32or64;
    1138 
    1139   c:    TBGRAPixel;
     1565  ixMod2: int32or64;
     1566  pUpLeft, pUpRight, pDownLeft, pDownRight: PBGRAPixel;
    11401567  scan: PBGRAPixel;
    11411568begin
    1142   w4 := (iFactX*iFactY+127) shr 8;
    1143   w3 := iFactY-w4;
    1144   w1 := cardinal(256-iFactX)-w3;
    1145   w2 := iFactX-w4;
    1146 
    1147   rSum   := 0;
    1148   gSum   := 0;
    1149   bSum   := 0;
    1150   aSum   := 0;
    1151 
    11521569  scan := GetScanlineFast(iy);
    11531570
    1154   ixMod1 := ix;
    1155   c      := (scan + ix)^;
    1156   alphaW := c.alpha * w1;
    1157   aSum   += alphaW;
    1158 
    1159   rSum   += c.red * alphaW;
    1160   gSum   += c.green * alphaW;
    1161   bSum   += c.blue * alphaW;
    1162 
     1571  pUpLeft := (scan + ix);
    11631572  ixMod2 := ix+1;
    11641573  if ixMod2=Width then ixMod2 := 0;
    1165   c      := (scan + ixMod2)^;
    1166   alphaW := c.alpha * w2;
    1167   aSum   += alphaW;
    1168 
    1169   rSum   += c.red * alphaW;
    1170   gSum   += c.green * alphaW;
    1171   bSum   += c.blue * alphaW;
     1574  pUpRight := (scan + ixMod2);
    11721575
    11731576  Inc(iy);
    11741577  if iy = Height then iy := 0;
    11751578  scan := GetScanlineFast(iy);
    1176 
    1177   c      := (scan + ixMod2)^;
    1178   alphaW := c.alpha * w4;
    1179   aSum   += alphaW;
    1180 
    1181   rSum   += c.red * alphaW;
    1182   gSum   += c.green * alphaW;
    1183   bSum   += c.blue * alphaW;
    1184 
    1185   c      := (scan + ixMod1)^;
    1186   alphaW := c.alpha * w3;
    1187   aSum   += alphaW;
    1188 
    1189   rSum   += c.red * alphaW;
    1190   gSum   += c.green * alphaW;
    1191   bSum   += c.blue * alphaW;
    1192 
    1193   if (aSum < 128) then
    1194     Result := BGRAPixelTransparent
    1195   else
    1196   begin
    1197     Result.red   := (rSum + aSum shr 1) div aSum;
    1198     Result.green := (gSum + aSum shr 1) div aSum;
    1199     Result.blue  := (bSum + aSum shr 1) div aSum;
    1200     Result.alpha := (aSum + 128) shr 8;
    1201   end;
     1579  pDownLeft := (scan + ix);
     1580  pDownRight := (scan + ixMod2);
     1581
     1582  InterpolateBilinear(pUpLeft, pUpRight, pDownLeft,
     1583          pDownRight, iFactX, iFactY, @result);
    12021584end;
    12031585
     
    12051587  iFactY: int32or64; smoothBorder: boolean): TBGRAPixel;
    12061588var
    1207   w1,w2,w3,w4,alphaW: cardinal;
    1208   rSum, gSum, bSum: cardinal; //rgbDiv = aSum
    1209   aSum, aDiv: cardinal;
    1210   c:    TBGRAPixel;
     1589  pUpLeft, pUpRight, pDownLeft, pDownRight: PBGRAPixel;
    12111590  scan: PBGRAPixel;
    12121591begin
    1213   rSum   := 0;
    1214   gSum   := 0;
    1215   bSum   := 0;
    1216   aSum   := 0;
    1217   aDiv   := 0;
    1218 
    1219   w4 := (iFactX*iFactY+127) shr 8;
    1220   w3 := iFactY-w4;
    1221   {$PUSH}{$HINTS OFF}
    1222   w1 := (256-iFactX)-w3;
    1223   {$POP}
    1224   w2 := iFactX-w4;
    1225 
    1226   { For each pixel around the coordinate, compute
    1227     the weight for it and multiply values by it before
    1228     adding to the sum }
    12291592  if (iy >= 0) and (iy < Height) then
    12301593  begin
     
    12321595
    12331596    if (ix >= 0) and (ix < Width) then
    1234     begin
    1235       c      := (scan + ix)^;
    1236       alphaW := c.alpha * w1;
    1237       aDiv   += w1;
    1238       aSum   += alphaW;
    1239       rSum   += c.red * alphaW;
    1240       gSum   += c.green * alphaW;
    1241       bSum   += c.blue * alphaW;
    1242     end;
    1243 
    1244     Inc(ix);
     1597      pUpLeft := scan+ix
     1598    else if smoothBorder then
     1599      pUpLeft := @BGRAPixelTransparent
     1600    else
     1601      pUpLeft := nil;
     1602
     1603    if (ix+1 >= 0) and (ix+1 < Width) then
     1604      pUpRight := scan+(ix+1)
     1605    else if smoothBorder then
     1606      pUpRight := @BGRAPixelTransparent
     1607    else
     1608      pUpRight := nil;
     1609  end else
     1610  if smoothBorder then
     1611  begin
     1612    pUpLeft := @BGRAPixelTransparent;
     1613    pUpRight := @BGRAPixelTransparent;
     1614  end else
     1615  begin
     1616    pUpLeft := nil;
     1617    pUpRight := nil;
     1618  end;
     1619
     1620  if (iy+1 >= 0) and (iy+1 < Height) then
     1621  begin
     1622    scan := GetScanlineFast(iy+1);
     1623
    12451624    if (ix >= 0) and (ix < Width) then
    1246     begin
    1247       c      := (scan + ix)^;
    1248       alphaW := c.alpha * w2;
    1249       aDiv   += w2;
    1250       aSum   += alphaW;
    1251       rSum   += c.red * alphaW;
    1252       gSum   += c.green * alphaW;
    1253       bSum   += c.blue * alphaW;
    1254     end;
    1255   end
    1256   else
    1257   begin
    1258     Inc(ix);
    1259   end;
    1260 
    1261   Inc(iy);
    1262   if (iy >= 0) and (iy < Height) then
    1263   begin
    1264     scan := GetScanlineFast(iy);
    1265 
    1266     if (ix >= 0) and (ix < Width) then
    1267     begin
    1268       c      := (scan + ix)^;
    1269       alphaW := c.alpha * w4;
    1270       aDiv   += w4;
    1271       aSum   += alphaW;
    1272       rSum   += c.red * alphaW;
    1273       gSum   += c.green * alphaW;
    1274       bSum   += c.blue * alphaW;
    1275     end;
    1276 
    1277     Dec(ix);
    1278     if (ix >= 0) and (ix < Width) then
    1279     begin
    1280       c      := (scan + ix)^;
    1281       alphaW := c.alpha * w3;
    1282       aDiv   += w3;
    1283       aSum   += alphaW;
    1284       rSum   += c.red * alphaW;
    1285       gSum   += c.green * alphaW;
    1286       bSum   += c.blue * alphaW;
    1287     end;
    1288   end;
    1289 
    1290   if aSum < 128 then //if there is no alpha
    1291     Result := BGRAPixelTransparent
    1292   else
    1293   begin
    1294     Result.red   := (rSum + aSum shr 1) div aSum;
    1295     Result.green := (gSum + aSum shr 1) div aSum;
    1296     Result.blue  := (bSum + aSum shr 1) div aSum;
    1297     if smoothBorder or (aDiv = 256) then
    1298       Result.alpha := (aSum + 128) shr 8
     1625      pDownLeft := scan+ix
     1626    else if smoothBorder then
     1627      pDownLeft := @BGRAPixelTransparent
    12991628    else
    1300       Result.alpha := (aSum + aDiv shr 1) div aDiv;
    1301   end;
    1302 end;
    1303 
    1304 function TBGRADefaultBitmap.GetPolyLineOption: TBGRAPolyLineOptions;
    1305 begin
    1306   result := [];
    1307   if Assigned(FArrow) and FArrow.IsStartDefined then result += [plNoStartCap];
    1308   if Assigned(FArrow) and FArrow.IsEndDefined then result += [plNoEndCap];
     1629      pDownLeft := nil;
     1630
     1631    if (ix+1 >= 0) and (ix+1 < Width) then
     1632      pDownRight := scan+(ix+1)
     1633    else if smoothBorder then
     1634      pDownRight := @BGRAPixelTransparent
     1635    else
     1636      pDownRight := nil;
     1637  end else
     1638  if smoothBorder then
     1639  begin
     1640    pDownLeft := @BGRAPixelTransparent;
     1641    pDownRight := @BGRAPixelTransparent;
     1642  end else
     1643  begin
     1644    pDownLeft := nil;
     1645    pDownRight := nil;
     1646  end;
     1647
     1648  InterpolateBilinear(pUpLeft, pUpRight, pDownLeft,
     1649          pDownRight, iFactX, iFactY, @result);
    13091650end;
    13101651
    13111652function TBGRADefaultBitmap.GetArrow: TBGRAArrow;
    13121653begin
    1313   if FArrow = nil then
    1314   begin
    1315     FArrow := TBGRAArrow.Create;
    1316     FArrow.LineCap := LineCap;
    1317   end;
    1318   result := FArrow;
     1654  if FPenStroker.Arrow = nil then
     1655  begin
     1656    FPenStroker.Arrow := TBGRAArrow.Create;
     1657    FPenStroker.Arrow.LineCap := LineCap;
     1658    FPenStroker.ArrowOwned := true;
     1659  end;
     1660  result := FPenStroker.Arrow as TBGRAArrow;
    13191661end;
    13201662
     
    13421684procedure TBGRADefaultBitmap.SetPixel(x, y: int32or64; c: TColor);
    13431685var
    1344   p: PByte;
     1686  p: PBGRAPixel;
    13451687begin
    13461688  if not PtInClipRect(x,y) then exit;
    13471689  LoadFromBitmapIfNeeded;
    1348   p  := PByte(GetScanlineFast(y) + x);
    1349   p^ := c shr 16;
    1350   Inc(p);
    1351   p^ := c shr 8;
    1352   Inc(p);
    1353   p^ := c;
    1354   Inc(p);
    1355   p^ := 255;
     1690  p  := GetScanlineFast(y) + x;
     1691  RedGreenBlue(c, p^.red,p^.green,p^.blue);
     1692  p^.alpha := 255;
    13561693  InvalidateBitmap;
    13571694end;
     
    16391976end;
    16401977
    1641 { Load raw image data. It must be 32bit or 24 bits per pixel}
    1642 function TBGRADefaultBitmap.LoadFromRawImage(ARawImage: TRawImage;
    1643   DefaultOpacity: byte; AlwaysReplaceAlpha: boolean; RaiseErrorOnInvalidPixelFormat: boolean): boolean;
    1644 var
    1645   psource_byte, pdest_byte,
    1646   psource_first, pdest_first: PByte;
    1647   psource_delta, pdest_delta: integer;
    1648 
    1649   n: integer;
    1650   mustSwapRedBlue, mustReverse32: boolean;
    1651 
    1652   procedure CopyAndSwapIfNecessary(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);
    1653   begin
    1654     if mustReverse32 then
    1655     begin
    1656       while count > 0 do
    1657       begin
    1658         pdest^.blue := psrc^.alpha;
    1659         pdest^.green := psrc^.red;
    1660         pdest^.red := psrc^.green;
    1661         pdest^.alpha := psrc^.blue;
    1662         dec(count);
    1663         inc(pdest);
    1664         inc(psrc);
    1665       end;
    1666     end else
    1667     if mustSwapRedBlue then
    1668     begin
    1669       while count > 0 do
    1670       begin
    1671         pdest^.red := psrc^.blue;
    1672         pdest^.green := psrc^.green;
    1673         pdest^.blue := psrc^.red;
    1674         pdest^.alpha := psrc^.alpha;
    1675         dec(count);
    1676         inc(pdest);
    1677         inc(psrc);
    1678       end;
    1679     end else
    1680       move(psrc^,pdest^,count*sizeof(TBGRAPixel));
    1681   end;
    1682 
    1683   procedure CopyRGBAndSwapIfNecessary(psrc: PByte; pdest: PBGRAPixel; count: integer);
    1684   begin
    1685     if mustSwapRedBlue then
    1686     begin
    1687       while count > 0 do
    1688       begin
    1689         pdest^.blue := (psrc+2)^;
    1690         pdest^.green := (psrc+1)^;
    1691         pdest^.red := psrc^;
    1692         pdest^.alpha := DefaultOpacity;
    1693         inc(psrc,3);
    1694         inc(pdest);
    1695         dec(count);
    1696       end;
    1697     end else
    1698     begin
    1699       while count > 0 do
    1700       begin
    1701         PWord(pdest)^ := PWord(psrc)^;
    1702         pdest^.red := (psrc+2)^;
    1703         pdest^.alpha := DefaultOpacity;
    1704         inc(psrc,3);
    1705         inc(pdest);
    1706         dec(count);
    1707       end;
    1708     end;
    1709   end;     
    1710 
    1711   procedure CopyAndSwapIfNecessaryAndSetAlpha(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);
    1712   begin
    1713     if mustReverse32 then
    1714     begin
    1715       while count > 0 do
    1716       begin
    1717         pdest^.blue := psrc^.alpha;
    1718         pdest^.green := psrc^.red;
    1719         pdest^.red := psrc^.green;
    1720         pdest^.alpha := DefaultOpacity; //use default opacity
    1721         inc(psrc);
    1722         inc(pdest);
    1723         dec(count);
    1724       end;
    1725     end else
    1726     if mustSwapRedBlue then
    1727     begin
    1728       while count > 0 do
    1729       begin
    1730         pdest^.red := psrc^.blue;
    1731         pdest^.green := psrc^.green;
    1732         pdest^.blue := psrc^.red;
    1733         pdest^.alpha := DefaultOpacity; //use default opacity
    1734         inc(psrc);
    1735         inc(pdest);
    1736         dec(count);
    1737       end;
    1738     end else
    1739     begin
    1740       while count > 0 do
    1741       begin
    1742         PWord(pdest)^ := PWord(psrc)^;
    1743         pdest^.red := psrc^.red;
    1744         pdest^.alpha := DefaultOpacity; //use default opacity
    1745         inc(psrc);
    1746         inc(pdest);
    1747         dec(count);
    1748       end;
    1749     end;
    1750   end;
    1751 
    1752   procedure CopyAndSwapIfNecessaryAndReplaceAlpha(psrc: PBGRAPixel; pdest: PBGRAPixel; count: integer);
    1753   var OpacityOrMask, OpacityAndMask, sourceval: Longword;
    1754   begin
    1755     OpacityOrMask := NtoLE(longword(DefaultOpacity) shl 24);
    1756     OpacityAndMask := NtoLE($FFFFFF);
    1757     if mustReverse32 then
    1758     begin
    1759       OpacityAndMask := NtoBE($FFFFFF);
    1760       while count > 0 do
    1761       begin
    1762         sourceval := plongword(psrc)^ and OpacityAndMask;
    1763         if (sourceval <> 0) and (psrc^.blue{=alpha} = 0) then //if not black but transparent
    1764         begin
    1765           pdest^.blue := psrc^.alpha;
    1766           pdest^.green := psrc^.red;
    1767           pdest^.red := psrc^.green;
    1768           pdest^.alpha := DefaultOpacity; //use default opacity
    1769         end
    1770         else
    1771         begin
    1772           pdest^.blue := psrc^.alpha;
    1773           pdest^.green := psrc^.red;
    1774           pdest^.red := psrc^.green;
    1775           pdest^.alpha := psrc^.blue;
    1776         end;
    1777         dec(count);
    1778         inc(pdest);
    1779         inc(psrc);
    1780       end;
    1781     end else
    1782     if mustSwapRedBlue then
    1783     begin
    1784       while count > 0 do
    1785       begin
    1786         sourceval := plongword(psrc)^ and OpacityAndMask;
    1787         if (sourceval <> 0) and (psrc^.alpha = 0) then //if not black but transparent
    1788         begin
    1789           pdest^.red := psrc^.blue;
    1790           pdest^.green := psrc^.green;
    1791           pdest^.blue := psrc^.red;
    1792           pdest^.alpha := DefaultOpacity; //use default opacity
    1793         end
    1794         else
    1795         begin
    1796           pdest^.red := psrc^.blue;
    1797           pdest^.green := psrc^.green;
    1798           pdest^.blue := psrc^.red;
    1799           pdest^.alpha := psrc^.alpha;
    1800         end;
    1801         dec(count);
    1802         inc(pdest);
    1803         inc(psrc);
    1804       end;
    1805     end else
    1806     begin
    1807       while count > 0 do
    1808       begin
    1809         sourceval := plongword(psrc)^ and OpacityAndMask;
    1810         if (sourceval <> 0) and (psrc^.alpha = 0) then //if not black but transparent
    1811           plongword(pdest)^ := sourceval or OpacityOrMask //use default opacity
    1812         else
    1813           pdest^ := psrc^;
    1814         dec(count);
    1815         inc(pdest);
    1816         inc(psrc);
    1817       end;
    1818     end;
    1819   end;
    1820 
    1821 begin
    1822   if (ARawImage.Description.Width <> cardinal(Width)) or
    1823     (ARawImage.Description.Height <> cardinal(Height)) then
    1824     raise Exception.Create('Bitmap size is inconsistant');
    1825 
    1826   DiscardBitmapChange;
    1827   if (Height=0) or (Width=0) then
    1828   begin
    1829     result := true;
    1830     exit;
    1831   end;
    1832 
    1833   if ARawImage.Description.LineOrder = riloTopToBottom then
    1834   begin
    1835     psource_first := ARawImage.Data;
    1836     psource_delta := ARawImage.Description.BytesPerLine;
    1837   end else
    1838   begin
    1839     psource_first := ARawImage.Data + (ARawImage.Description.Height-1) * ARawImage.Description.BytesPerLine;
    1840     psource_delta := -ARawImage.Description.BytesPerLine;
    1841   end;
    1842 
    1843   if ((ARawImage.Description.RedShift = 0) and
    1844     (ARawImage.Description.BlueShift = 16) and
    1845     (ARawImage.Description.ByteOrder = riboLSBFirst)) or
    1846     ((ARawImage.Description.RedShift = 24) and
    1847     (ARawImage.Description.BlueShift = 8) and
    1848     (ARawImage.Description.ByteOrder = riboMSBFirst)) then
    1849   begin
    1850     mustSwapRedBlue:= true;
    1851     mustReverse32 := false;
    1852   end
    1853   else
    1854   begin
    1855     mustSwapRedBlue:= false;
    1856     if ((ARawImage.Description.RedShift = 8) and
    1857       (ARawImage.Description.GreenShift = 16) and
    1858       (ARawImage.Description.BlueShift = 24) and
    1859       (ARawImage.Description.ByteOrder = riboLSBFirst)) or
    1860       ((ARawImage.Description.RedShift = 16) and
    1861       (ARawImage.Description.GreenShift = 8) and
    1862       (ARawImage.Description.BlueShift = 0) and
    1863       (ARawImage.Description.ByteOrder = riboMSBFirst)) then
    1864         mustReverse32 := true
    1865       else
    1866         mustReverse32 := false;
    1867   end;
    1868 
    1869   if self.LineOrder = riloTopToBottom then
    1870   begin
    1871     pdest_first := PByte(self.Data);
    1872     pdest_delta := self.Width*sizeof(TBGRAPixel);
    1873   end else
    1874   begin
    1875     pdest_first := PByte(self.Data) + (self.Height-1)*self.Width*sizeof(TBGRAPixel);
    1876     pdest_delta := -self.Width*sizeof(TBGRAPixel);
    1877   end;
    1878 
    1879   { 32 bits per pixel }
    1880   if (ARawImage.Description.BitsPerPixel = 32) and
    1881     (ARawImage.DataSize >= longword(NbPixels) * 4) then
    1882   begin
    1883     { If there is an alpha channel }
    1884     if (ARawImage.Description.AlphaPrec = 8) and not AlwaysReplaceAlpha then
    1885     begin
    1886       if DefaultOpacity = 0 then
    1887       begin
    1888         if ARawImage.Description.LineOrder = FLineOrder then
    1889           CopyAndSwapIfNecessary(PBGRAPixel(ARawImage.Data), FData, NbPixels) else
    1890         begin
    1891           psource_byte := psource_first;
    1892           pdest_byte := pdest_first;
    1893           for n := FHeight-1 downto 0 do
    1894           begin
    1895             CopyAndSwapIfNecessary(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);
    1896             inc(psource_byte, psource_delta);
    1897             inc(pdest_byte, pdest_delta);
    1898           end;
    1899         end;
    1900       end
    1901       else
    1902       begin
    1903         psource_byte := psource_first;
    1904         pdest_byte := pdest_first;
    1905         for n := FHeight-1 downto 0 do
    1906         begin
    1907           CopyAndSwapIfNecessaryAndReplaceAlpha(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);
    1908           inc(psource_byte, psource_delta);
    1909           inc(pdest_byte, pdest_delta);
    1910         end;
    1911       end;
    1912     end
    1913     else
    1914     begin { If there isn't any alpha channel }
    1915       psource_byte := psource_first;
    1916       pdest_byte := pdest_first;
    1917       for n := FHeight-1 downto 0 do
    1918       begin
    1919         CopyAndSwapIfNecessaryAndSetAlpha(PBGRAPixel(psource_byte), PBGRAPixel(pdest_byte), FWidth);
    1920         inc(psource_byte, psource_delta);
    1921         inc(pdest_byte, pdest_delta);
    1922       end;
    1923     end;
    1924   end
    1925   else
    1926   { 24 bit per pixel }
    1927   if (ARawImage.Description.BitsPerPixel = 24) then
    1928   begin
    1929     psource_byte := psource_first;
    1930     pdest_byte := pdest_first;
    1931     for n := FHeight-1 downto 0 do
    1932     begin
    1933       CopyRGBAndSwapIfNecessary(psource_byte, PBGRAPixel(pdest_byte), FWidth);
    1934       inc(psource_byte, psource_delta);
    1935       inc(pdest_byte, pdest_delta);
    1936     end;
    1937   end
    1938   else
    1939   begin
    1940     if RaiseErrorOnInvalidPixelFormat then
    1941       raise Exception.Create('Invalid raw image format (' + IntToStr(
    1942         ARawImage.Description.Depth) + ' found)') else
    1943     begin
    1944       result := false;
    1945       exit;
    1946     end;
    1947   end;
    1948 
    1949   InvalidateBitmap;
    1950   result := true;
    1951 end;
    1952 
    19531978procedure TBGRADefaultBitmap.LoadFromBitmapIfNeeded;
    19541979begin
    19551980  if FBitmapModified then
    19561981  begin
    1957     if FBitmap <> nil then
    1958       LoadFromRawImage(FBitmap.RawImage, FCanvasOpacity);
     1982    DoLoadFromBitmap;
    19591983    DiscardBitmapChange;
    19601984  end;
     
    20242048  FWidth     := 0;
    20252049  FHeight    := 0;
     2050  FScanWidth := FWidth;
     2051  FScanHeight:= FHeight;
    20262052  FLineOrder := riloTopToBottom;
    20272053  FCanvasOpacity := 255;
     
    20332059  FontStyle := [];
    20342060  FontAntialias := False;
     2061  FontVerticalAnchor:= fvaTop;
    20352062  FFontHeight := 20;
    20362063
    2037   PenStyle := psSolid;
    2038   LineCap := pecRound;
    2039   JoinStyle := pjsBevel;
    2040   JoinMiterLimit := 2;
    20412064  ResampleFilter := rfHalfCosine;
    20422065  ScanInterpolationFilter := rfLinear;
    20432066  ScanOffset := Point(0,0);
     2067
     2068  FPenStroker := TBGRAPenStroker.Create;
     2069  FPenStroker.Arrow := TBGRAArrow.Create;
     2070  FPenStroker.Arrow.LineCap := LineCap;
     2071  FPenStroker.ArrowOwned := true;
    20442072end;
    20452073
     
    20512079function TBGRADefaultBitmap.GetInternalColor(x, y: integer): TFPColor;
    20522080begin
    2053   if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then exit;
    2054   result := BGRAToFPColor((Scanline[y] + x)^);
     2081  if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then
     2082    result := colTransparent
     2083  else
     2084    result := BGRAToFPColor((Scanline[y] + x)^);
    20552085end;
    20562086
     
    20692099  c: TFPColor;
    20702100begin
    2071   if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then exit;
    2072   c := BGRAToFPColor((Scanline[y] + x)^);
    2073   Result := palette.IndexOf(c);
     2101  if (x < 0) or (y < 0) or (x >= Width) or (y >= Height) then
     2102    result := 0
     2103  else
     2104  begin
     2105    c := BGRAToFPColor((Scanline[y] + x)^);
     2106    Result := palette.IndexOf(c);
     2107  end;
    20742108end;
    20752109
    20762110procedure TBGRADefaultBitmap.Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean);
    20772111begin
    2078   if self = nil then
    2079     exit;
     2112  if (self = nil) or (Width = 0) or (Height = 0) then exit;
    20802113  if Opaque then
    20812114    DataDrawOpaque(ACanvas, Rect(X, Y, X + Width, Y + Height), Data,
     
    20922125procedure TBGRADefaultBitmap.Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean);
    20932126begin
    2094   if self = nil then
    2095     exit;
     2127  if (self = nil) or (Width = 0) or (Height = 0) then exit;
    20962128  if Opaque then
    20972129    DataDrawOpaque(ACanvas, Rect, Data, FLineOrder, FWidth, FHeight)
     
    20992131  begin
    21002132    LoadFromBitmapIfNeeded;
    2101     if Empty then
    2102       exit;
    21032133    ACanvas.StretchDraw(Rect, Bitmap);
    21042134  end;
     
    23092339end;
    23102340
    2311 procedure TBGRADefaultBitmap.SetArrowStart(AStyle: TBGRAArrowStyle;
    2312   ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single);
    2313 begin
    2314   GetArrow.SetStart(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset);
    2315 end;
    2316 
    2317 procedure TBGRADefaultBitmap.SetArrowEnd(AStyle: TBGRAArrowStyle;
    2318   ATipStyle: TPenJoinStyle; ARelativePenWidth: single; ATriangleBackOffset: single);
    2319 begin
    2320   GetArrow.SetEnd(AStyle,ATipStyle,ARelativePenWidth,ATriangleBackOffset);
    2321 end;
    2322 
    2323 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single);
    2324 var tempCanvas: TBGRACanvas2D;
    2325 begin
    2326   tempCanvas:= TBGRACanvas2D.Create(self);
    2327   tempCanvas.strokeStyle(c);
    2328   tempCanvas.lineWidth := w;
    2329   tempCanvas.lineStyle(CustomPenStyle);
    2330   tempCanvas.lineCapLCL := LineCap;
    2331   tempCanvas.lineJoinLCL := JoinStyle;
    2332   tempCanvas.path(APath);
    2333   tempCanvas.stroke;
    2334   tempCanvas.Free;
    2335 end;
    2336 
    2337 procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single);
    2338 var tempCanvas: TBGRACanvas2D;
    2339 begin
    2340   tempCanvas:= TBGRACanvas2D.Create(self);
    2341   tempCanvas.strokeStyle(texture);
    2342   tempCanvas.lineWidth := w;
    2343   tempCanvas.lineStyle(CustomPenStyle);
    2344   tempCanvas.lineCapLCL := LineCap;
    2345   tempCanvas.lineJoinLCL := JoinStyle;
    2346   tempCanvas.path(APath);
    2347   tempCanvas.stroke;
    2348   tempCanvas.Free;
     2341procedure TBGRADefaultBitmap.InternalTextOutCurved(
     2342  ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel;
     2343  ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single);
     2344var
     2345  pstr: pchar;
     2346  left,charlen: integer;
     2347  nextchar: string;
     2348  charwidth, angle, textlen: single;
     2349begin
     2350  if (ATexture = nil) and (AColor.alpha = 0) then exit;
     2351  sUTF8 := CleanTextOutString(sUTF8);
     2352  if sUTF8 = '' then exit;
     2353  pstr := @sUTF8[1];
     2354  left := length(sUTF8);
     2355  if AALign<> taLeftJustify then
     2356  begin
     2357    textlen := TextSize(sUTF8).cx + (UTF8Length(sUTF8)-1)*ALetterSpacing;
     2358    case AAlign of
     2359      taCenter: ACursor.MoveBackward(textlen*0.5);
     2360      taRightJustify: ACursor.MoveBackward(textlen);
     2361    end;
     2362  end;
     2363  while left > 0 do
     2364  begin
     2365    charlen := UTF8CharacterLength(pstr);
     2366    setlength(nextchar, charlen);
     2367    move(pstr^, nextchar[1], charlen);
     2368    inc(pstr,charlen);
     2369    dec(left,charlen);
     2370    charwidth := TextSize(nextchar).cx;
     2371    ACursor.MoveForward(charwidth);
     2372    ACursor.MoveBackward(charwidth, false);
     2373    ACursor.MoveForward(charwidth*0.5);
     2374    with ACursor.CurrentTangent do angle := arctan2(y,x);
     2375    with ACursor.CurrentCoordinate do
     2376    begin
     2377      if ATexture = nil then
     2378        TextOutAngle(x,y, system.round(-angle*1800/Pi), nextchar, AColor, taCenter)
     2379      else
     2380        TextOutAngle(x,y, system.round(-angle*1800/Pi), nextchar, ATexture, taCenter);
     2381    end;
     2382    ACursor.MoveForward(charwidth*0.5 + ALetterSpacing);
     2383  end;
     2384end;
     2385
     2386procedure TBGRADefaultBitmap.InternalArc(cx, cy, rx, ry: single; StartAngleRad,
     2387  EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions;
     2388  ADrawChord: boolean; ATexture: IBGRAScanner);
     2389var
     2390  pts, ptsFill: array of TPointF;
     2391  temp: single;
     2392  multi: TBGRAMultishapeFiller;
     2393begin
     2394  if (rx = 0) or (ry = 0) then exit;
     2395  if ADrawChord then AOptions := AOptions+[aoClosePath];
     2396  if not (aoFillPath in AOptions) then
     2397    AFillColor := BGRAPixelTransparent;
     2398
     2399  if (ABorderColor.alpha = 0) and (AFillColor.alpha = 0) then exit;
     2400
     2401  if abs(StartAngleRad-EndAngleRad) >= 2*PI - 1e-6 then
     2402  begin
     2403    if aoPie in AOptions then
     2404      EndAngleRad:= StartAngleRad+2*PI
     2405    else
     2406      EllipseAntialias(cx,cy,rx,ry,ABorderColor,w,AFillColor);
     2407    exit;
     2408  end;
     2409
     2410  if EndAngleRad < StartAngleRad then
     2411  begin
     2412    temp := StartAngleRad;
     2413    StartAngleRad:= EndAngleRad;
     2414    EndAngleRad:= temp;
     2415  end;
     2416
     2417  pts := ComputeArcRad(cx,cy,rx,ry,StartAngleRad,EndAngleRad);
     2418  if aoPie in AOptions then pts := ConcatPointsF([PointsF([PointF(cx,cy)]),pts]);
     2419
     2420  multi := TBGRAMultishapeFiller.Create;
     2421  multi.PolygonOrder := poLastOnTop;
     2422  if AFillColor.alpha <> 0 then
     2423  begin
     2424    if not (aoPie in AOptions) and (length(pts)>=2) then ptsFill := ConcatPointsF([PointsF([(pts[0]+pts[high(pts)])*0.5]),pts])
     2425     else ptsFill := pts;
     2426    if ATexture <> nil then
     2427      multi.AddPolygon(ptsFill, ATexture)
     2428    else
     2429      multi.AddPolygon(ptsFill, AFillColor);
     2430  end;
     2431  if ABorderColor.alpha <> 0 then
     2432  begin
     2433    if [aoPie,aoClosePath]*AOptions <> [] then
     2434      multi.AddPolygon(ComputeWidePolygon(pts,w), ABorderColor)
     2435    else
     2436      multi.AddPolygon(ComputeWidePolyline(pts,w), ABorderColor);
     2437  end;
     2438  multi.Antialiasing := true;
     2439  multi.Draw(self);
     2440  multi.Free;
     2441end;
     2442
     2443function TBGRADefaultBitmap.IsAffineRoughlyTranslation(AMatrix: TAffineMatrix; ASourceBounds: TRect): boolean;
     2444const oneOver512 = 1/512;
     2445var Orig,HAxis,VAxis: TPointF;
     2446begin
     2447  Orig := AMatrix*PointF(ASourceBounds.Left,ASourceBounds.Top);
     2448  if (abs(Orig.x-round(Orig.x)) > oneOver512) or
     2449     (abs(Orig.y-round(Orig.y)) > oneOver512) then
     2450  begin
     2451    result := false;
     2452    exit;
     2453  end;
     2454  HAxis := AMatrix*PointF(ASourceBounds.Right-1,ASourceBounds.Top);
     2455  if (abs(HAxis.x - (round(Orig.x)+ASourceBounds.Right-1 - ASourceBounds.Left)) > oneOver512) or
     2456     (abs(HAxis.y - round(Orig.y)) > oneOver512) then
     2457  begin
     2458    result := false;
     2459    exit;
     2460  end;
     2461  VAxis := AMatrix*PointF(ASourceBounds.Left,ASourceBounds.Bottom-1);
     2462  if (abs(VAxis.y - (round(Orig.y)+ASourceBounds.Bottom-1 - ASourceBounds.Top)) > oneOver512) or
     2463     (abs(VAxis.x - round(Orig.x)) > oneOver512) then
     2464  begin
     2465    result := false;
     2466    exit;
     2467  end;
     2468  result := true;
    23492469end;
    23502470
     
    23812501  c: TBGRAPixel; w: single);
    23822502begin
    2383   if Assigned(FArrow) then
    2384     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2385   else
    2386     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit);
     2503  FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,c), c);
    23872504end;
    23882505
     
    23902507  texture: IBGRAScanner; w: single);
    23912508begin
    2392   if Assigned(FArrow) then
    2393     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2394   else
    2395     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit);
     2509  FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w), texture);
    23962510end;
    23972511
    23982512procedure TBGRADefaultBitmap.DrawLineAntialias(x1, y1, x2, y2: single;
    2399   c: TBGRAPixel; w: single; Closed: boolean);
    2400 var
    2401   options: TBGRAPolyLineOptions;
    2402 begin
    2403   if not closed then options := [plRoundCapOpen] else options := [];
    2404   options += GetPolyLineOption;
    2405   if Assigned(FArrow) then
    2406     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2407   else
    2408     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,nil,JoinMiterLimit)
     2513  c: TBGRAPixel; w: single; ClosedCap: boolean);
     2514begin
     2515  FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,c,ClosedCap), c);
    24092516end;
    24102517
    24112518procedure TBGRADefaultBitmap.DrawLineAntialias(x1, y1, x2, y2: single;
    2412   texture: IBGRAScanner; w: single; Closed: boolean);
    2413 var
    2414   options: TBGRAPolyLineOptions;
    2415   c: TBGRAPixel;
    2416 begin
    2417   if not closed then
    2418   begin
    2419     options := [plRoundCapOpen];
    2420     c := BGRAWhite; //needed for alpha junction
    2421   end else
    2422   begin
    2423     options := [];
    2424     c := BGRAPixelTransparent;
    2425   end;
    2426   options += GetPolyLineOption;
    2427   if Assigned(FArrow) then
    2428     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2429   else
    2430     BGRAPen.BGRAPolyLine(self,[PointF(x1,y1),PointF(x2,y2)],w,c,pecRound,pjsRound,FCustomPenStyle,options,texture,JoinMiterLimit);
     2519  texture: IBGRAScanner; w: single; ClosedCap: boolean);
     2520begin
     2521  FillPolyAntialias( FPenStroker.ComputePolyline([PointF(x1,y1),PointF(x2,y2)],w,ClosedCap), texture);
    24312522end;
    24322523
     
    24342525  c: TBGRAPixel; w: single);
    24352526begin
    2436   if Assigned(FArrow) then
    2437     BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2438   else
    2439     BGRAPen.BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,nil,JoinMiterLimit)
     2527  FillPolyAntialias( FPenStroker.ComputePolyline(points,w,c), c);
    24402528end;
    24412529
     
    24432531  const points: array of TPointF; texture: IBGRAScanner; w: single);
    24442532begin
    2445   if Assigned(FArrow) then
    2446     BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2447   else
    2448     BGRAPen.BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,texture,JoinMiterLimit);
     2533  FillPolyAntialias( FPenStroker.ComputePolyline(points,w), texture);
    24492534end;
    24502535
    24512536procedure TBGRADefaultBitmap.DrawPolyLineAntialias(const points: array of TPointF;
    2452   c: TBGRAPixel; w: single; Closed: boolean);
    2453 var
    2454   options: TBGRAPolyLineOptions;
    2455 begin
    2456   if not closed then options := [plRoundCapOpen] else options := [];
    2457   options += GetPolyLineOption;
    2458   if Assigned(FArrow) then
    2459     BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    2460   else
    2461     BGRAPen.BGRAPolyLine(self,points,w,c,pecRound,JoinStyle,FCustomPenStyle,options,nil,JoinMiterLimit);
     2537  c: TBGRAPixel; w: single; ClosedCap: boolean);
     2538begin
     2539  FillPolyAntialias( FPenStroker.ComputePolyline(points,w,c,ClosedCap), c);
     2540end;
     2541
     2542procedure TBGRADefaultBitmap.DrawPolyLineAntialias(
     2543  const points: array of TPointF; texture: IBGRAScanner; w: single;
     2544  ClosedCap: boolean);
     2545begin
     2546  FillPolyAntialias( FPenStroker.ComputePolyline(points,w,ClosedCap), texture);
    24622547end;
    24632548
     
    24782563end;
    24792564
     2565procedure TBGRADefaultBitmap.DrawPolyLineAntialiasAutocycle(
     2566  const points: array of TPointF; c: TBGRAPixel; w: single);
     2567begin
     2568  FillPolyAntialias( FPenStroker.ComputePolylineAutocycle(points,w), c);
     2569end;
     2570
     2571procedure TBGRADefaultBitmap.DrawPolyLineAntialiasAutocycle(
     2572  const points: array of TPointF; texture: IBGRAScanner; w: single);
     2573begin
     2574  FillPolyAntialias( FPenStroker.ComputePolylineAutocycle(points,w), texture);
     2575end;
     2576
    24802577procedure TBGRADefaultBitmap.DrawPolygonAntialias(const points: array of TPointF;
    24812578  c: TBGRAPixel; w: single);
    24822579begin
    2483   BGRAPolyLine(self,points,w,c,LineCap,JoinStyle,FCustomPenStyle,[plCycle],nil,JoinMiterLimit);
     2580  FillPolyAntialias( FPenStroker.ComputePolygon(points,w), c);
    24842581end;
    24852582
     
    24872584  const points: array of TPointF; texture: IBGRAScanner; w: single);
    24882585begin
    2489   BGRAPolyLine(self,points,w,BGRAPixelTransparent,LineCap,JoinStyle,FCustomPenStyle,[plCycle],texture,JoinMiterLimit);
     2586  FillPolyAntialias( FPenStroker.ComputePolygon(points,w), texture);
    24902587end;
    24912588
     
    25342631end;
    25352632
    2536 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; c: TBGRAPixel);
    2537 var tempCanvas: TBGRACanvas2D;
    2538 begin
    2539   tempCanvas:= TBGRACanvas2D.Create(self);
    2540   tempCanvas.fillStyle(c);
    2541   tempCanvas.path(APath);
    2542   tempCanvas.fill;
    2543   tempCanvas.Free;
    2544 end;
    2545 
    2546 procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; texture: IBGRAScanner);
    2547 var tempCanvas: TBGRACanvas2D;
    2548 begin
    2549   tempCanvas:= TBGRACanvas2D.Create(self);
    2550   tempCanvas.fillStyle(texture);
    2551   tempCanvas.path(APath);
    2552   tempCanvas.fill;
    2553   tempCanvas.Free;
     2633procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AFillColor: TBGRAPixel);
     2634begin
     2635  FillPolyAntialias(APath.getPoints,AFillColor);
     2636end;
     2637
     2638procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AFillTexture: IBGRAScanner);
     2639begin
     2640  FillPolyAntialias(APath.getPoints,AFillTexture);
     2641end;
     2642
     2643procedure TBGRADefaultBitmap.ErasePath(APath: IBGRAPath; alpha: byte);
     2644begin
     2645  ErasePolyAntialias(APath.getPoints,alpha);
     2646end;
     2647
     2648procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2649  AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel);
     2650var tempPath: TBGRAPath;
     2651  multi: TBGRAMultishapeFiller;
     2652begin
     2653  tempPath := TBGRAPath.Create(APath);
     2654  multi := TBGRAMultishapeFiller.Create;
     2655  multi.PolygonOrder := poLastOnTop;
     2656  multi.AddPathFill(tempPath,AMatrix,AFillColor);
     2657  multi.AddPathStroke(tempPath,AMatrix,AStrokeColor,AWidth,FPenStroker);
     2658  multi.Draw(self);
     2659  multi.Free;
     2660  tempPath.Free;
     2661end;
     2662
     2663procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2664  AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel);
     2665var tempPath: TBGRAPath;
     2666  multi: TBGRAMultishapeFiller;
     2667begin
     2668  tempPath := TBGRAPath.Create(APath);
     2669  multi := TBGRAMultishapeFiller.Create;
     2670  multi.PolygonOrder := poLastOnTop;
     2671  multi.AddPathFill(tempPath,AMatrix,AFillColor);
     2672  multi.AddPathStroke(tempPath,AMatrix,AStrokeTexture,AWidth,FPenStroker);
     2673  multi.Draw(self);
     2674  multi.Free;
     2675  tempPath.Free;
     2676end;
     2677
     2678procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2679  AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner);
     2680var tempPath: TBGRAPath;
     2681  multi: TBGRAMultishapeFiller;
     2682begin
     2683  tempPath := TBGRAPath.Create(APath);
     2684  multi := TBGRAMultishapeFiller.Create;
     2685  multi.PolygonOrder := poLastOnTop;
     2686  multi.AddPathFill(tempPath,AMatrix,AFillTexture);
     2687  multi.AddPathStroke(tempPath,AMatrix,AStrokeColor,AWidth,FPenStroker);
     2688  multi.Draw(self);
     2689  multi.Free;
     2690  tempPath.Free;
     2691end;
     2692
     2693procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2694  AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner);
     2695var
     2696  tempPath: TBGRAPath;
     2697  multi: TBGRAMultishapeFiller;
     2698begin
     2699  tempPath := TBGRAPath.Create(APath);
     2700  multi := TBGRAMultishapeFiller.Create;
     2701  multi.PolygonOrder := poLastOnTop;
     2702  multi.AddPathFill(tempPath,AMatrix,AFillTexture);
     2703  multi.AddPathStroke(tempPath,AMatrix,AStrokeTexture,AWidth,FPenStroker);
     2704  multi.Draw(self);
     2705  multi.Free;
     2706  tempPath.Free;
     2707end;
     2708
     2709procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2710  AStrokeColor: TBGRAPixel; AWidth: single);
     2711var tempPath: TBGRAPath;
     2712begin
     2713  tempPath := TBGRAPath.Create(APath);
     2714  tempPath.stroke(self, AMatrix, AStrokeColor, AWidth);
     2715  tempPath.Free;
     2716end;
     2717
     2718procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2719  AStrokeTexture: IBGRAScanner; AWidth: single);
     2720var tempPath: TBGRAPath;
     2721begin
     2722  tempPath := TBGRAPath.Create(APath);
     2723  tempPath.stroke(self, AMatrix, AStrokeTexture, AWidth);
     2724  tempPath.Free;
     2725end;
     2726
     2727procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2728  AFillColor: TBGRAPixel);
     2729begin
     2730  FillPolyAntialias(APath.getPoints(AMatrix),AFillColor);
     2731end;
     2732
     2733procedure TBGRADefaultBitmap.FillPath(APath: IBGRAPath; AMatrix: TAffineMatrix;
     2734  AFillTexture: IBGRAScanner);
     2735begin
     2736  FillPolyAntialias(APath.getPoints(AMatrix),AFillTexture);
     2737end;
     2738
     2739procedure TBGRADefaultBitmap.ErasePath(APath: IBGRAPath;
     2740  AMatrix: TAffineMatrix; alpha: byte);
     2741begin
     2742  ErasePolyAntialias(APath.getPoints(AMatrix),alpha);
     2743end;
     2744
     2745procedure TBGRADefaultBitmap.ArrowStartAsNone;
     2746begin
     2747  GetArrow.StartAsNone;
     2748end;
     2749
     2750procedure TBGRADefaultBitmap.ArrowStartAsClassic(AFlipped: boolean;
     2751  ACut: boolean; ARelativePenWidth: single);
     2752begin
     2753  GetArrow.StartAsClassic(AFlipped,ACut,ARelativePenWidth);
     2754end;
     2755
     2756procedure TBGRADefaultBitmap.ArrowStartAsTriangle(ABackOffset: single;
     2757  ARounded: boolean; AHollow: boolean; AHollowPenWidth: single);
     2758begin
     2759  GetArrow.StartAsTriangle(ABackOffset,ARounded,AHollow,AHollowPenWidth);
     2760end;
     2761
     2762procedure TBGRADefaultBitmap.ArrowStartAsTail;
     2763begin
     2764  GetArrow.StartAsTail;
     2765end;
     2766
     2767procedure TBGRADefaultBitmap.ArrowEndAsNone;
     2768begin
     2769  GetArrow.EndAsNone;
     2770end;
     2771
     2772procedure TBGRADefaultBitmap.ArrowEndAsClassic(AFlipped: boolean;
     2773  ACut: boolean; ARelativePenWidth: single);
     2774begin
     2775  GetArrow.EndAsClassic(AFlipped,ACut,ARelativePenWidth);
     2776end;
     2777
     2778procedure TBGRADefaultBitmap.ArrowEndAsTriangle(ABackOffset: single;
     2779  ARounded: boolean; AHollow: boolean; AHollowPenWidth: single);
     2780begin
     2781  GetArrow.EndAsTriangle(ABackOffset,ARounded,AHollow,AHollowPenWidth);
     2782end;
     2783
     2784procedure TBGRADefaultBitmap.ArrowEndAsTail;
     2785begin
     2786  GetArrow.EndAsTail;
    25542787end;
    25552788
     
    26322865
    26332866procedure TBGRADefaultBitmap.FillQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF;
    2634   texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True);
    2635 var
    2636   center: TPointF;
    2637   centerTex: TPointF;
    2638 begin
    2639   center := (pt1+pt2+pt3+pt4)*(1/4);
    2640   centerTex := (tex1+tex2+tex3+tex4)*(1/4);
    2641   FillTriangleLinearMapping(pt1,pt2,center, texture,tex1,tex2,centerTex, TextureInterpolation);
    2642   FillTriangleLinearMapping(pt2,pt3,center, texture,tex2,tex3,centerTex, TextureInterpolation);
    2643   FillTriangleLinearMapping(pt3,pt4,center, texture,tex3,tex4,centerTex, TextureInterpolation);
    2644   FillTriangleLinearMapping(pt4,pt1,center, texture,tex4,tex1,centerTex, TextureInterpolation);
     2867  texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     2868  TextureInterpolation: Boolean; ACulling: TFaceCulling);
     2869var
     2870  scan: TBGRAQuadLinearScanner;
     2871begin
     2872  if ((abs(pt1.y-pt2.y)<1e-6) and (abs(pt3.y-pt4.y)<1e-6)) or
     2873     ((abs(pt3.y-pt2.y)<1e-6) and (abs(pt1.y-pt4.y)<1e-6)) then
     2874     FillPolyLinearMapping([pt1,pt2,pt3,pt4], texture,
     2875            [tex1,tex2,tex3,tex4], TextureInterpolation)
     2876  else
     2877  begin
     2878    scan := TBGRAQuadLinearScanner.Create(texture,
     2879         [tex1,tex2,tex3,tex4],
     2880         [pt1,pt2,pt3,pt4],TextureInterpolation);
     2881    scan.Culling := ACulling;
     2882    FillPoly([pt1,pt2,pt3,pt4],scan,dmDrawWithTransparency);
     2883    scan.Free;
     2884  end;
    26452885end;
    26462886
     
    26632903
    26642904procedure TBGRADefaultBitmap.FillQuadLinearMappingAntialias(pt1, pt2, pt3,
    2665   pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
     2905  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     2906  ACulling: TFaceCulling);
    26662907var multi : TBGRAMultishapeFiller;
    26672908begin
    26682909  multi := TBGRAMultishapeFiller.Create;
    2669   multi.AddQuadLinearMapping(pt1, pt2, pt3, pt4, texture, tex1,tex2,tex3,tex4);
     2910  multi.AddQuadLinearMapping(pt1, pt2, pt3, pt4, texture, tex1,tex2,tex3,tex4, ACulling);
    26702911  multi.Draw(self);
    26712912  multi.free;
     
    26732914
    26742915procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3,
    2675   pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
     2916  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     2917  ADrawMode: TDrawMode);
    26762918var
    26772919  persp: TBGRAPerspectiveScannerTransform;
    26782920begin
    26792921  persp := TBGRAPerspectiveScannerTransform.Create(texture,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]);
    2680   FillPoly([pt1,pt2,pt3,pt4],persp,dmDrawWithTransparency);
     2922  FillPoly([pt1,pt2,pt3,pt4],persp,ADrawMode);
    26812923  persp.Free;
    26822924end;
     
    26842926procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3,
    26852927  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
    2686   ACleanBorders: TRect);
     2928  ACleanBorders: TRect; ADrawMode: TDrawMode);
    26872929var
    26882930  persp: TBGRAPerspectiveScannerTransform;
     
    26912933  clean := TBGRAExtendedBorderScanner.Create(texture,ACleanBorders);
    26922934  persp := TBGRAPerspectiveScannerTransform.Create(clean,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]);
    2693   FillPoly([pt1,pt2,pt3,pt4],persp,dmDrawWithTransparency);
     2935  FillPoly([pt1,pt2,pt3,pt4],persp,ADrawMode);
    26942936  persp.Free;
    26952937  clean.Free;
     
    27202962end;
    27212963
     2964procedure TBGRADefaultBitmap.FillQuadAffineMapping(Orig, HAxis, VAxis: TPointF;
     2965  AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean; ADrawMode: TDrawMode; AOpacity: byte);
     2966var pts3: TPointF;
     2967  affine: TBGRAAffineBitmapTransform;
     2968begin
     2969  if not APixelCenteredCoordinates then
     2970  begin
     2971    Orig -= PointF(0.5,0.5);
     2972    HAxis -= PointF(0.5,0.5);
     2973    VAxis -= PointF(0.5,0.5);
     2974  end;
     2975  pts3 := HAxis+(VAxis-Orig);
     2976  affine := TBGRAAffineBitmapTransform.Create(AImage,False,AImage.ScanInterpolationFilter,not APixelCenteredCoordinates);
     2977  affine.GlobalOpacity:= AOpacity;
     2978  affine.Fit(Orig,HAxis,VAxis);
     2979  FillPoly([Orig,HAxis,pts3,VAxis],affine,ADrawMode);
     2980  affine.Free;
     2981end;
     2982
     2983procedure TBGRADefaultBitmap.FillQuadAffineMappingAntialias(Orig, HAxis,
     2984  VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean; AOpacity: byte);
     2985var pts3: TPointF;
     2986  affine: TBGRAAffineBitmapTransform;
     2987begin
     2988  if not APixelCenteredCoordinates then
     2989  begin
     2990    Orig -= PointF(0.5,0.5);
     2991    HAxis -= PointF(0.5,0.5);
     2992    VAxis -= PointF(0.5,0.5);
     2993  end;
     2994  pts3 := HAxis+(VAxis-Orig);
     2995  affine := TBGRAAffineBitmapTransform.Create(AImage,False,AImage.ScanInterpolationFilter,not APixelCenteredCoordinates);
     2996  affine.GlobalOpacity:= AOpacity;
     2997  affine.Fit(Orig,HAxis,VAxis);
     2998  FillPolyAntialias([Orig,HAxis,pts3,VAxis],affine);
     2999  affine.Free;
     3000end;
     3001
    27223002procedure TBGRADefaultBitmap.FillPolyLinearMapping(const points: array of TPointF;
    27233003  texture: IBGRAScanner; texCoords: array of TPointF;
     
    28383118end;
    28393119
     3120procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath;
     3121  AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel);
     3122begin
     3123  DrawPath(APath,AffineMatrixIdentity,AStrokeColor,AWidth,AFillColor);
     3124end;
     3125
     3126procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath;
     3127  AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel);
     3128begin
     3129  DrawPath(APath,AffineMatrixIdentity,AStrokeTexture,AWidth,AFillColor);
     3130end;
     3131
     3132procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath;
     3133  AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner);
     3134begin
     3135  DrawPath(APath,AffineMatrixIdentity,AStrokeColor,AWidth,AFillTexture);
     3136end;
     3137
     3138procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath;
     3139  AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner);
     3140begin
     3141  DrawPath(APath,AffineMatrixIdentity,AStrokeTexture,AWidth,AFillTexture);
     3142end;
     3143
     3144procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single);
     3145begin
     3146  DrawPath(APath, AffineMatrixIdentity, AStrokeColor, AWidth);
     3147end;
     3148
     3149procedure TBGRADefaultBitmap.DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single);
     3150begin
     3151  DrawPath(APath, AffineMatrixIdentity, AStrokeTexture, AWidth);
     3152end;
     3153
    28403154procedure TBGRADefaultBitmap.EllipseAntialias(x, y, rx, ry: single;
    28413155  c: TBGRAPixel; w: single);
    28423156begin
    2843   if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;
    2844   if IsSolidPenStyle(FCustomPenStyle) then
     3157  if (PenStyle = psClear) or (c.alpha = 0) then exit;
     3158  if (PenStyle = psSolid) then
    28453159    BGRAPolygon.BorderEllipseAntialias(self, x, y, rx, ry, w, c, FEraseMode, LinearAntialiasing)
    28463160  else
     
    28513165  texture: IBGRAScanner; w: single);
    28523166begin
    2853   if IsClearPenStyle(FCustomPenStyle) then exit;
    2854   if IsSolidPenStyle(FCustomPenStyle) then
     3167  if (PenStyle = psClear) then exit;
     3168  if (PenStyle = psSolid) then
    28553169    BGRAPolygon.BorderEllipseAntialiasWithTexture(self, x, y, rx, ry, w, texture, LinearAntialiasing)
    28563170  else
     
    28743188  { use multishape filler for fine junction between polygons }
    28753189  multi := TBGRAMultishapeFiller.Create;
    2876   if not IsClearPenStyle(FCustomPenStyle) and (c.alpha <> 0) then
    2877   begin
    2878     if IsSolidPenStyle(FCustomPenStyle) then
     3190  if not (PenStyle = psClear) and (c.alpha <> 0) then
     3191  begin
     3192    if (PenStyle = psSolid) then
    28793193    begin
    28803194      multi.AddEllipse(x,y,rx-hw,ry-hw,back);
     
    29413255  hw: single;
    29423256begin
    2943   if IsClearPenStyle(FCustomPenStyle) or (c.alpha=0) or (w=0) then
     3257  if (PenStyle = psClear) or (c.alpha=0) or (w=0) then
    29443258  begin
    29453259    if back <> BGRAPixelTransparent then
     
    29663280  multi := TBGRAMultishapeFiller.Create;
    29673281  multi.FillMode := FillMode;
    2968   if (JoinStyle = pjsMiter) and IsSolidPenStyle(FCustomPenStyle) then
     3282  if (JoinStyle = pjsMiter) and (PenStyle = psSolid) then
    29693283    multi.AddRectangleBorder(x,y,x2,y2,w,c)
    29703284  else
     
    29853299  multi: TBGRAMultishapeFiller;
    29863300begin
    2987   if IsClearPenStyle(FCustomPenStyle) or (w=0) then exit;
     3301  if (PenStyle = psClear) or (w=0) then exit;
    29883302
    29893303  hw := w/2;
     
    30053319  multi := TBGRAMultishapeFiller.Create;
    30063320  multi.FillMode := FillMode;
    3007   if (JoinStyle = pjsMiter) and IsSolidPenStyle(FCustomPenStyle) then
     3321  if (JoinStyle = pjsMiter) and (PenStyle = psSolid) then
    30083322    multi.AddRectangleBorder(x,y,x2,y2,w, texture)
    30093323  else
     
    30163330   c: TBGRAPixel; w: single; options: TRoundRectangleOptions);
    30173331begin
    3018   if IsClearPenStyle(FCustomPenStyle) or (c.alpha = 0) then exit;
    3019   if IsSolidPenStyle(FCustomPenStyle) then
     3332  if (PenStyle = psClear) or (c.alpha = 0) then exit;
     3333  if (PenStyle = psSolid) then
    30203334    BGRAPolygon.BorderRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,c,False, LinearAntialiasing)
    30213335  else
     
    30293343  multi: TBGRAMultishapeFiller;
    30303344begin
    3031   if IsClearPenStyle(FCustomPenStyle) or (pencolor.alpha = 0) then
     3345  if (PenStyle = psClear) or (pencolor.alpha = 0) then
    30323346  begin
    30333347    FillRoundRectAntialias(x,y,x2,y2,rx,ry,fillColor,options);
    30343348    exit;
    30353349  end;
    3036   if IsSolidPenStyle(FCustomPenStyle) then
     3350  if (PenStyle = psSolid) then
    30373351    BGRAPolygon.BorderAndFillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,pencolor,fillcolor,nil,nil,False)
    30383352  else
     
    30533367  multi: TBGRAMultishapeFiller;
    30543368begin
    3055   if IsClearPenStyle(FCustomPenStyle) then
     3369  if (PenStyle = psClear) then
    30563370  begin
    30573371    FillRoundRectAntialias(x,y,x2,y2,rx,ry,fillTexture,options);
    30583372    exit;
    30593373  end else
    3060   if IsSolidPenStyle(FCustomPenStyle) then
     3374  if (PenStyle = psSolid) then
    30613375    BGRAPolygon.BorderAndFillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,w,options,BGRAPixelTransparent,BGRAPixelTransparent,pentexture,filltexture,False)
    30623376  else
     
    30743388  texture: IBGRAScanner; w: single; options: TRoundRectangleOptions);
    30753389begin
    3076   if IsClearPenStyle(FCustomPenStyle) then exit;
    3077   if IsSolidPenStyle(FCustomPenStyle) then
     3390  if (PenStyle = psClear) then exit;
     3391  if (PenStyle = psSolid) then
    30783392    BGRAPolygon.BorderRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,w,options,texture, LinearAntialiasing)
    30793393  else
     
    32603574
    32613575procedure TBGRADefaultBitmap.FillRect(x, y, x2, y2: integer;
    3262   texture: IBGRAScanner; mode: TDrawMode);
     3576  texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint);
    32633577var
    32643578  yb, tx, delta: integer;
     
    32783592  for yb := y to y2 do
    32793593  begin
    3280     texture.ScanMoveTo(x,yb);
     3594    texture.ScanMoveTo(x+AScanOffset.X,yb+AScanOffset.Y);
    32813595    ScannerPutPixels(texture, p, tx, mode);
    32823596    Inc(p, delta);
     
    32843598
    32853599  InvalidateBitmap;
     3600end;
     3601
     3602procedure TBGRADefaultBitmap.FillRect(x, y, x2, y2: integer;
     3603  texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm);
     3604var dither: TDitheringTask;
     3605begin
     3606  if not CheckClippedRectBounds(x,y,x2,y2) then exit;
     3607  dither := CreateDitheringTask(ditheringAlgorithm, texture, self, rect(x,y,x2,y2));
     3608  dither.ScanOffset := AScanOffset;
     3609  dither.DrawMode := mode;
     3610  dither.Execute;
     3611  dither.Free;
    32863612end;
    32873613
     
    33153641end;
    33163642
    3317 procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel);
     3643procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel; pixelCenteredCoordinates: boolean);
    33183644var tx,ty: single;
    33193645begin
     3646  if not pixelCenteredCoordinates then
     3647  begin
     3648    x -= 0.5;
     3649    y -= 0.5;
     3650    x2 -= 0.5;
     3651    y2 -= 0.5;
     3652  end;
     3653
    33203654  tx := x2-x;
    33213655  ty := y2-y;
    3322   if (tx=0) or (ty=0) then exit;
     3656  if (abs(tx)<1e-3) or (abs(ty)<1e-3) then exit;
    33233657  if (abs(tx) > 2) and (abs(ty) > 2) then
    33243658  begin
     
    33453679
    33463680procedure TBGRADefaultBitmap.EraseRectAntialias(x, y, x2, y2: single;
    3347   alpha: byte);
    3348 begin
     3681  alpha: byte; pixelCenteredCoordinates: boolean);
     3682begin
     3683  if not pixelCenteredCoordinates then
     3684  begin
     3685    x -= 0.5;
     3686    y -= 0.5;
     3687    x2 -= 0.5;
     3688    y2 -= 0.5;
     3689  end;
    33493690  ErasePolyAntialias([pointf(x, y), pointf(x2, y), pointf(x2, y2), pointf(x, y2)], alpha);
    33503691end;
    33513692
    33523693procedure TBGRADefaultBitmap.FillRectAntialias(x, y, x2, y2: single;
    3353   texture: IBGRAScanner);
    3354 begin
     3694  texture: IBGRAScanner; pixelCenteredCoordinates: boolean);
     3695begin
     3696  if not pixelCenteredCoordinates then
     3697  begin
     3698    x -= 0.5;
     3699    y -= 0.5;
     3700    x2 -= 0.5;
     3701    y2 -= 0.5;
     3702  end;
    33553703  FillPolyAntialias([pointf(x, y), pointf(x2, y), pointf(x2, y2), pointf(x, y2)], texture);
    33563704end;
    33573705
    33583706procedure TBGRADefaultBitmap.FillRoundRectAntialias(x, y, x2, y2, rx,ry: single;
    3359   c: TBGRAPixel; options: TRoundRectangleOptions);
    3360 begin
     3707  c: TBGRAPixel; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean);
     3708begin
     3709  if not pixelCenteredCoordinates then
     3710  begin
     3711    x -= 0.5;
     3712    y -= 0.5;
     3713    x2 -= 0.5;
     3714    y2 -= 0.5;
     3715  end;
    33613716  BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,c,False, LinearAntialiasing);
    33623717end;
    33633718
    33643719procedure TBGRADefaultBitmap.FillRoundRectAntialias(x, y, x2, y2, rx,
    3365   ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions);
    3366 begin
     3720  ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean);
     3721begin
     3722  if not pixelCenteredCoordinates then
     3723  begin
     3724    x -= 0.5;
     3725    y -= 0.5;
     3726    x2 -= 0.5;
     3727    y2 -= 0.5;
     3728  end;
    33673729  BGRAPolygon.FillRoundRectangleAntialiasWithTexture(self,x,y,x2,y2,rx,ry,options,texture, LinearAntialiasing);
    33683730end;
    33693731
    33703732procedure TBGRADefaultBitmap.EraseRoundRectAntialias(x, y, x2, y2, rx,
    3371   ry: single; alpha: byte; options: TRoundRectangleOptions);
    3372 begin
     3733  ry: single; alpha: byte; options: TRoundRectangleOptions; pixelCenteredCoordinates: boolean);
     3734begin
     3735  if not pixelCenteredCoordinates then
     3736  begin
     3737    x -= 0.5;
     3738    y -= 0.5;
     3739    x2 -= 0.5;
     3740    y2 -= 0.5;
     3741  end;
    33733742  BGRAPolygon.FillRoundRectangleAntialias(self,x,y,x2,y2,rx,ry,options,BGRA(0,0,0,alpha),True, LinearAntialiasing);
    33743743end;
     
    33913760  sUTF8: string; c: TBGRAPixel; align: TAlignment);
    33923761begin
    3393   FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),c,align);
     3762  with (PointF(x,y)-GetFontAnchorRotatedOffset(orientationTenthDegCCW)) do
     3763    FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),c,align);
    33943764end;
    33953765
     
    33973767  sUTF8: string; texture: IBGRAScanner; align: TAlignment);
    33983768begin
    3399   FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),texture,align);
     3769  with (PointF(x,y)-GetFontAnchorRotatedOffset(orientationTenthDegCCW)) do
     3770    FontRenderer.TextOutAngle(self,x,y,orientationTenthDegCCW,CleanTextOutString(sUTF8),texture,align);
     3771end;
     3772
     3773procedure TBGRADefaultBitmap.TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single);
     3774begin
     3775  InternalTextOutCurved(ACursor, sUTF8, AColor, nil, AAlign, ALetterSpacing);
     3776end;
     3777
     3778procedure TBGRADefaultBitmap.TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single);
     3779begin
     3780  InternalTextOutCurved(ACursor, sUTF8, BGRAPixelTransparent, ATexture, AAlign, ALetterSpacing);
    34003781end;
    34013782
     
    34093790  c: TBGRAPixel; align: TAlignment);
    34103791begin
    3411   FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),c,align);
     3792  with (PointF(x,y)-GetFontAnchorRotatedOffset) do
     3793    FontRenderer.TextOut(self,x,y,CleanTextOutString(sUTF8),c,align);
    34123794end;
    34133795
     
    34153797  sUTF8: string; style: TTextStyle; c: TBGRAPixel);
    34163798begin
    3417   FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,c);
     3799  with (PointF(x,y)-GetFontAnchorRotatedOffset(0)) do
     3800    FontRenderer.TextRect(self,ARect,system.round(x),system.round(y),sUTF8,style,c);
    34183801end;
    34193802
     
    34213804  style: TTextStyle; texture: IBGRAScanner);
    34223805begin
    3423   FontRenderer.TextRect(self,ARect,x,y,sUTF8,style,texture);
     3806  with (PointF(x,y)-GetFontAnchorRotatedOffset(0)) do
     3807    FontRenderer.TextRect(self,ARect,system.round(x),system.round(y),sUTF8,style,texture);
    34243808end;
    34253809
     
    34703854  w: single): ArrayOfTPointF;
    34713855begin
    3472   if Assigned(FArrow) then
    3473     Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    3474   else
    3475     Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption,JoinMiterLimit)
     3856  result := FPenStroker.ComputePolyline(points,w);
    34763857end;
    34773858
    34783859function TBGRADefaultBitmap.ComputeWidePolyline(const points: array of TPointF;
    3479   w: single; Closed: boolean): ArrayOfTPointF;
    3480 var
    3481   options: TBGRAPolyLineOptions;
    3482 begin
    3483   if not closed then options := [plRoundCapOpen] else options := [];
    3484   options += GetPolyLineOption;
    3485   if Assigned(FArrow) then
    3486     Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit,@FArrow.ComputeStartAt,FArrow.StartOffsetX,@FArrow.ComputeEndAt,FArrow.EndOffsetX)
    3487   else
    3488     Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,pecRound,pjsRound,FCustomPenStyle,options,JoinMiterLimit);
     3860  w: single; ClosedCap: boolean): ArrayOfTPointF;
     3861begin
     3862  result := FPenStroker.ComputePolyline(points,w,ClosedCap);
    34893863end;
    34903864
     
    34923866  w: single): ArrayOfTPointF;
    34933867begin
    3494   Result:= BGRAPen.ComputeWidePolylinePoints(points,w,BGRAWhite,LineCap,JoinStyle,FCustomPenStyle,GetPolyLineOption+[plCycle],JoinMiterLimit);
     3868  result := FPenStroker.ComputePolygon(points,w);
    34953869end;
    34963870
     
    35983972
    35993973procedure TBGRADefaultBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
    3600   texture: IBGRAScanner; ADrawMode: TDrawMode);
     3974  texture: IBGRAScanner; ADrawMode: TDrawMode; AOpacity: byte);
    36013975var
    36023976  scan: TBGRACustomScanner;
    36033977begin
    36043978  if AMask = nil then exit;
    3605   scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),texture);
     3979  scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),texture, AOpacity);
    36063980  self.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,ADrawMode);
    36073981  scan.Free;
     
    36264000  n: integer;
    36274001  colorMask,beforeBGR, afterBGR: longword;
    3628 begin
    3629   colorMask := NtoLE($00FFFFFF);
    3630   beforeBGR := NtoLE((before and $FF shl 16) + (before and $FF00) + (before shr 16 and $FF));
    3631   afterBGR  := NtoLE((after and $FF shl 16) + (after and $FF00) + (after shr 16 and $FF));
     4002  rAfter,gAfter,bAfter,rBefore,gBefore,bBefore: byte;
     4003begin
     4004  colorMask := LongWord(BGRA(255,255,255,0));
     4005  RedGreenBlue(before, rBefore,gBefore,bBefore);
     4006  RedGreenBlue(after, rAfter,gAfter,bAfter);
     4007  beforeBGR := LongWord(BGRA(rBefore,gBefore,bBefore,0));
     4008  afterBGR  := LongWord(BGRA(rAfter,gAfter,bAfter,0));
    36324009
    36334010  p := PLongWord(Data);
     
    36544031  for n := NbPixels - 1 downto 0 do
    36554032  begin
    3656     if p^ = before then
     4033    if PDWord(p)^ = DWord(before) then
    36574034      p^ := after;
    36584035    Inc(p);
     4036  end;
     4037  InvalidateBitmap;
     4038end;
     4039
     4040procedure TBGRADefaultBitmap.ReplaceColor(ABounds: TRect; before, after: TColor);
     4041var p: PLongWord;
     4042  xb,yb,xcount: integer;
     4043
     4044  colorMask,beforeBGR, afterBGR: longword;
     4045  rAfter,gAfter,bAfter,rBefore,gBefore,bBefore: byte;
     4046begin
     4047  colorMask := LongWord(BGRA(255,255,255,0));
     4048  RedGreenBlue(before, rBefore,gBefore,bBefore);
     4049  RedGreenBlue(after, rAfter,gAfter,bAfter);
     4050  beforeBGR := LongWord(BGRA(rBefore,gBefore,bBefore,0));
     4051  afterBGR  := LongWord(BGRA(rAfter,gAfter,bAfter,0));
     4052
     4053  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     4054  xcount := ABounds.Right-ABounds.Left;
     4055  for yb := ABounds.Top to ABounds.Bottom-1 do
     4056  begin
     4057    p := PLongWord(ScanLine[yb]+ABounds.Left);
     4058    for xb := xcount-1 downto 0 do
     4059    begin
     4060      if p^ and colorMask = beforeBGR then
     4061        p^ := (p^ and not ColorMask) or afterBGR;
     4062      Inc(p);
     4063    end;
     4064  end;
     4065  InvalidateBitmap;
     4066end;
     4067
     4068procedure TBGRADefaultBitmap.ReplaceColor(ABounds: TRect; before,
     4069  after: TBGRAPixel);
     4070var p: PBGRAPixel;
     4071  xb,yb,xcount: integer;
     4072begin
     4073  if before.alpha = 0 then
     4074  begin
     4075    ReplaceTransparent(ABounds,after);
     4076    exit;
     4077  end;
     4078  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     4079  xcount := ABounds.Right-ABounds.Left;
     4080  for yb := ABounds.Top to ABounds.Bottom-1 do
     4081  begin
     4082    p := ScanLine[yb]+ABounds.Left;
     4083    for xb := xcount-1 downto 0 do
     4084    begin
     4085      if PDWord(p)^ = DWord(before) then
     4086        p^ := after;
     4087      Inc(p);
     4088    end;
    36594089  end;
    36604090  InvalidateBitmap;
     
    36734103      p^ := after;
    36744104    Inc(p);
     4105  end;
     4106  InvalidateBitmap;
     4107end;
     4108
     4109procedure TBGRADefaultBitmap.ReplaceTransparent(ABounds: TRect;
     4110  after: TBGRAPixel);
     4111var p: PBGRAPixel;
     4112  xb,yb,xcount: integer;
     4113begin
     4114  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     4115  xcount := ABounds.Right-ABounds.Left;
     4116  for yb := ABounds.Top to ABounds.Bottom-1 do
     4117  begin
     4118    p := ScanLine[yb]+ABounds.Left;
     4119    for xb := xcount-1 downto 0 do
     4120    begin
     4121      if p^.alpha = 0 then
     4122        p^ := after;
     4123      Inc(p);
     4124    end;
    36754125  end;
    36764126  InvalidateBitmap;
     
    38264276procedure TBGRADefaultBitmap.GradientFill(x, y, x2, y2: integer;
    38274277  c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    3828   gammaColorCorrection: boolean = True; Sinus: Boolean=False);
    3829 begin
    3830   BGRAGradientFill(self, x, y, x2, y2, c1, c2, gtype, o1, o2, mode, gammaColorCorrection, Sinus);
     4278  gammaColorCorrection: boolean; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm);
     4279var
     4280  scanner: TBGRAGradientScanner;
     4281begin
     4282  if (c1.alpha = 0) and (c2.alpha = 0) then
     4283    FillRect(x, y, x2, y2, BGRAPixelTransparent, mode)
     4284  else
     4285  if ditherAlgo <> daNearestNeighbor then
     4286    GradientFillDithered(x,y,x2,y2,c1,c2,gtype,o1,o2,mode,gammaColorCorrection,sinus,ditherAlgo)
     4287  else
     4288  begin
     4289    scanner := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus);
     4290    FillRect(x,y,x2,y2,scanner,mode);
     4291    scanner.Free;
     4292  end;
    38314293end;
    38324294
    38334295procedure TBGRADefaultBitmap.GradientFill(x, y, x2, y2: integer;
    38344296  gradient: TBGRACustomGradient; gtype: TGradientType; o1, o2: TPointF;
    3835   mode: TDrawMode; Sinus: Boolean);
     4297  mode: TDrawMode; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm);
    38364298var
    38374299  scanner: TBGRAGradientScanner;
    38384300begin
     4301  if ditherAlgo <> daNearestNeighbor then
     4302    GradientFillDithered(x,y,x2,y2,gradient,gtype,o1,o2,mode,sinus,ditherAlgo)
     4303  else
     4304  begin
     4305    scanner := TBGRAGradientScanner.Create(gradient,gtype,o1,o2,sinus);
     4306    FillRect(x,y,x2,y2,scanner,mode);
     4307    scanner.Free;
     4308  end;
     4309end;
     4310
     4311procedure TBGRADefaultBitmap.GradientFillDithered(x, y, x2, y2: integer; c1,
     4312  c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF;
     4313  mode: TDrawMode; gammaColorCorrection: boolean; Sinus: Boolean;
     4314  ditherAlgo: TDitheringAlgorithm);
     4315var
     4316  scanner: TBGRAGradientScanner;
     4317begin
     4318  if (c1.alpha = 0) and (c2.alpha = 0) then
     4319    FillRect(x, y, x2, y2, BGRAPixelTransparent, dmSet)
     4320  else
     4321  begin
     4322    scanner := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus);
     4323    FillRect(x,y,x2,y2,scanner,mode,ditherAlgo);
     4324    scanner.Free;
     4325  end;
     4326end;
     4327
     4328procedure TBGRADefaultBitmap.GradientFillDithered(x, y, x2, y2: integer;
     4329  gradient: TBGRACustomGradient; gtype: TGradientType; o1, o2: TPointF;
     4330  mode: TDrawMode; Sinus: Boolean; ditherAlgo: TDitheringAlgorithm);
     4331var
     4332  scanner: TBGRAGradientScanner;
     4333begin
    38394334  scanner := TBGRAGradientScanner.Create(gradient,gtype,o1,o2,sinus);
    3840   FillRect(x,y,x2,y2,scanner,mode);
     4335  FillRect(x,y,x2,y2,scanner,mode,ditherAlgo);
    38414336  scanner.Free;
    38424337end;
     
    38504345function TBGRADefaultBitmap.ScanAtInteger(X, Y: integer): TBGRAPixel;
    38514346begin
    3852   if FData <> nil then
    3853     result := (GetScanlineFast(PositiveMod(Y+ScanOffset.Y, FHeight))+PositiveMod(X+ScanOffset.X, FWidth))^
     4347  if (FScanWidth <> 0) and (FScanHeight <> 0) then
     4348    result := (GetScanlineFast(PositiveMod(Y+ScanOffset.Y, FScanHeight))+PositiveMod(X+ScanOffset.X, FScanWidth))^
    38544349  else
    38554350    result := BGRAPixelTransparent;
     
    38594354procedure TBGRADefaultBitmap.ScanMoveTo(X, Y: Integer);
    38604355begin
    3861   if FData = nil then exit;
     4356  if (FScanWidth = 0) or (FScanHeight = 0) then exit;
    38624357  LoadFromBitmapIfNeeded;
    3863   FScanCurX := PositiveMod(X+ScanOffset.X, FWidth);
    3864   FScanCurY := PositiveMod(Y+ScanOffset.Y, FHeight);
     4358  FScanCurX := PositiveMod(X+ScanOffset.X, FScanWidth);
     4359  FScanCurY := PositiveMod(Y+ScanOffset.Y, FScanHeight);
    38654360  FScanPtr := ScanLine[FScanCurY];
    38664361end;
     
    38684363function TBGRADefaultBitmap.ScanNextPixel: TBGRAPixel;
    38694364begin
    3870   if FData <> nil then
     4365  if (FScanWidth <> 0) and (FScanHeight <> 0) then
    38714366  begin
    38724367    result := (FScanPtr+FScanCurX)^;
    38734368    inc(FScanCurX);
    3874     if FScanCurX = FWidth then //cycle
     4369    if FScanCurX = FScanWidth then //cycle
    38754370      FScanCurX := 0;
    38764371  end
     
    38844379  iFactX,iFactY: Int32or64;
    38854380begin
    3886   if FData = nil then
     4381  if (FScanWidth = 0) or (FScanHeight = 0) then
    38874382  begin
    38884383    result := BGRAPixelTransparent;
     
    38924387  ix := round(x*256);
    38934388  iy := round(y*256);
     4389  if ScanInterpolationFilter = rfBox then
     4390  begin
     4391    ix := PositiveMod((ix+128)+(ScanOffset.X shl 8), FScanWidth shl 8) shr 8;
     4392    iy := PositiveMod((iy+128)+(ScanOffset.Y shl 8), FScanHeight shl 8) shr 8;
     4393    result := (GetScanlineFast(iy)+ix)^;
     4394    exit;
     4395  end;
    38944396  iFactX := ix and 255;
    38954397  iFactY := iy and 255;
    3896   ix := PositiveMod(ix+(ScanOffset.X shl 8), FWidth shl 8) shr 8;
    3897   iy := PositiveMod(iy+(ScanOffset.Y shl 8), FHeight shl 8) shr 8;
     4398  ix := PositiveMod(ix+(ScanOffset.X shl 8), FScanWidth shl 8) shr 8;
     4399  iy := PositiveMod(iy+(ScanOffset.Y shl 8), FScanHeight shl 8) shr 8;
    38984400  if (iFactX = 0) and (iFactY = 0) then
    38994401  begin
     
    39204422  c: TBGRAPixel;
    39214423begin
     4424  if (FScanWidth <= 0) or (FScanHeight <= 0) then
     4425  begin
     4426    if mode = dmSet then
     4427      FillDWord(pdest^, count, DWord(BGRAPixelTransparent));
     4428    exit;
     4429  end;
    39224430  case mode of
    39234431    dmLinearBlend:
     
    39364444      while count > 0 do
    39374445      begin
    3938         nbCopy := FWidth-FScanCurX;
     4446        nbCopy := FScanWidth-FScanCurX;
    39394447        if count < nbCopy then nbCopy := count;
    39404448        move((FScanPtr+FScanCurX)^,pdest^,nbCopy*sizeof(TBGRAPixel));
    39414449        inc(pdest,nbCopy);
    39424450        inc(FScanCurX,nbCopy);
    3943         if FScanCurX = FWidth then FScanCurX := 0;
     4451        if FScanCurX = FScanWidth then FScanCurX := 0;
    39444452        dec(count,nbCopy);
    39454453      end;
     
    39944502  p: PBGRAPixel;
    39954503  n: integer;
     4504  colormask: longword;
    39964505begin
    39974506  if CanvasAlphaCorrection then
    39984507  begin
    39994508    p := FData;
     4509    colormask := longword(BGRA(255,255,255,0));
    40004510    for n := NbPixels - 1 downto 0 do
    40014511    begin
    4002       if (longword(p^) and $FFFFFF <> 0) and (p^.alpha = 0) then
     4512      if (longword(p^) and colormask <> 0) and (p^.alpha = 0) then
    40034513        p^.alpha := FCanvasOpacity;
    40044514      Inc(p);
     
    42994809  Parameters are the bitmap origin, the end of the horizontal axis and the end of the vertical axis.
    43004810  The output bounds correspond to the pixels that will be affected in the destination. }
    4301 procedure TBGRADefaultBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
    4302   Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte);
     4811procedure TBGRADefaultBitmap.PutImageAffine(AMatrix: TAffineMatrix;
     4812  Source: TBGRACustomBitmap; AOutputBounds: TRect;
     4813  AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte);
    43034814var affine: TBGRAAffineBitmapTransform;
    4304     SourceBounds: TRect;
    4305 begin
    4306   if (Source = nil) or (AOpacity = 0) then exit;
     4815    sourceBounds: TRect;
     4816begin
     4817  if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit;
    43074818  IntersectRect(AOutputBounds,AOutputBounds,ClipRect);
    43084819  if IsRectEmpty(AOutputBounds) then exit;
    43094820
    4310   if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
    4311      (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
    4312      (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
    4313   begin
    4314     SourceBounds := AOutputBounds;
    4315     OffsetRect(SourceBounds, -round(origin.x),-round(origin.y));
    4316     IntersectRect(SourceBounds,SourceBounds,rect(0,0,Source.Width,Source.Height));
    4317     PutImagePart(round(origin.x)+SourceBounds.Left,round(origin.y)+SourceBounds.Top,Source,SourceBounds,AMode,AOpacity);
    4318     exit;
    4319   end;
    4320 
    4321   { Create affine transformation }
    4322   affine := TBGRAAffineBitmapTransform.Create(Source, false, AResampleFilter);
    4323   affine.GlobalOpacity := AOpacity;
    4324   affine.Fit(Origin,HAxis,VAxis);
    4325   FillRect(AOutputBounds,affine,AMode);
    4326   affine.Free;
     4821  if IsAffineRoughlyTranslation(AMatrix, rect(0,0,Source.Width,Source.Height)) then
     4822  begin
     4823    sourceBounds := AOutputBounds;
     4824    OffsetRect(sourceBounds, -round(AMatrix[1,3]),-round(AMatrix[2,3]));
     4825    IntersectRect(sourceBounds,sourceBounds,rect(0,0,Source.Width,Source.Height));
     4826    PutImagePart(round(AMatrix[1,3])+sourceBounds.Left,round(AMatrix[2,3])+sourceBounds.Top,Source,sourceBounds,AMode,AOpacity);
     4827  end else
     4828  begin
     4829    affine := TBGRAAffineBitmapTransform.Create(Source, false, AResampleFilter);
     4830    affine.GlobalOpacity := AOpacity;
     4831    affine.ViewMatrix := AMatrix;
     4832    FillRect(AOutputBounds,affine,AMode);
     4833    affine.Free;
     4834  end;
     4835end;
     4836
     4837function TBGRADefaultBitmap.GetImageAffineBounds(AMatrix: TAffineMatrix;
     4838  ASourceBounds: TRect; AClipOutput: boolean): TRect;
     4839const pointMargin = 0.5 - 1/512;
     4840
     4841  procedure FirstPoint(pt: TPointF);
     4842  begin
     4843    result.Left := round(pt.X);
     4844    result.Top := round(pt.Y);
     4845    result.Right := round(pt.X)+1;
     4846    result.Bottom := round(pt.Y)+1;
     4847  end;
     4848
     4849  //include specified point in the bounds
     4850  procedure IncludePoint(pt: TPointF);
     4851  begin
     4852    if round(pt.X) < result.Left then result.Left := round(pt.X);
     4853    if round(pt.Y) < result.Top then result.Top := round(pt.Y);
     4854    if round(pt.X)+1 > result.Right then result.Right := round(pt.X)+1;
     4855    if round(pt.Y)+1 > result.Bottom then result.Bottom := round(pt.Y)+1;
     4856  end;
     4857
     4858begin
     4859  result := EmptyRect;
     4860  if IsRectEmpty(ASourceBounds) then exit;
     4861  if IsAffineRoughlyTranslation(AMatrix,ASourceBounds) then
     4862  begin
     4863    result := ASourceBounds;
     4864    OffsetRect(result,round(AMatrix[1,3]),round(AMatrix[2,3]));
     4865  end else
     4866  begin
     4867    FirstPoint(AMatrix*PointF(ASourceBounds.Left-pointMargin,ASourceBounds.Top-pointMargin));
     4868    IncludePoint(AMatrix*PointF(ASourceBounds.Right-1+pointMargin,ASourceBounds.Top-pointMargin));
     4869    IncludePoint(AMatrix*PointF(ASourceBounds.Left-pointMargin,ASourceBounds.Bottom-1+pointMargin));
     4870    IncludePoint(AMatrix*PointF(ASourceBounds.Right-1+pointMargin,ASourceBounds.Bottom-1+pointMargin));
     4871  end;
     4872  if AClipOutput then IntersectRect(result,result,ClipRect);
    43274873end;
    43284874
     
    44344980function TBGRADefaultBitmap.FilterSmooth: TBGRACustomBitmap;
    44354981begin
    4436   Result := BGRAFilters.FilterBlurRadialPrecise(self, 0.3);
     4982  Result := BGRAFilters.FilterBlurRadial(self, 3, rbPrecise);
    44374983end;
    44384984
     
    44795025end;
    44805026
    4481 function TBGRADefaultBitmap.FilterBlurRadial(radius: integer;
     5027function TBGRADefaultBitmap.FilterBlurRadial(radius: single;
    44825028  blurType: TRadialBlurType): TBGRACustomBitmap;
    44835029begin
     
    44855031end;
    44865032
    4487 function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radius: integer;
     5033function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radius: single;
    44885034  blurType: TRadialBlurType): TBGRACustomBitmap;
    44895035var task: TFilterTask;
     
    44975043end;
    44985044
     5045function TBGRADefaultBitmap.FilterBlurRadial(radiusX, radiusY: single;
     5046  blurType: TRadialBlurType): TBGRACustomBitmap;
     5047begin
     5048  Result := BGRAFilters.FilterBlurRadial(self, radiusX,radiusY, blurType);
     5049end;
     5050
     5051function TBGRADefaultBitmap.FilterBlurRadial(ABounds: TRect; radiusX,
     5052  radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap;
     5053var task: TFilterTask;
     5054begin
     5055  task := BGRAFilters.CreateRadialBlurTask(self, ABounds, radiusX,radiusY, blurType);
     5056  try
     5057    result := task.Execute;
     5058  finally
     5059    task.Free;
     5060  end;
     5061end;
     5062
    44995063function TBGRADefaultBitmap.FilterPixelate(pixelSize: integer;
    45005064  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
     
    45035067end;
    45045068
    4505 function TBGRADefaultBitmap.FilterBlurMotion(distance: integer;
     5069function TBGRADefaultBitmap.FilterBlurMotion(distance: single;
    45065070  angle: single; oriented: boolean): TBGRACustomBitmap;
    45075071begin
     
    45095073end;
    45105074
    4511 function TBGRADefaultBitmap.FilterBlurMotion(ABounds: TRect; distance: integer;
     5075function TBGRADefaultBitmap.FilterBlurMotion(ABounds: TRect; distance: single;
    45125076  angle: single; oriented: boolean): TBGRACustomBitmap;
    45135077var task: TFilterTask;
     
    45395103end;
    45405104
    4541 function TBGRADefaultBitmap.FilterEmboss(angle: single): TBGRACustomBitmap;
    4542 begin
    4543   Result := BGRAFilters.FilterEmboss(self, angle);
    4544 end;
    4545 
    4546 function TBGRADefaultBitmap.FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap;
    4547 begin
    4548   Result := BGRAFilters.FilterEmboss(self, angle, ABounds);
     5105function TBGRADefaultBitmap.FilterEmboss(angle: single;
     5106  AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap;
     5107begin
     5108  Result := BGRAFilters.FilterEmboss(self, angle, AStrength, AOptions);
     5109end;
     5110
     5111function TBGRADefaultBitmap.FilterEmboss(angle: single; ABounds: TRect;
     5112  AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap;
     5113begin
     5114  Result := BGRAFilters.FilterEmboss(self, angle, ABounds, AStrength, AOptions);
    45495115end;
    45505116
     
    45925158begin
    45935159  Result := BGRAFilters.FilterRotate(self, origin, angle, correctBlur);
     5160end;
     5161
     5162function TBGRADefaultBitmap.FilterAffine(AMatrix: TAffineMatrix;
     5163  correctBlur: boolean): TBGRACustomBitmap;
     5164begin
     5165  Result := NewBitmap(Width,Height);
     5166  Result.PutImageAffine(AMatrix,self,255,correctBlur);
    45945167end;
    45955168
     
    46205193  if pix.alpha = 0 then
    46215194    result := clNone else
    4622      result := pix.red + pix.green shl 8 + pix.blue shl 16;
     5195     result := RGBToColor(pix.red,pix.green,pix.blue);
    46235196  {$hints on}
    46245197end;
     
    46515224end;
    46525225
    4653 function TBGRADefaultBitmap.CreateAdaptedPngWriter: TFPWriterPNG;
    4654 begin
    4655   result := TFPWriterPNG.Create;
    4656   result.Indexed := False;
    4657   result.UseAlpha := HasTransparentPixels;
    4658   result.WordSized := false;
     5226function TBGRADefaultBitmap.GetPenJoinStyle: TPenJoinStyle;
     5227begin
     5228  result := FPenStroker.JoinStyle;
     5229end;
     5230
     5231procedure TBGRADefaultBitmap.SetPenJoinStyle(const AValue: TPenJoinStyle);
     5232begin
     5233  FPenStroker.JoinStyle := AValue;
     5234end;
     5235
     5236function TBGRADefaultBitmap.GetPenMiterLimit: single;
     5237begin
     5238  result := FPenStroker.MiterLimit;
     5239end;
     5240
     5241procedure TBGRADefaultBitmap.SetPenMiterLimit(const AValue: single);
     5242begin
     5243  FPenStroker.MiterLimit := AValue;
    46595244end;
    46605245
     
    48255410  It is NOT EXACTLY an involution, when applied twice, some color information is lost }
    48265411procedure TBGRADefaultBitmap.Negative;
    4827 var
    4828   p: PBGRAPixel;
    4829   n: integer;
    4830 begin
    4831   LoadFromBitmapIfNeeded;
    4832   p := Data;
    4833   for n := NbPixels - 1 downto 0 do
    4834   begin
    4835     if p^.alpha <> 0 then
    4836     begin
    4837       p^.red   := GammaCompressionTab[not GammaExpansionTab[p^.red]];
    4838       p^.green := GammaCompressionTab[not GammaExpansionTab[p^.green]];
    4839       p^.blue  := GammaCompressionTab[not GammaExpansionTab[p^.blue]];
    4840     end;
    4841     Inc(p);
    4842   end;
    4843   InvalidateBitmap;
     5412begin
     5413  TBGRAFilterScannerNegative.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), True);
    48445414end;
    48455415
    48465416procedure TBGRADefaultBitmap.NegativeRect(ABounds: TRect);
    4847 var p: PBGRAPixel;
    4848   xb,yb,xcount: integer;
    48495417begin
    48505418  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
    4851   xcount := ABounds.Right-ABounds.Left;
    4852   for yb := ABounds.Top to ABounds.Bottom-1 do
    4853   begin
    4854     p := ScanLine[yb]+ABounds.Left;
    4855     for xb := xcount-1 downto 0 do
    4856     begin
    4857       if p^.alpha <> 0 then
    4858       begin
    4859         p^.red   := GammaCompressionTab[not GammaExpansionTab[p^.red]];
    4860         p^.green := GammaCompressionTab[not GammaExpansionTab[p^.green]];
    4861         p^.blue  := GammaCompressionTab[not GammaExpansionTab[p^.blue]];
    4862       end;
    4863       Inc(p);
    4864     end;
    4865   end;
     5419  TBGRAFilterScannerNegative.ComputeFilterInplace(self, ABounds, True);
    48665420end;
    48675421
     
    48705424  It is an involution, i.e it does nothing when applied twice }
    48715425procedure TBGRADefaultBitmap.LinearNegative;
    4872 var
    4873   p: PBGRAPixel;
    4874   n: integer;
    4875 begin
    4876   LoadFromBitmapIfNeeded;
    4877   p := Data;
    4878   for n := NbPixels - 1 downto 0 do
    4879   begin
    4880     if p^.alpha <> 0 then
    4881     begin
    4882       p^.red   := not p^.red;
    4883       p^.green := not p^.green;
    4884       p^.blue  := not p^.blue;
    4885     end;
    4886     Inc(p);
    4887   end;
    4888   InvalidateBitmap;
     5426begin
     5427  TBGRAFilterScannerNegative.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), False);
    48895428end;
    48905429
    48915430procedure TBGRADefaultBitmap.LinearNegativeRect(ABounds: TRect);
    4892 var p: PBGRAPixel;
    4893   xb,yb,xcount: integer;
    48945431begin
    48955432  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
    4896   xcount := ABounds.Right-ABounds.Left;
    4897   for yb := ABounds.Top to ABounds.Bottom-1 do
    4898   begin
    4899     p := ScanLine[yb]+ABounds.Left;
    4900     for xb := xcount-1 downto 0 do
    4901     begin
    4902       if p^.alpha <> 0 then
    4903       begin
    4904         p^.red   := not p^.red;
    4905         p^.green := not p^.green;
    4906         p^.blue  := not p^.blue;
    4907       end;
    4908       Inc(p);
    4909     end;
    4910   end;
    4911 end;
    4912 
    4913 procedure TBGRADefaultBitmap.InplaceGrayscale;
    4914 begin
    4915   InplaceGrayscale(rect(0,0,Width,Height));
    4916 end;
    4917 
    4918 procedure TBGRADefaultBitmap.InplaceGrayscale(ABounds: TRect);
    4919 var
    4920   task: TFilterTask;
    4921 begin
    4922   task := CreateGrayscaleTask(self, ABounds);
    4923   task.Destination := self;
    4924   task.Execute;
    4925   task.Free;
     5433  TBGRAFilterScannerNegative.ComputeFilterInplace(self, ABounds, False);
     5434end;
     5435
     5436procedure TBGRADefaultBitmap.InplaceGrayscale(AGammaCorrection: boolean = true);
     5437begin
     5438  TBGRAFilterScannerGrayscale.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), AGammaCorrection);
     5439end;
     5440
     5441procedure TBGRADefaultBitmap.InplaceGrayscale(ABounds: TRect; AGammaCorrection: boolean = true);
     5442begin
     5443  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     5444  TBGRAFilterScannerGrayscale.ComputeFilterInplace(self, ABounds, AGammaCorrection);
     5445end;
     5446
     5447procedure TBGRADefaultBitmap.InplaceNormalize(AEachChannel: boolean);
     5448begin
     5449  InplaceNormalize(rect(0,0,Width,Height),AEachChannel);
     5450end;
     5451
     5452procedure TBGRADefaultBitmap.InplaceNormalize(ABounds: TRect;
     5453  AEachChannel: boolean);
     5454var scanner: TBGRAFilterScannerNormalize;
     5455begin
     5456  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     5457  scanner := TBGRAFilterScannerNormalize.Create(self,Point(0,0),ABounds,AEachChannel);
     5458  FillRect(ABounds,scanner,dmSet);
     5459  scanner.Free;
    49265460end;
    49275461
     
    49305464  It is an involution, i.e it does nothing when applied twice }
    49315465procedure TBGRADefaultBitmap.SwapRedBlue;
     5466begin
     5467  TBGRAFilterScannerSwapRedBlue.ComputeFilterInplace(self, rect(0,0,FWidth,FHeight), False);
     5468end;
     5469
     5470procedure TBGRADefaultBitmap.SwapRedBlue(ARect: TRect);
     5471begin
     5472  if not CheckClippedRectBounds(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom) then exit;
     5473  TBGRAFilterScannerSwapRedBlue.ComputeFilterInplace(self, ARect, False);
     5474end;
     5475
     5476{ Convert a grayscale image into a black image with alpha value }
     5477procedure TBGRADefaultBitmap.GrayscaleToAlpha;
    49325478var
    49335479  n:    integer;
    4934   temp: longword;
    49355480  p:    PLongword;
    49365481begin
     
    49415486    exit;
    49425487  repeat
    4943     temp := LEtoN(p^);
    4944     p^   := NtoLE(((temp and $FF) shl 16) or ((temp and $FF0000) shr 16) or
    4945       temp and $FF00FF00);
     5488    p^   := (p^ shr TBGRAPixel_RedShift and $FF) shl TBGRAPixel_AlphaShift;
    49465489    Inc(p);
    49475490    Dec(n);
     
    49505493end;
    49515494
    4952 { Convert a grayscale image into a black image with alpha value }
    4953 procedure TBGRADefaultBitmap.GrayscaleToAlpha;
     5495procedure TBGRADefaultBitmap.AlphaToGrayscale;
    49545496var
    49555497  n:    integer;
     
    49635505    exit;
    49645506  repeat
    4965     temp := LEtoN(p^);
    4966     p^   := NtoLE((temp and $FF) shl 24);
    4967     Inc(p);
    4968     Dec(n);
    4969   until n = 0;
    4970   InvalidateBitmap;
    4971 end;
    4972 
    4973 procedure TBGRADefaultBitmap.AlphaToGrayscale;
    4974 var
    4975   n:    integer;
    4976   temp: longword;
    4977   p:    PLongword;
    4978 begin
    4979   LoadFromBitmapIfNeeded;
    4980   p := PLongword(Data);
    4981   n := NbPixels;
    4982   if n = 0 then
    4983     exit;
    4984   repeat
    4985     temp := LEtoN(p^ shr 24);
    4986     p^   := NtoLE(temp or (temp shl 8) or (temp shl 16) or $FF000000);
     5507    temp := (p^ shr TBGRAPixel_AlphaShift) and $ff;
     5508    p^   := (temp shl TBGRAPixel_RedShift) or (temp shl TBGRAPixel_GreenShift)
     5509         or (temp shl TBGRAPixel_BlueShift) or ($ff shl TBGRAPixel_AlphaShift);
    49875510    Inc(p);
    49885511    Dec(n);
     
    50455568    end;
    50465569  end;
     5570end;
     5571
     5572procedure TBGRADefaultBitmap.ApplyGlobalOpacity(ABounds: TRect; alpha: byte);
     5573var p: PBGRAPixel;
     5574  xb,yb,xcount: integer;
     5575begin
     5576  if not IntersectRect(ABounds,ABounds,ClipRect) then exit;
     5577  xcount := ABounds.Right-ABounds.Left;
     5578  for yb := ABounds.Top to ABounds.Bottom-1 do
     5579  begin
     5580    p := ScanLine[yb]+ABounds.Left;
     5581    for xb := xcount-1 downto 0 do
     5582    begin
     5583      p^.alpha := ApplyOpacity(p^.alpha, alpha);
     5584      Inc(p);
     5585    end;
     5586  end;
     5587  InvalidateBitmap;
    50475588end;
    50485589
     
    51275668end;
    51285669
    5129 { Get bounds of non zero values of specified channel }
    5130 function TBGRADefaultBitmap.GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect;
    5131 begin
    5132   result := GetImageBounds([Channel], ANothingValue);
    5133 end;
    5134 
    5135 function TBGRADefaultBitmap.GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect;
    5136 var
    5137   minx, miny, maxx, maxy: integer;
    5138   xb, xb2, yb: integer;
    5139   p:      PDWord;
    5140   colorMask, colorZeros: DWord;
    5141 begin
    5142   maxx := -1;
    5143   maxy := -1;
    5144   minx := self.Width;
    5145   miny := self.Height;
    5146   colorMask := 0;
    5147   colorZeros := 0;
    5148   if cBlue in Channels then
    5149   begin
    5150     colorMask := colorMask or $ff;
    5151     colorZeros:= colorZeros or ANothingValue;
    5152   end;
    5153   if cGreen in Channels then
    5154   begin
    5155     colorMask := colorMask or $ff00;
    5156     colorZeros:= colorZeros or (ANothingValue shl 8);
    5157   end;
    5158   if cRed in Channels then
    5159   begin
    5160     colorMask := colorMask or $ff0000;
    5161     colorZeros:= colorZeros or (ANothingValue shl 16);
    5162   end;
    5163   if cAlpha in Channels then
    5164   begin
    5165     colorMask := colorMask or $ff000000;
    5166     colorZeros:= colorZeros or (ANothingValue shl 24);
    5167   end;
    5168   colorMask := NtoLE(colorMask);
    5169   colorZeros := NtoLE(colorZeros);
    5170   for yb := 0 to self.Height - 1 do
    5171   begin
    5172     p := PDWord(self.ScanLine[yb]);
    5173     for xb := 0 to self.Width - 1 do
    5174     begin
    5175       if (p^ and colorMask) <> colorZeros then
    5176       begin
    5177         if xb < minx then
    5178           minx := xb;
    5179         if yb < miny then
    5180           miny := yb;
    5181         if xb > maxx then
    5182           maxx := xb;
    5183         if yb > maxy then
    5184           maxy := yb;
    5185 
    5186         inc(p, self.width-1-xb);
    5187         for xb2 := self.Width-1 downto xb+1 do
    5188         begin
    5189           if (p^ and colorMask) <> colorZeros then
    5190           begin
    5191             if xb2 > maxx then
    5192               maxx := xb2;
    5193             break;
    5194           end;
    5195           dec(p);
    5196         end;
    5197         break;
    5198       end;
    5199       Inc(p);
    5200     end;
    5201   end;
    5202   if minx > maxx then
    5203   begin
    5204     Result.left   := 0;
    5205     Result.top    := 0;
    5206     Result.right  := 0;
    5207     Result.bottom := 0;
    5208   end
    5209   else
    5210   begin
    5211     Result.left   := minx;
    5212     Result.top    := miny;
    5213     Result.right  := maxx + 1;
    5214     Result.bottom := maxy + 1;
    5215   end;
    5216 end;
    5217 
    52185670function TBGRADefaultBitmap.GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect;
    52195671var
     
    53785830  begin
    53795831    if LineOrder = riloTopToBottom then
    5380       ptrbmp := TBGRAPtrBitmap.Create(Width,Bottom-Top,ScanLine[Top]) else
    5381       ptrbmp := TBGRAPtrBitmap.Create(Width,Bottom-Top,ScanLine[Bottom-1]);
     5832      ptrbmp := CreatePtrBitmap(Width,Bottom-Top,ScanLine[Top]) else
     5833      ptrbmp := CreatePtrBitmap(Width,Bottom-Top,ScanLine[Bottom-1]);
    53825834    ptrbmp.LineOrder := LineOrder;
    53835835    result := ptrbmp;
    53845836  end;
    5385 end;
    5386 
    5387 { Draw BGRA data to a canvas with transparency }
    5388 procedure TBGRADefaultBitmap.DataDrawTransparent(ACanvas: TCanvas;
    5389   Rect: TRect; AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
    5390 var
    5391   Temp:     TBitmap;
    5392   RawImage: TRawImage;
    5393   BitmapHandle, MaskHandle: HBitmap;
    5394 begin
    5395   RawImage.Init;
    5396   RawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight);
    5397   RawImage.Description.LineOrder := ALineOrder;
    5398   RawImage.Data     := PByte(AData);
    5399   RawImage.DataSize := AWidth * AHeight * sizeof(TBGRAPixel);
    5400   if not RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False) then
    5401     raise FPImageException.Create('Failed to create bitmap handle');
    5402   Temp := TBitmap.Create;
    5403   Temp.Handle := BitmapHandle;
    5404   Temp.MaskHandle := MaskHandle;
    5405   ACanvas.StretchDraw(Rect, Temp);
    5406   Temp.Free;
    5407 end;
    5408 
    5409 { Draw BGRA data to a canvas without transparency }
    5410 procedure TBGRADefaultBitmap.DataDrawOpaque(ACanvas: TCanvas;
    5411   Rect: TRect; AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
    5412 var
    5413   Temp:      TBitmap;
    5414   RawImage:  TRawImage;
    5415   BitmapHandle, MaskHandle: HBitmap;
    5416   TempData:  Pointer;
    5417   x, y:      integer;
    5418   PTempData: PByte;
    5419   PSource:   PByte;
    5420   ADataSize: integer;
    5421   ALineEndMargin: integer;
    5422   CreateResult: boolean;
    5423   {$IFDEF DARWIN}
    5424   TempShift: Byte;
    5425   {$ENDIF}
    5426 begin
    5427   if (AHeight = 0) or (AWidth = 0) then
    5428     exit;
    5429 
    5430   ALineEndMargin := (4 - ((AWidth * 3) and 3)) and 3;
    5431   ADataSize      := (AWidth * 3 + ALineEndMargin) * AHeight;
    5432 
    5433      {$HINTS OFF}
    5434   GetMem(TempData, ADataSize);
    5435      {$HINTS ON}
    5436   PTempData := TempData;
    5437   PSource   := AData;
    5438 
    5439 {$IFDEF DARWIN} //swap red and blue values
    5440   for y := 0 to AHeight - 1 do
    5441   begin
    5442     for x := 0 to AWidth - 1 do
    5443     begin
    5444       PTempData^ := (PSource+2)^;
    5445       (PTempData+1)^ := (PSource+1)^;
    5446       (PTempData+2)^ := PSource^;
    5447       inc(PTempData,3);
    5448       inc(PSource,4);
    5449     end;
    5450     Inc(PTempData, ALineEndMargin);
    5451   end;
    5452 {$ELSE}
    5453   for y := 0 to AHeight - 1 do
    5454   begin
    5455     for x := 0 to AWidth - 1 do
    5456     begin
    5457       PWord(PTempData)^ := PWord(PSource)^;
    5458       (PTempData+2)^ := (PSource+2)^;
    5459       Inc(PTempData,3);
    5460       Inc(PSource, 4);
    5461     end;
    5462     Inc(PTempData, ALineEndMargin);
    5463   end;
    5464 {$ENDIF}
    5465 
    5466   RawImage.Init;
    5467   RawImage.Description.Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight);
    5468 {$IFDEF DARWIN}
    5469   TempShift := RawImage.Description.RedShift;
    5470   RawImage.Description.RedShift := RawImage.Description.BlueShift;
    5471   RawImage.Description.BlueShift := TempShift;
    5472 {$ENDIF}
    5473 
    5474   RawImage.Description.LineOrder := ALineOrder;
    5475   RawImage.Description.LineEnd := rileDWordBoundary;
    5476 
    5477   if integer(RawImage.Description.BytesPerLine) <> AWidth * 3 + ALineEndMargin then
    5478   begin
    5479     FreeMem(TempData);
    5480     raise FPImageException.Create('Line size is inconsistant');
    5481   end;
    5482   RawImage.Data     := PByte(TempData);
    5483   RawImage.DataSize := ADataSize;
    5484 
    5485   CreateResult := RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False);
    5486   FreeMem(TempData);
    5487 
    5488   if not CreateResult then
    5489     raise FPImageException.Create('Failed to create bitmap handle');
    5490 
    5491   Temp := TBitmap.Create;
    5492   Temp.Handle := BitmapHandle;
    5493   Temp.MaskHandle := MaskHandle;
    5494   ACanvas.StretchDraw(Rect, Temp);
    5495   Temp.Free;
    54965837end;
    54975838
     
    55145855end;
    55155856
    5516 procedure TBGRADefaultBitmap.RebuildBitmap;
    5517 var
    5518   RawImage: TRawImage;
    5519   BitmapHandle, MaskHandle: HBitmap;
    5520 begin
    5521   if FBitmap <> nil then
    5522     FBitmap.Free;
    5523 
    5524   FBitmap := TBitmapTracker.Create(self);
    5525 
    5526   if (FWidth > 0) and (FHeight > 0) then
    5527   begin
    5528     RawImage.Init;
    5529     RawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(FWidth, FHeight);
    5530     RawImage.Description.LineOrder := FLineOrder;
    5531     RawImage.Data     := PByte(FData);
    5532     RawImage.DataSize := FWidth * FHeight * sizeof(TBGRAPixel);
    5533     if not RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False) then
    5534       raise FPImageException.Create('Failed to create bitmap handle');
    5535     FBitmap.Handle     := BitmapHandle;
    5536     FBitmap.MaskHandle := MaskHandle;
    5537   end;
    5538 
    5539   FBitmap.Canvas.AntialiasingMode := amOff;
    5540   FBitmapModified := False;
     5857function TBGRADefaultBitmap.CreatePtrBitmap(AWidth, AHeight: integer;
     5858  AData: PBGRAPixel): TBGRAPtrBitmap;
     5859begin
     5860  result := TBGRAPtrBitmap.Create(AWidth,AHeight,AData);
    55415861end;
    55425862
     
    55465866end;
    55475867
    5548 procedure TBGRADefaultBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer);
    5549 var
    5550   bmp: TBitmap;
    5551   subBmp: TBGRACustomBitmap;
    5552   subRect: TRect;
    5553   cw,ch: integer;
    5554 begin
    5555   DiscardBitmapChange;
    5556   cw := CanvasSource.Width;
    5557   ch := CanvasSource.Height;
    5558   if (x < 0) or (y < 0) or (x+Width > cw) or
    5559     (y+Height > ch) then
    5560   begin
    5561     FillTransparent;
    5562     if (x+Width <= 0) or (y+Height <= 0) or
    5563       (x >= cw) or (y >= ch) then
    5564       exit;
    5565 
    5566     if (x > 0) then subRect.Left := x else subRect.Left := 0;
    5567     if (y > 0) then subRect.Top := y else subRect.Top := 0;
    5568     if (x+Width > cw) then subRect.Right := cw else
    5569       subRect.Right := x+Width;
    5570     if (y+Height > ch) then subRect.Bottom := ch else
    5571       subRect.Bottom := y+Height;
    5572 
    5573     subBmp := NewBitmap(subRect.Right-subRect.Left,subRect.Bottom-subRect.Top);
    5574     subBmp.GetImageFromCanvas(CanvasSource,subRect.Left,subRect.Top);
    5575     PutImage(subRect.Left-x,subRect.Top-y,subBmp,dmSet);
    5576     subBmp.Free;
    5577     exit;
    5578   end;
    5579   bmp := TBitmap.Create;
    5580   bmp.PixelFormat := pf24bit;
    5581   bmp.Width := Width;
    5582   bmp.Height := Height;
    5583   bmp.Canvas.CopyRect(Classes.rect(0, 0, Width, Height), CanvasSource,
    5584     Classes.rect(x, y, x + Width, y + Height));
    5585   LoadFromRawImage(bmp.RawImage, 255, True);
    5586   bmp.Free;
    5587   InvalidateBitmap;
    5588 end;
    5589 
    55905868function TBGRADefaultBitmap.GetNbPixels: integer;
    55915869begin
     
    56135891end;
    56145892
     5893procedure TBGRADefaultBitmap.SetLineOrder(AValue: TRawImageLineOrder);
     5894begin
     5895  FLineOrder := AValue;
     5896end;
     5897
    56155898function TBGRADefaultBitmap.GetCanvasOpacity: byte;
    56165899begin
     
    56255908{ TBGRAPtrBitmap }
    56265909
     5910function TBGRAPtrBitmap.GetLineOrder: TRawImageLineOrder;
     5911begin
     5912  result := inherited GetLineOrder;
     5913end;
     5914
     5915procedure TBGRAPtrBitmap.SetLineOrder(AValue: TRawImageLineOrder);
     5916begin
     5917  inherited SetLineOrder(AValue);
     5918end;
     5919
    56275920procedure TBGRAPtrBitmap.ReallocData;
    56285921begin
     
    56335926begin
    56345927  FData := nil;
     5928end;
     5929
     5930procedure TBGRAPtrBitmap.CannotResize;
     5931begin
     5932  raise exception.Create('A pointer bitmap cannot be resized');
     5933end;
     5934
     5935procedure TBGRAPtrBitmap.NotImplemented;
     5936begin
     5937  raise exception.Create('Not implemented');
     5938end;
     5939
     5940procedure TBGRAPtrBitmap.RebuildBitmap;
     5941begin
     5942  NotImplemented;
     5943end;
     5944
     5945function TBGRAPtrBitmap.CreateDefaultFontRenderer: TBGRACustomFontRenderer;
     5946begin
     5947  result := nil;
     5948  NotImplemented;
     5949end;
     5950
     5951function TBGRAPtrBitmap.LoadFromRawImage(ARawImage: TRawImage;
     5952  DefaultOpacity: byte; AlwaysReplaceAlpha: boolean;
     5953  RaiseErrorOnInvalidPixelFormat: boolean): boolean;
     5954begin
     5955  result := false;
     5956  NotImplemented;
    56355957end;
    56365958
     
    56505972begin
    56515973  FData := AData;
     5974end;
     5975
     5976procedure TBGRAPtrBitmap.DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
     5977  AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
     5978begin
     5979  NotImplemented;
     5980end;
     5981
     5982procedure TBGRAPtrBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect;
     5983  AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
     5984begin
     5985  NotImplemented;
     5986end;
     5987
     5988procedure TBGRAPtrBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer
     5989  );
     5990begin
     5991  NotImplemented;
     5992end;
     5993
     5994procedure TBGRAPtrBitmap.Assign(Source: TPersistent);
     5995begin
     5996  CannotResize;
     5997end;
     5998
     5999procedure TBGRAPtrBitmap.TakeScreenshot(ARect: TRect);
     6000begin
     6001  CannotResize;
     6002end;
     6003
     6004procedure TBGRAPtrBitmap.TakeScreenshotOfPrimaryMonitor;
     6005begin
     6006  CannotResize;
     6007end;
     6008
     6009procedure TBGRAPtrBitmap.LoadFromDevice(DC: System.THandle);
     6010begin
     6011  CannotResize;
     6012end;
     6013
     6014procedure TBGRAPtrBitmap.LoadFromDevice(DC: System.THandle; ARect: TRect);
     6015begin
     6016  CannotResize;
    56526017end;
    56536018
     
    56556020  c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
    56566021  gammaColorCorrection: boolean = True; Sinus: Boolean=False);
    5657 var
    5658   gradScan : TBGRAGradientScanner;
    5659 begin
    5660   //handles transparency
    5661   if (c1.alpha = 0) and (c2.alpha = 0) then
    5662   begin
    5663     bmp.FillRect(x, y, x2, y2, BGRAPixelTransparent, mode);
    5664     exit;
    5665   end;
    5666 
    5667   gradScan := TBGRAGradientScanner.Create(c1,c2,gtype,o1,o2,gammaColorCorrection,Sinus);
    5668   bmp.FillRect(x,y,x2,y2,gradScan,mode);
    5669   gradScan.Free;
     6022begin
     6023  bmp.GradientFill(x,y,x2,y2,c1,c2,gtype,o1,o2,mode,gammaColorCorrection,sinus);
    56706024end;
    56716025
     
    56836037  end;
    56846038
    5685   ImageHandlers.RegisterImageWriter ('Personal Computer eXchange', 'pcx', TFPWriterPcx);
    5686   ImageHandlers.RegisterImageReader ('Personal Computer eXchange', 'pcx', TFPReaderPcx);
    5687 
    5688   ImageHandlers.RegisterImageWriter ('X Pixmap', 'xpm', TFPWriterXPM);
    5689   ImageHandlers.RegisterImageReader ('X Pixmap', 'xpm', TFPReaderXPM);
    5690 
    56916039end.
    56926040
  • GraphicTest/Packages/bgrabitmap/bgradithering.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, BGRAFilters, BGRAPalette, BGRABitmapTypes;
     8  Classes, SysUtils, BGRAFilterType, BGRAPalette, BGRABitmapTypes;
    99
    1010type
     11  TOutputPixelProc = procedure(X,Y: NativeInt; AColorIndex: NativeInt; AColor: TBGRAPixel) of object;
    1112
    1213  { TDitheringTask }
     
    1718    FIgnoreAlpha: boolean;
    1819    FPalette: TBGRACustomApproxPalette;
     20    FCurrentOutputScanline: PBGRAPixel;
     21    FCurrentOutputY: NativeInt;
     22    FOutputPixel : TOutputPixelProc;
     23    FDrawMode: TDrawMode;
     24    procedure OutputPixel(X,Y: NativeInt; {%H-}AColorIndex: NativeInt; AColor: TBGRAPixel); virtual;
     25    procedure ApproximateColor(const AColor: TBGRAPixel; out AApproxColor: TBGRAPixel; out AIndex: integer);
    1926  public
     27    constructor Create(ASource: IBGRAScanner; APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap; AIgnoreAlpha: boolean; ABounds: TRect); overload;
    2028    constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean; ABounds: TRect); overload;
    2129    constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean); overload;
     30    property OnOutputPixel: TOutputPixelProc read FOutputPixel write FOutputPixel;
     31    property DrawMode: TDrawMode read FDrawMode write FDrawMode;
    2232  end;
    2333
     
    3444  protected
    3545    procedure DoExecute; override;
     46  end;
     47
     48  { TDitheringToIndexedImage }
     49
     50  TDitheringToIndexedImage = class
     51  protected
     52    FBitOrder: TRawImageBitOrder;
     53    FByteOrder: TRawImageByteOrder;
     54    FBitsPerPixel: integer;
     55    FLineOrder: TRawImageLineOrder;
     56    FPalette: TBGRACustomApproxPalette;
     57    FIgnoreAlpha: boolean;
     58    FTransparentColorIndex: NativeInt;
     59
     60    //following variables are used during dithering
     61    FCurrentScanlineSize: PtrInt;
     62    FCurrentData: PByte;
     63    FCurrentOutputY: NativeInt;
     64    FCurrentOutputScanline: PByte;
     65    FCurrentBitOrderMask: NativeInt;
     66    FCurrentMaxY: NativeInt;
     67
     68    procedure SetPalette(AValue: TBGRACustomApproxPalette);
     69    procedure SetIgnoreAlpha(AValue: boolean);
     70    procedure SetLineOrder(AValue: TRawImageLineOrder);
     71    procedure SetBitOrder(AValue: TRawImageBitOrder); virtual;
     72    procedure SetBitsPerPixel(AValue: integer); virtual;
     73    procedure SetByteOrder(AValue: TRawImageByteOrder); virtual;
     74    procedure OutputPixelSubByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual;
     75    procedure OutputPixelFullByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual;
     76    function GetScanline(Y: NativeInt): Pointer; virtual;
     77    function GetTransparentColorIndex: integer;
     78    procedure SetTransparentColorIndex(AValue: integer);
     79  public
     80    constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer); //use platform byte order
     81    constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer; AByteOrder: TRawImageByteOrder); //maybe necessary if larger than 8 bits per pixel
     82
     83    function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap): Pointer; overload; //use minimum scanline size
     84    function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer; overload;
     85    procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer); overload; //use minimum scanline size
     86    procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt); overload;
     87    function ComputeMinimumScanlineSize(AWidthInPixels: integer): PtrInt;
     88    function AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): pointer;
     89
     90    //optional customization of format
     91    property BitsPerPixel: integer read FBitsPerPixel write SetBitsPerPixel;
     92    property BitOrder: TRawImageBitOrder read FBitOrder write SetBitOrder;
     93    property ByteOrder: TRawImageByteOrder read FByteOrder write SetByteOrder;
     94    property LineOrder: TRawImageLineOrder read FLineOrder write SetLineOrder;
     95
     96    property Palette: TBGRACustomApproxPalette read FPalette write SetPalette;
     97    property IgnoreAlpha: boolean read FIgnoreAlpha write SetIgnoreAlpha;
     98
     99    //when there is no transparent color in the palette, or that IgnoreAlpha is set to True,
     100    //this allows to define the index for the fully transparent color
     101    property DefaultTransparentColorIndex: integer read GetTransparentColorIndex write SetTransparentColorIndex;
    36102  end;
    37103
     
    40106function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette;
    41107  AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload;
     108function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect): TDitheringTask; overload;
     109function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette;
     110    AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload;
     111
     112function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
    42113
    43114implementation
     115
     116uses BGRABlend;
    44117
    45118function AbsRGBADiff(const c1, c2: TExpandedPixel): NativeInt;
     
    68141end;
    69142
     143function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm;
     144  ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect
     145  ): TDitheringTask;
     146begin
     147  result := CreateDitheringTask(AAlgorithm, ASource, ADestination, nil, true, ABounds);
     148end;
     149
     150function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm;
     151  ASource: IBGRAScanner; ADestination: TBGRACustomBitmap;
     152  APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABounds: TRect
     153  ): TDitheringTask;
     154begin
     155  result := nil;
     156  case AAlgorithm of
     157    daNearestNeighbor: result := TNearestColorTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds);
     158    daFloydSteinberg: result := TFloydSteinbergDitheringTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds);
     159    else raise exception.Create('Unknown algorithm');
     160  end;
     161end;
     162
     163function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm;
     164  ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
     165var
     166  palette16bit: TBGRA16BitPalette;
     167  dither: TDitheringTask;
     168begin
     169  palette16bit := TBGRA16BitPalette.Create;
     170  dither := CreateDitheringTask(AAlgorithm, ABitmap, palette16bit, false);
     171  result := dither.Execute;
     172  dither.Free;
     173  palette16bit.Free;
     174end;
     175
     176{ TDitheringToIndexedImage }
     177
     178procedure TDitheringToIndexedImage.SetBitsPerPixel(AValue: integer);
     179begin
     180  if not (AValue in [1,2,4,8,16,32]) then
     181    raise exception.Create('Invalid value for bits per pixel. Allowed values: 1,2,4,8,16,32.');
     182  if FBitsPerPixel=AValue then Exit;
     183  FBitsPerPixel:=AValue;
     184end;
     185
     186procedure TDitheringToIndexedImage.SetByteOrder(AValue: TRawImageByteOrder);
     187begin
     188  if FByteOrder=AValue then Exit;
     189  FByteOrder:=AValue;
     190end;
     191
     192procedure TDitheringToIndexedImage.OutputPixelSubByte(X, Y: NativeInt;
     193  AColorIndex: NativeInt; AColor: TBGRAPixel);
     194var p: PByte;
     195begin
     196  if y <> FCurrentOutputY then
     197  begin
     198    FCurrentOutputY := y;
     199    FCurrentOutputScanline := GetScanline(Y);
     200  end;
     201  if AColorIndex = -1 then AColorIndex := FTransparentColorIndex;
     202  case FBitsPerPixel of
     203    1: begin
     204         p := FCurrentOutputScanline+(x shr 3);
     205         p^ := p^ or ((AColorIndex and 1) shl ((x xor FCurrentBitOrderMask) and 7));
     206       end;
     207    2: begin
     208         p := FCurrentOutputScanline+(x shr 2);
     209         p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 3) shl 1));
     210       end;
     211    4: begin
     212         p := FCurrentOutputScanline+(x shr 1);
     213         p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 1) shl 2));
     214       end;
     215  end;
     216end;
     217
     218procedure TDitheringToIndexedImage.OutputPixelFullByte(X, Y: NativeInt;
     219  AColorIndex: NativeInt; AColor: TBGRAPixel);
     220begin
     221  if y <> FCurrentOutputY then
     222  begin
     223    FCurrentOutputY := y;
     224    FCurrentOutputScanline := GetScanline(Y);
     225  end;
     226  if AColorIndex = -1 then AColorIndex := FTransparentColorIndex;
     227  case FBitsPerPixel of
     228    8: (FCurrentOutputScanline+x)^ := AColorIndex;
     229    16: (PWord(FCurrentOutputScanline)+x)^ := AColorIndex;
     230    32: (PDWord(FCurrentOutputScanline)+x)^ := AColorIndex;
     231  end;
     232end;
     233
     234function TDitheringToIndexedImage.GetScanline(Y: NativeInt): Pointer;
     235begin
     236  if FLineOrder = riloTopToBottom then
     237    result := FCurrentData + Y*FCurrentScanlineSize
     238  else
     239    result := FCurrentData + (FCurrentMaxY-Y)*FCurrentScanlineSize
     240end;
     241
     242procedure TDitheringToIndexedImage.SetIgnoreAlpha(AValue: boolean);
     243begin
     244  if FIgnoreAlpha=AValue then Exit;
     245  FIgnoreAlpha:=AValue;
     246end;
     247
     248procedure TDitheringToIndexedImage.SetTransparentColorIndex(AValue: integer);
     249begin
     250  if FTransparentColorIndex=AValue then Exit;
     251  FTransparentColorIndex:=AValue;
     252end;
     253
     254function TDitheringToIndexedImage.GetTransparentColorIndex: integer;
     255begin
     256  result := FTransparentColorIndex;
     257end;
     258
     259procedure TDitheringToIndexedImage.SetPalette(AValue: TBGRACustomApproxPalette);
     260begin
     261  if FPalette=AValue then Exit;
     262  FPalette:=AValue;
     263end;
     264
     265procedure TDitheringToIndexedImage.SetLineOrder(AValue: TRawImageLineOrder);
     266begin
     267  if FLineOrder=AValue then Exit;
     268  FLineOrder:=AValue;
     269end;
     270
     271procedure TDitheringToIndexedImage.SetBitOrder(AValue: TRawImageBitOrder);
     272begin
     273  if FBitOrder=AValue then Exit;
     274  FBitOrder:=AValue;
     275end;
     276
     277constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer);
     278begin
     279  BitsPerPixel:= ABitsPerPixelForIndices;
     280  BitOrder := riboReversedBits; //convention in BMP format
     281  {$IFDEF ENDIAN_LITTLE}
     282  ByteOrder:= riboLSBFirst;
     283  {$ELSE}
     284  ByteOrder:= riboMSBFirst;
     285  {$ENDIF}
     286  Palette := APalette;
     287  IgnoreAlpha:= AIgnoreAlpha;
     288end;
     289
     290constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer;
     291  AByteOrder: TRawImageByteOrder);
     292begin
     293  BitsPerPixel:= ABitsPerPixelForIndices;
     294  BitOrder := riboReversedBits; //convention in BMP format
     295  ByteOrder:= AByteOrder;
     296  Palette := APalette;
     297  IgnoreAlpha:= AIgnoreAlpha;
     298end;
     299
     300function TDitheringToIndexedImage.ComputeMinimumScanlineSize(
     301  AWidthInPixels: integer): PtrInt;
     302begin
     303  result := (AWidthInPixels*FBitsPerPixel+7) shr 3;
     304end;
     305
     306function TDitheringToIndexedImage.AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap;
     307  AScanlineSize: PtrInt): pointer;
     308var size: integer;
     309begin
     310  size := AScanlineSize * AImage.Height;
     311  GetMem(result, size);
     312  Fillchar(result^, size, 0);
     313end;
     314
     315function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm;
     316  AImage: TBGRACustomBitmap): Pointer;
     317begin
     318  result := DitherImage(AAlgorithm, AImage, ComputeMinimumScanlineSize(AImage.Width));
     319end;
     320
     321procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm;
     322  AImage: TBGRACustomBitmap; AData: Pointer);
     323begin
     324  DitherImageTo(AAlgorithm, AImage, AData, ComputeMinimumScanlineSize(AImage.Width));
     325end;
     326
     327function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm;
     328  AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer;
     329begin
     330  result := AllocateSpaceForIndexedData(AImage, AScanlineSize);
     331  DitherImageTo(AAlgorithm, AImage, result, AScanlineSize);
     332end;
     333
     334procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm;
     335  AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt);
     336var ditherTask: TDitheringTask;
     337begin
     338  FCurrentOutputY := -1;
     339  FCurrentOutputScanline := nil;
     340  FCurrentData := AData;
     341  FCurrentMaxY:= AImage.Height-1;
     342  FCurrentScanlineSize:= AScanlineSize;
     343
     344  ditherTask := CreateDitheringTask(AAlgorithm, AImage, FPalette, FIgnoreAlpha);
     345  try
     346    ditherTask.Inplace := True; //do not allocate destination
     347    if BitsPerPixel >= 8 then
     348      ditherTask.OnOutputPixel := @OutputPixelFullByte
     349    else
     350    begin
     351      ditherTask.OnOutputPixel:= @OutputPixelSubByte;
     352      if BitOrder = riboBitsInOrder then
     353        FCurrentBitOrderMask := 0
     354      else
     355        FCurrentBitOrderMask := $ff;
     356    end;
     357    ditherTask.Execute;
     358  finally
     359    ditherTask.Free;
     360  end;
     361end;
     362
    70363{ TDitheringTask }
     364
     365procedure TDitheringTask.OutputPixel(X, Y: NativeInt; AColorIndex: NativeInt;
     366  AColor: TBGRAPixel);
     367begin
     368  if Y <> FCurrentOutputY then
     369  begin
     370    FCurrentOutputY := Y;
     371    FCurrentOutputScanline := Destination.ScanLine[y];
     372  end;
     373  PutPixels(FCurrentOutputScanline+x, @AColor, 1, FDrawMode, 255);
     374end;
     375
     376procedure TDitheringTask.ApproximateColor(const AColor: TBGRAPixel;
     377  out AApproxColor: TBGRAPixel; out AIndex: integer);
     378begin
     379  if FPalette <> nil then
     380  begin
     381    AIndex := FPalette.FindNearestColorIndex(AColor, FIgnoreAlpha);
     382    if AIndex = -1 then
     383      AApproxColor := BGRAPixelTransparent
     384    else
     385      AApproxColor := FPalette.Color[AIndex];
     386  end else
     387  begin
     388    if AColor.alpha = 0 then
     389    begin
     390      AApproxColor := BGRAPixelTransparent;
     391      AIndex := -1;
     392    end else
     393    begin
     394      AApproxColor := AColor;
     395      AIndex := 0;
     396    end;
     397  end;
     398end;
     399
     400constructor TDitheringTask.Create(ASource: IBGRAScanner;
     401  APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap;
     402  AIgnoreAlpha: boolean; ABounds: TRect);
     403begin
     404  FPalette := APalette;
     405  SetSource(ASource);
     406  FBounds := ABounds;
     407  FIgnoreAlpha:= AIgnoreAlpha;
     408  FCurrentOutputY := -1;
     409  FCurrentOutputScanline:= nil;
     410  OnOutputPixel:= @OutputPixel;
     411  Destination := ADestination;
     412  FDrawMode:= dmSet;
     413end;
    71414
    72415constructor TDitheringTask.Create(bmp: TBGRACustomBitmap;
     
    75418begin
    76419  FPalette := APalette;
    77   FSource := bmp;
     420  SetSource(bmp);
    78421  FBounds := ABounds;
    79422  FIgnoreAlpha:= AIgnoreAlpha;
    80   if AInPlace then Destination := FSource;
     423  FCurrentOutputY := -1;
     424  FCurrentOutputScanline:= nil;
     425  OnOutputPixel:= @OutputPixel;
     426  InPlace := AInPlace;
     427  FDrawMode:= dmSet;
    81428end;
    82429
     
    85432begin
    86433  FPalette := APalette;
    87   FSource := bmp;
     434  SetSource(bmp);
    88435  FBounds := rect(0,0,bmp.Width,bmp.Height);
    89436  FIgnoreAlpha:= AIgnoreAlpha;
    90   if AInPlace then Destination := FSource;
     437  FCurrentOutputY := -1;
     438  FCurrentOutputScanline:= nil;
     439  OnOutputPixel:= @OutputPixel;
     440  InPlace := AInPlace;
     441  FDrawMode:= dmSet;
    91442end;
    92443
     
    125476
    126477var
    127   p,pNext,pDest: PBGRAPixel;
     478  p,pNext: PExpandedPixel;
     479  destX,destY: NativeInt;
    128480  orig,cur,approxExp: TExpandedPixel;
    129481  approx: TBGRAPixel;
     482  approxIndex: integer;
    130483  curPix,diff: TAccPixel;
    131484  i: NativeInt;
    132485  yWrite: NativeInt;
    133486  tempLine, currentLine, nextLine: TLine;
     487
     488  nextScan,curScan: PExpandedPixel;
    134489
    135490  function ClampWordDiv(AValue: NativeInt): Word; inline;
     
    158513  setlength(currentLine,w);
    159514  setlength(nextLine,w);
     515  curScan := nil;
     516  nextScan := RequestSourceExpandedScanLine(FBounds.Left, FBounds.Top, FBounds.Right-FBounds.Left);
    160517  for yWrite := 0 to h-1 do
    161518  begin
    162519    if GetShouldStop(yWrite) then break;
    163     p := FSource.ScanLine[yWrite+FBounds.Top]+FBounds.Left;
    164     pDest := FDestination.ScanLine[yWrite+FBounds.Top]+FBounds.Left;
     520    ReleaseSourceExpandedScanLine(curScan);
     521    curScan := nextScan;
     522    nextScan := nil;
     523    p := curScan;
     524    destX := FBounds.Left;
     525    destY := yWrite+FBounds.Top;
    165526    if yWrite < h-1 then
    166       pNext := FSource.ScanLine[yWrite+FBounds.Top+1]+FBounds.Left
    167     else
    168       pNext := nil;
     527      nextScan := RequestSourceExpandedScanLine(FBounds.Left,yWrite+FBounds.Top+1, FBounds.Right-FBounds.Left);
     528    pNext := nextScan;
    169529    if odd(yWrite) then
    170530    begin
    171531      inc(p, w);
    172       inc(pDest, w);
     532      inc(destX, w);
    173533      if pNext<>nil then inc(pNext, w);
    174534      for i := w-1 downto 0 do
    175535      begin
    176536        dec(p);
    177         dec(pDest);
     537        dec(destX);
    178538        if pNext<>nil then dec(pNext);
    179539        if p^.alpha <> 0 then
    180540        begin
    181           orig := GammaExpansion(p^);
     541          orig := p^;
    182542          with currentLine[i] do
    183543          begin
     
    191551            cur.blue := ClampWordDiv(curPix.blue);
    192552          end;
    193           approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);
     553          ApproximateColor(GammaCompression(cur), approx, approxIndex);
    194554          approxExp := GammaExpansion(approx);
    195555          diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift));
     
    207567          if i > 0 then
    208568          begin
    209             if AbsRGBADiff(GammaExpansion((p-1)^),orig) < MaxColorDiffForDiffusion then
     569            if AbsRGBADiff((p-1)^,orig) < MaxColorDiffForDiffusion then
    210570              AddError(currentLine[i-1], diff, 7);
    211571          end;
     
    214574            if i > 0 then
    215575            begin
    216               if AbsRGBADiff(GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then
     576              if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then
    217577                AddError(nextLine[i-1], diff, 1);
    218578            end;
    219             if AbsRGBADiff(GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then
     579            if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then
    220580              AddError(nextLine[i], diff, 5);
    221581            if i < w-1 then
    222582            begin
    223               if AbsRGBADiff(GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then
     583              if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then
    224584                AddError(nextLine[i+1], diff, 3);
    225585            end;
    226586          end;
    227           pDest^ := approx;
     587          OnOutputPixel(destX,destY,approxIndex,approx);
    228588        end;
    229589      end
     
    234594      if p^.alpha <> 0 then
    235595      begin
    236         orig := GammaExpansion(p^);
     596        orig := p^;
    237597        with currentLine[i] do
    238598        begin
     
    246606          cur.blue := ClampWordDiv(curPix.blue);
    247607        end;
    248         approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);
     608        ApproximateColor(GammaCompression(cur), approx, approxIndex);
    249609        approxExp := GammaExpansion(approx);
    250610        diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift));
     
    262622        if i < w-1 then
    263623        begin
    264           if AbsRGBADiff(GammaExpansion((p+1)^),orig) < MaxColorDiffForDiffusion then
     624          if AbsRGBADiff((p+1)^,orig) < MaxColorDiffForDiffusion then
    265625            AddError(currentLine[i+1], diff, 7);
    266626        end;
    267         if nextLine <> nil then
     627        if pNext <> nil then
    268628        begin
    269629          if i > 0 then
    270630          begin
    271             if AbsRGBADiff(GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then
     631            if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then
    272632              AddError(nextLine[i-1], diff, 3);
    273633          end;
    274           if AbsRGBADiff(GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then
     634          if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then
    275635            AddError(nextLine[i], diff, 5);
    276636          if i < w-1 then
    277637          begin
    278             if AbsRGBADiff(GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then
     638            if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then
    279639              AddError(nextLine[i+1], diff, 1);
    280640          end;
    281641        end;
    282         pDest^ := approx;
     642        OnOutputPixel(destX,destY,approxIndex,approx);
    283643      end;
    284644      inc(p);
    285       inc(pDest);
     645      inc(destX);
    286646      if pNext<>nil then inc(pNext);
    287647    end;
     
    300660      end;
    301661  end;
    302   FDestination.InvalidateBitmap;
     662  ReleaseSourceExpandedScanLine(curScan);
     663  ReleaseSourceExpandedScanLine(nextScan);
     664  Destination.InvalidateBitmap;
    303665end;
    304666
     
    306668
    307669procedure TNearestColorTask.DoExecute;
    308 var yb,xb: integer;
    309   psrc,pdest: PBGRAPixel;
     670var yb,xb: NativeInt;
     671  curScan,psrc: PBGRAPixel;
     672  colorIndex: LongInt;
     673  colorValue: TBGRAPixel;
    310674begin
    311675  for yb := FBounds.Top to FBounds.Bottom - 1 do
    312676  begin
    313677    if GetShouldStop(yb) then break;
    314     psrc := FSource.ScanLine[yb] + FBounds.Left;
    315     pdest := FDestination.ScanLine[yb] + FBounds.Left;
    316     for xb := FBounds.Right - FBounds.Left -1 downto 0 do
     678    curScan := RequestSourceScanLine(FBounds.Left,yb,FBounds.Right-FBounds.Left);
     679    psrc := curScan;
     680    for xb := FBounds.Left to FBounds.Right-1 do
    317681    begin
    318       pdest^ := FPalette.FindNearestColor(psrc^, FIgnoreAlpha);
    319       inc(pdest);
     682      ApproximateColor(psrc^, colorValue, colorIndex);
     683      OnOutputPixel(xb,yb,colorIndex,colorValue);
    320684      inc(psrc);
    321685    end;
    322   end;
    323   FDestination.InvalidateBitmap;
     686    ReleaseSourceScanLine(curScan);
     687  end;
     688  Destination.InvalidateBitmap;
    324689end;
    325690
  • GraphicTest/Packages/bgrabitmap/bgradnetdeserial.pas

    r472 r494  
    184184implementation
    185185
    186 uses lazutf8classes;
     186uses BGRAUTF8;
    187187
    188188const
  • GraphicTest/Packages/bgrabitmap/bgrafillinfo.pas

    r472 r494  
    3737      function GetBounds: TRect; override;
    3838
    39       //compute min-max to be drawn on destination bitmap according to cliprect. Returns false if
    40       //there is nothing to draw
    41       function ComputeMinMax(out minx,miny,maxx,maxy: integer; bmpDest: TBGRACustomBitmap): boolean; override;
    42 
    4339      //check if the point is inside the filling zone
    4440      function IsPointInside(x,y: single; windingMode: boolean): boolean; override;
     
    5450      procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); override;
    5551
     52      //can be called after ComputeAndSort or ComputeIntersection to determine the current horizontal slice
     53      //so that it can be checked if the intermediates scanlines can be skipped
     54      function GetSliceIndex: integer; override;
     55
    5656  end;
    5757
     
    6161  private
    6262    FX, FY, FRX, FRY: single;
     63    FSliceIndex: integer;
    6364    function GetCenter: TPointF;
    6465  protected
     
    7172    function GetBounds: TRect; override;
    7273    function SegmentsCurved: boolean; override;
     74    function GetSliceIndex: integer; override;
    7375    property Center: TPointF read GetCenter;
    7476    property RadiusX: single read FRX;
     
    9092    function SegmentsCurved: boolean; override;
    9193    destructor Destroy; override;
     94    function GetSliceIndex: integer; override;
    9295    property InnerBorder: TFillEllipseInfo read FInnerBorder;
    9396    property OuterBorder: TFillEllipseInfo read FOuterBorder;
     
    179182    constructor Create(const points: array of TPointF);
    180183    destructor Destroy; override;
     184    function GetSliceIndex: integer; override;
    181185  end;
    182186
     
    208212    FFirstWaiting, FFirstDrawing: POnePassRecord;
    209213    FShouldInitializeDrawing: boolean;
     214    FSliceIndex: integer;
    210215    procedure ComputeIntersection(cury: single;
    211216      var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
     
    213218    constructor Create(const points: array of TPointF);
    214219    function CreateIntersectionArray: ArrayOfTIntersectionInfo; override;
     220    function GetSliceIndex: integer; override;
    215221    destructor Destroy; override;
    216222  end;
     
    243249function IsPointInRectangle(x1, y1, x2, y2: single; point: TPointF): boolean;
    244250
     251function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer;
     252  bmpDest: TBGRACustomBitmap): boolean;
     253
    245254implementation
    246255
    247256uses Math;
     257
     258function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer;
     259  bmpDest: TBGRACustomBitmap): boolean;
     260var clip,bounds: TRect;
     261begin
     262  result := true;
     263  bounds := AShape.GetBounds;
     264
     265  if (bounds.Right <= bounds.left) or (bounds.bottom <= bounds.top) then
     266  begin
     267    result := false;
     268    exit;
     269  end;
     270
     271  miny := bounds.top;
     272  maxy := bounds.bottom - 1;
     273  minx := bounds.left;
     274  maxx := bounds.right - 1;
     275
     276  clip := bmpDest.ClipRect;
     277
     278  if minx < clip.Left then
     279    minx := clip.Left;
     280  if maxx < clip.Left then
     281    result := false;
     282
     283  if maxx > clip.Right - 1 then
     284    maxx := clip.Right- 1;
     285  if minx > clip.Right - 1 then
     286    result := false;
     287
     288  if miny < clip.Top then
     289    miny := clip.Top;
     290  if maxy < clip.Top then
     291    result := false;
     292
     293  if maxy > clip.Bottom - 1 then
     294    maxy := clip.Bottom - 1;
     295  if miny > clip.Bottom - 1 then
     296    result := false;
     297end;
    248298
    249299procedure ComputeAliasedRowBounds(x1,x2: single; minx,maxx: integer; out ix1,ix2: integer);
     
    345395end;
    346396
    347 function TFillShapeInfo.ComputeMinMax(out minx, miny, maxx, maxy: integer;
    348   bmpDest: TBGRACustomBitmap): boolean;
    349 var clip,bounds: TRect;
    350 begin
    351   result := true;
    352   bounds := GetBounds;
    353 
    354   if (bounds.Right <= bounds.left) or (bounds.bottom <= bounds.top) then
    355   begin
    356     result := false;
    357     exit;
    358   end;
    359 
    360   miny := bounds.top;
    361   maxy := bounds.bottom - 1;
    362   minx := bounds.left;
    363   maxx := bounds.right - 1;
    364 
    365   clip := bmpDest.ClipRect;
    366 
    367   if minx < clip.Left then
    368     minx := clip.Left;
    369   if maxx < clip.Left then
    370     result := false;
    371 
    372   if maxx > clip.Right - 1 then
    373     maxx := clip.Right- 1;
    374   if minx > clip.Right - 1 then
    375     result := false;
    376 
    377   if miny < clip.Top then
    378     miny := clip.Top;
    379   if maxy < clip.Top then
    380     result := false;
    381 
    382   if maxy > clip.Bottom - 1 then
    383     maxy := clip.Bottom - 1;
    384   if miny > clip.Bottom - 1 then
    385     result := false;
    386 end;
    387397
    388398function TFillShapeInfo.IsPointInside(x, y: single; windingMode: boolean
     
    489499  SortIntersection(inter,nbInter);
    490500  if windingMode then ConvertFromNonZeroWinding(inter,nbInter);
     501end;
     502
     503function TFillShapeInfo.GetSliceIndex: integer;
     504begin
     505  result := 0;
    491506end;
    492507
     
    886901end;
    887902
     903function TFillPolyInfo.GetSliceIndex: integer;
     904begin
     905  Result:= FCurSlice;
     906end;
     907
    888908{ TOnePassFillPolyInfo }
    889909
     
    9831003        p^.nextDrawing := FFirstDrawing;
    9841004        FFirstDrawing := p;
     1005        inc(FSliceIndex);
    9851006      end;
    9861007    end
     
    10131034        FFirstDrawing:= pnext;
    10141035      p := pnext;
     1036      Inc(FSliceIndex);
    10151037      continue;
    10161038    end;
     
    10561078
    10571079  SortByY;
     1080  FSliceIndex := 0;
    10581081end;
    10591082
     
    10861109
    10871110  setlength(result, NbMaxIntersection);
     1111  for i := 0 to high(result) do
     1112    result[i] := nil;
     1113end;
     1114
     1115function TOnePassFillPolyInfo.GetSliceIndex: integer;
     1116begin
     1117  Result:= FSliceIndex;
    10881118end;
    10891119
     
    11541184  FRY := abs(ry);
    11551185  WindingFactor := 1;
     1186  FSliceIndex:= -1;
    11561187end;
    11571188
     
    11641195begin
    11651196  Result:= true;
     1197end;
     1198
     1199function TFillEllipseInfo.GetSliceIndex: integer;
     1200begin
     1201  Result:= FSliceIndex;
    11661202end;
    11671203
     
    11901226    inter[nbinter].SetValues( FX + d, windingFactor, 1);
    11911227    Inc(nbinter);
     1228    FSliceIndex := 0;
     1229  end else
     1230  begin
     1231    if cury < FY then
     1232      FSliceIndex:= -1
     1233    else
     1234      FSliceIndex:= 1;
    11921235  end;
    11931236end;
     
    12411284    FInnerBorder.Free;
    12421285  inherited Destroy;
     1286end;
     1287
     1288function TFillBorderEllipseInfo.GetSliceIndex: integer;
     1289begin
     1290  Result:= FOuterBorder.GetSliceIndex;
    12431291end;
    12441292
  • GraphicTest/Packages/bgrabitmap/bgrafilters.pas

    r472 r494  
    1010
    1111uses
    12   Classes, BGRABitmapTypes;
     12  Classes, BGRABitmapTypes, BGRAFilterType, BGRAFilterBlur;
    1313
    1414type
    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;
    35 
    36 { The median filter consist in calculating the median value of pixels. Here
    37   a square of 9x9 pixel is considered. The median allow to select the most
    38   representative colors. The option parameter allow to choose to smooth the
    39   result or not. }
    40 function FilterMedian(bmp: TBGRACustomBitmap;
    41   Option: TMedianOption): TBGRACustomBitmap;
    42 
    43 { SmartZoom x3 is a filter that upsizes 3 times the picture and add
    44   pixels that could be logically expected (horizontal, vertical, diagonal lines) }
    45 function FilterSmartZoom3(bmp: TBGRACustomBitmap;
    46   Option: TMedianOption): TBGRACustomBitmap;
    47 
    48 { Sharpen filter add more contrast between pixels }
    49 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap;
    50 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    51 
    52 { A radial blur applies a blur with a circular influence, i.e, each pixel
    53   is merged with pixels within the specified radius. There is an exception
    54   with rbFast blur, the optimization entails an hyperbolic shape. }
    55 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
    56   blurType: TRadialBlurType): TBGRACustomBitmap;
    57 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
    58   ABlurType: TRadialBlurType): TFilterTask;
    59 
    60 { The precise blur allow to specify the blur radius with subpixel accuracy }
    61 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap;
    62 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TFilterTask;
    63 
    64 { Motion blur merge pixels in a direction. The oriented parameter specifies
    65   if the weights of the pixels are the same along the line or not. }
    66 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    67   angle: single; oriented: boolean): TBGRACustomBitmap;
    68 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TFilterTask;
    69 
    70 { General purpose blur filter, with a blur mask as parameter to describe
    71   how pixels influence each other }
    72 function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
    73 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask;
    74 
    75 function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
    76 
    77 { Emboss filter compute a color difference in the angle direction }
    78 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
    79 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    80 
    81 { Emboss highlight computes a sort of emboss with 45 degrees angle and
    82   with standard selection color (white/black and filled with blue) }
    83 function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
    84   FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
    85 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap;
    86   FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
    87 
    88 { Normalize use the whole available range of values, making dark colors darkest possible
    89   and light colors lightest possible }
    90 function FilterNormalize(bmp: TBGRACustomBitmap;
    91   eachChannel: boolean = True): TBGRACustomBitmap;
    92 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
    93   eachChannel: boolean = True): TBGRACustomBitmap;
    94 
    95 { Rotate filter rotate the image and clip it in the bounding rectangle }
    96 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    97   angle: single; correctBlur: boolean = false): TBGRACustomBitmap;
    98 
    99 { Grayscale converts colored pixel into grayscale with same luminosity }
    100 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    101 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
    102 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask;
    103 
    104 { Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil }
    105 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    106 
    107 { Distort the image as if it were on a sphere }
    108 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    109 
    110 { Twirl distortion, i.e. a progressive rotation }
    111 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    112 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    113 
    114 { Distort the image as if it were on a vertical cylinder }
    115 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    116 
    117 { Compute a plane projection towards infinity (SLOW) }
    118 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    119 
    120 implementation
    121 
    122 uses Math, GraphType, Dialogs, BGRATransform, Types, SysUtils;
    123 
     15  TFilterTask = BGRAFilterType.TFilterTask;
     16
     17/////////////////////// PIXELWISE FILTERS ////////////////////////////////
    12418type
    12519  { TGrayscaleTask }
    126 
     20  { Grayscale converts colored pixel into grayscale with same luminosity }
    12721  TGrayscaleTask = class(TFilterTask)
    12822  private
     
    13428  end;
    13529
    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 
    201 procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
    202   blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    203 procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; ABounds: TRect;
    204   radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    205 procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
    206   angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    207 procedure FilterBlur(bmp: TBGRACustomBitmap; ABounds: TRect;
    208    blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
     30{ Grayscale converts colored pixel into grayscale with same luminosity }
     31function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     32function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     33function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask;
     34
     35{ Normalize use the whole available range of values, making dark colors darkest possible
     36  and light colors lightest possible }
     37function FilterNormalize(bmp: TBGRACustomBitmap;
     38  eachChannel: boolean = True): TBGRACustomBitmap;
     39function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
     40  eachChannel: boolean = True): TBGRACustomBitmap;
     41
     42////////////////////// 3X3 FILTERS ////////////////////////////////////////////
     43
     44{ Sharpen filter add more contrast between pixels }
     45function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer = 256): TBGRACustomBitmap;
     46function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
     47
     48{ Compute a contour, as if the image was drawn with a 2 pixels-wide black pencil }
     49function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     50
     51{ Emboss filter compute a color difference in the angle direction }
     52function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap;
     53function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRACustomBitmap;
     54
     55{ Emboss highlight computes a sort of emboss with 45 degrees angle and
     56  with standard selection color (white/black and filled with blue) }
     57function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
     58  FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
     59function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap;
     60  FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
     61
     62{ The median filter consist in calculating the median value of pixels. Here
     63  a square of 9x9 pixel is considered. The median allow to select the most
     64  representative colors. The option parameter allow to choose to smooth the
     65  result or not. }
     66function FilterMedian(bmp: TBGRACustomBitmap; Option: TMedianOption): TBGRACustomBitmap;
     67
     68//////////////////////// DEFORMATION FILTERS /////////////////////////////////
     69
     70{ Distort the image as if it were on a sphere }
     71function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     72
     73{ Twirl distortion, i.e. a progressive rotation }
     74function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     75function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     76
     77{ Distort the image as if it were on a vertical cylinder }
     78function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     79
     80{ Compute a plane projection towards infinity (SLOW) }
     81function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     82
     83{ Rotate filter rotate the image and clip it in the bounding rectangle }
     84function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
     85  angle: single; correctBlur: boolean = false): TBGRACustomBitmap;
     86
     87///////////////////////// BLUR FILTERS //////////////////////////////////////
     88
     89{ A radial blur applies a blur with a circular influence, i.e, each pixel
     90  is merged with pixels within the specified radius. There is an exception
     91  with rbFast blur, the optimization entails an hyperbolic shape. }
     92type TRadialBlurTask = BGRAFilterBlur.TRadialBlurTask;
     93function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap;
     94function FilterBlurRadial(bmp: TBGRACustomBitmap; radiusX: single; radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap;
     95function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single; ABlurType: TRadialBlurType): TRadialBlurTask;
     96function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadiusX,ARadiusY: single; ABlurType: TRadialBlurType): TRadialBlurTask;
     97
     98{ The precise blur allow to specify the blur radius with subpixel accuracy }
     99function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap; deprecated 'Use FilterBlurRadial with blurType:=rbPrecise and radius multiplied by 10';
     100function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TRadialBlurTask; deprecated 'Use CreateRadialBlurTask with blurType:=rbPrecise and radius multiplied by 10';
     101
     102{ Motion blur merge pixels in a direction. The oriented parameter specifies
     103  if the weights of the pixels are the same along the line or not. }
     104type TMotionBlurTask = BGRAFilterBlur.TMotionBlurTask;
     105function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap;
     106function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ADistance,AAngle: single; AOriented: boolean): TMotionBlurTask;
     107
     108{ General purpose blur filter, with a blur mask as parameter to describe
     109  how pixels influence each other }
     110function FilterBlur(bmp: TBGRACustomBitmap; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TBGRACustomBitmap;
     111function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask;
     112
     113////////////////////////////// OTHER FILTERS /////////////////////////////////
     114
     115{ SmartZoom x3 is a filter that upsizes 3 times the picture and add
     116  pixels that could be logically expected (horizontal, vertical, diagonal lines) }
     117function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     118  Option: TMedianOption): TBGRACustomBitmap;
     119
     120function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap;
     121
     122implementation
     123
     124uses Math, BGRATransform, Types, SysUtils, BGRAFilterScanner;
     125
     126/////////////////////// PIXELWISE FILTERS ////////////////////////////////
     127
     128{ TGrayscaleTask }
     129
     130constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect);
     131begin
     132  SetSource(bmp);
     133  FBounds := ABounds;
     134end;
     135
     136procedure TGrayscaleTask.DoExecute;
     137var
     138  yb: LongInt;
     139begin
     140  if IsRectEmpty(FBounds) then exit;
     141  for yb := FBounds.Top to FBounds.bottom - 1 do
     142  begin
     143    if GetShouldStop(yb) then break;
     144    TBGRAFilterScannerGrayscale.ComputeFilterAt(FSource.scanline[yb] + FBounds.left,
     145            Destination.scanline[yb] + FBounds.left, FBounds.right-FBounds.left, true);
     146  end;
     147  Destination.InvalidateBitmap;
     148end;
     149
     150{ Filter grayscale applies BGRAToGrayscale function to all pixels }
     151function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     152begin
     153  result := FilterGrayscale(bmp,rect(0,0,bmp.width,bmp.Height));
     154end;
     155
     156function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
     157var scanner: TBGRAFilterScannerGrayscale;
     158begin
     159  result := bmp.NewBitmap(bmp.Width,bmp.Height);
     160  scanner := TBGRAFilterScannerGrayscale.Create(bmp,Point(0,0),True);
     161  result.FillRect(ABounds,scanner,dmSet);
     162  scanner.Free;
     163end;
     164
     165function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect): TFilterTask;
     166begin
     167  result := TGrayscaleTask.Create(bmp,ABounds);
     168end;
     169
     170function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean
     171  ): TBGRACustomBitmap;
     172begin
     173  result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel);
     174end;
     175
     176{ Normalize compute min-max of specified channel and apply an affine transformation
     177  to make it use the full range of values }
     178function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
     179  eachChannel: boolean = True): TBGRACustomBitmap;
     180var scanner: TBGRAFilterScannerNormalize;
     181  remain: TRect;
     182begin
     183  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     184  remain := EmptyRect;
     185  if not IntersectRect(remain,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit;
     186  scanner := TBGRAFilterScannerNormalize.Create(bmp,Point(0,0),remain,eachChannel);
     187  result.FillRect(remain,scanner,dmSet);
     188  scanner.Free;
     189end;
     190
     191////////////////////// 3X3 FILTERS ////////////////////////////////////////////
     192
     193{ This filter compute for each pixel the mean of the eight surrounding pixels,
     194  then the difference between this average pixel and the pixel at the center
     195  of the square. Finally the difference is added to the new pixel, exagerating
     196  its difference with its neighbours. }
     197function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
     198var scanner: TBGRAFilterScanner;
     199begin
     200  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     201  if IsRectEmpty(ABounds) then exit;
     202  scanner := TBGRASharpenScanner.Create(bmp,ABounds,AAmount);
     203  result.FillRect(ABounds,scanner,dmSet);
     204  scanner.Free;
     205end;
     206
     207function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer
     208  ): TBGRACustomBitmap;
     209begin
     210  result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount);
     211end;
     212
     213{ Filter contour computes for each pixel
     214  the grayscale difference with surrounding pixels (in intensity and alpha)
     215  and draw black pixels when there is a difference }
     216function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     217var scanner: TBGRAContourScanner;
     218begin
     219  result := bmp.NewBitmap(bmp.Width, bmp.Height);
     220  scanner := TBGRAContourScanner.Create(bmp,rect(0,0,bmp.width,bmp.height));
     221  result.Fill(scanner);
     222  scanner.Free;
     223end;
     224
     225function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap;
     226begin
     227  result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height), AStrength, AOptions);
     228end;
     229
     230{ Emboss filter computes the difference between each pixel and the surrounding pixels
     231  in the specified direction. }
     232function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect; AStrength: integer; AOptions: TEmbossOptions): TBGRACustomBitmap;
     233var
     234  yb, xb: NativeInt;
     235  dx, dy: single;
     236  idx, idy: NativeInt;
     237  x256,y256: NativeInt;
     238  cMiddle: TBGRAPixel;
     239  hMiddle: THSLAPixel;
     240
     241  tempPixel, refPixel: TBGRAPixel;
     242  pdest: PBGRAPixel;
     243
     244  bounds: TRect;
     245  psrc: PBGRAPixel;
     246  redDiff,greenDiff,blueDiff: NativeUInt;
     247  diff: NativeInt;
     248begin
     249  //compute pixel position and weight
     250  dx   := cos(angle * Pi / 180);
     251  dy   := sin(angle * Pi / 180);
     252  idx := floor(dx);
     253  idy := floor(dy);
     254  x256 := trunc((dx-idx)*256);
     255  y256 := trunc((dy-idy)*256);
     256
     257  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     258  if IsRectEmpty(ABounds) then exit;
     259
     260  bounds := bmp.GetImageBounds;
     261
     262  if not IntersectRect(bounds, bounds, ABounds) then exit;
     263  bounds.Left   := max(0, bounds.Left - 1);
     264  bounds.Top    := max(0, bounds.Top - 1);
     265  bounds.Right  := min(bmp.Width, bounds.Right + 1);
     266  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     267
     268  if not (eoTransparent in AOptions) then
     269  begin
     270    if eoPreserveHue in AOptions then
     271      Result.PutImagePart(ABounds.left,ABounds.top,bmp,ABounds,dmSet)
     272    else
     273      Result.FillRect(ABounds,BGRA(128, 128, 128, 255),dmSet);
     274  end;
     275
     276  //loop through destination
     277  for yb := bounds.Top to bounds.bottom - 1 do
     278  begin
     279    pdest := Result.scanline[yb] + bounds.Left;
     280    psrc := bmp.ScanLine[yb]+bounds.Left;
     281
     282    for xb := bounds.Left+idx to bounds.Right-1+idx do
     283    begin
     284      refPixel := bmp.GetPixel256(xb,yb+idy,x256,y256);
     285      cMiddle := psrc^;
     286      inc(psrc);
     287
     288      if eoPreserveHue in AOptions then
     289      begin
     290        {$push}{$hints off}
     291        diff := ((refPixel.red * refPixel.alpha - cMiddle.red * cMiddle.alpha)+
     292                 (refPixel.green * refPixel.alpha - cMiddle.green * cMiddle.alpha)+
     293                 (refPixel.blue * refPixel.alpha - cMiddle.blue * cMiddle.alpha))* AStrength div 128;
     294        {$pop}
     295        if diff > 0 then
     296          hMiddle := BGRAToHSLA(refPixel)
     297        else
     298          hMiddle := BGRAToHSLA(cMiddle);
     299        hMiddle.lightness := min(65535,max(0,hMiddle.lightness+diff));
     300        if eoTransparent in AOptions then
     301          hMiddle.alpha := min(65535,abs(diff));
     302        pdest^ := HSLAToBGRA(hMiddle);
     303      end else
     304      begin
     305        {$push}{$hints off}
     306        redDiff := NativeUInt(max(0, 65536 + (refPixel.red * refPixel.alpha - cMiddle.red * cMiddle.alpha) * AStrength div 64)) shr 9;
     307        greenDiff := NativeUInt(max(0, 65536 + (refPixel.green * refPixel.alpha - cMiddle.green * cMiddle.alpha) * AStrength div 64)) shr 9;
     308        blueDiff := NativeUInt(max(0, 65536 + (refPixel.blue * refPixel.alpha - cMiddle.blue * cMiddle.alpha) * AStrength div 64)) shr 9;
     309        {$pop}
     310        if (redDiff <> 128) or (greenDiff <> 128) or (blueDiff <> 128) then
     311        begin
     312          tempPixel.red := min(255, redDiff);
     313          tempPixel.green := min(255, greenDiff);
     314          tempPixel.blue := min(255, blueDiff);
     315          if eoTransparent in AOptions then
     316          begin
     317            tempPixel.alpha := min(255,abs(NativeInt(redDiff-128))+abs(NativeInt(greenDiff-128))+abs(NativeInt(blueDiff-128)));
     318            pdest^ := tempPixel;
     319          end else
     320          begin
     321            tempPixel.alpha := 255;
     322            pdest^ := tempPixel;
     323          end;
     324        end;
     325      end;
     326
     327      Inc(pdest);
     328    end;
     329  end;
     330  Result.InvalidateBitmap;
     331end;
     332
     333{ Like general emboss, but with fixed direction and automatic color with transparency }
     334function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
     335  FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
     336var
     337  bounds: TRect;
     338  borderColorOverride: boolean;
     339  borderColorLevel: Int32or64;
     340  scan: TBGRAEmbossHightlightScanner;
     341begin
     342  borderColorOverride := DefineBorderColor.alpha <> 0;
     343  borderColorLevel := DefineBorderColor.red;
     344
     345  Result    := bmp.NewBitmap(bmp.Width, bmp.Height);
     346
     347  if borderColorOverride then
     348    bounds := bmp.GetImageBounds(cRed, borderColorLevel)
     349  else
     350    bounds := bmp.GetImageBounds(cRed);
     351  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     352    exit;
     353  bounds.Left   := max(0, bounds.Left - 1);
     354  bounds.Top    := max(0, bounds.Top - 1);
     355  bounds.Right  := min(bmp.Width, bounds.Right + 1);
     356  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     357
     358  scan := TBGRAEmbossHightlightScanner.Create(bmp, bounds, borderColorOverride);
     359  scan.AllowDirectRead := true;
     360  scan.FillSelection := FillSelection;
     361  if borderColorOverride then scan.SourceBorderColor := DefineBorderColor;
     362  Result.FillRect(bounds, scan, dmSet);
     363  scan.Free;
     364end;
     365
     366function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap;
     367  FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
     368var
     369  bounds: TRect;
     370  borderColorOverride: boolean;
     371  borderColorLevel: int32or64;
     372  scan: TBGRAEmbossHightlightScanner;
     373begin
     374  borderColorOverride := DefineBorderColor.alpha <> 0;
     375  borderColorLevel := DefineBorderColor.red;
     376
     377  if borderColorOverride then
     378    bounds := bmp.GetImageBounds(cRed, borderColorLevel)
     379  else
     380    bounds := bmp.GetImageBounds(cRed);
     381  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     382  begin
     383    Result    := bmp.NewBitmap(0, 0);
     384    exit;
     385  end;
     386  bounds.Left   := max(0, bounds.Left - 1);
     387  bounds.Top    := max(0, bounds.Top - 1);
     388  bounds.Right  := min(bmp.Width, bounds.Right + 1);
     389  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     390
     391  Result    := bmp.NewBitmap(bounds.Right-Bounds.Left+1, bounds.Bottom-Bounds.Top+1);
     392  inc(Offset.X, bounds.Left);
     393  inc(Offset.Y, bounds.Top);
     394
     395  scan := TBGRAEmbossHightlightScanner.Create(bmp, bounds, borderColorOverride);
     396  scan.AllowDirectRead := true;
     397  scan.FillSelection := FillSelection;
     398  if borderColorOverride then scan.SourceBorderColor := DefineBorderColor;
     399  Result.FillRect(rect(0,0,result.Width,result.Height), scan, dmSet, Offset);
     400  scan.Free;
     401end;
     402
     403{ For each component, sort values to get the median }
     404function FilterMedian(bmp: TBGRACustomBitmap;
     405  Option: TMedianOption): TBGRACustomBitmap;
     406
     407  function ComparePixLt(p1, p2: TBGRAPixel): boolean;
     408  begin
     409    if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then
     410      Result := (int32or64(p1.red) shl 8) + (int32or64(p1.green) shl 16) +
     411        int32or64(p1.blue) < (int32or64(p2.red) shl 8) + (int32or64(p2.green) shl 16) +
     412        int32or64(p2.blue)
     413    else
     414      Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue);
     415  end;
     416
     417const
     418  nbpix = 9;
     419var
     420  yb, xb:    int32or64;
     421  dx, dy, n, i, j, k: int32or64;
     422  a_pixels:  array[0..nbpix - 1] of TBGRAPixel;
     423  tempPixel, refPixel: TBGRAPixel;
     424  tempValue: byte;
     425  sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64;
     426  tempAlpha: word;
     427  bounds:    TRect;
     428  pdest:     PBGRAPixel;
     429begin
     430  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     431
     432  bounds := bmp.GetImageBounds;
     433  if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
     434    exit;
     435  bounds.Left   := max(0, bounds.Left - 1);
     436  bounds.Top    := max(0, bounds.Top - 1);
     437  bounds.Right  := min(bmp.Width, bounds.Right + 1);
     438  bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
     439
     440  for yb := bounds.Top to bounds.bottom - 1 do
     441  begin
     442    pdest := Result.scanline[yb] + bounds.left;
     443    for xb := bounds.left to bounds.right - 1 do
     444    begin
     445      n := 0;
     446      for dy := -1 to 1 do
     447        for dx := -1 to 1 do
     448        begin
     449          a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
     450          if a_pixels[n].alpha = 0 then
     451            a_pixels[n] := BGRAPixelTransparent;
     452          Inc(n);
     453        end;
     454      for i := 1 to n - 1 do
     455      begin
     456        j := i;
     457        while (j > 1) and (a_pixels[j].alpha < a_pixels[j - 1].alpha) do
     458        begin
     459          tempValue := a_pixels[j].alpha;
     460          a_pixels[j].alpha := a_pixels[j - 1].alpha;
     461          a_pixels[j - 1].alpha := tempValue;
     462          Dec(j);
     463        end;
     464        j := i;
     465        while (j > 1) and (a_pixels[j].red < a_pixels[j - 1].red) do
     466        begin
     467          tempValue := a_pixels[j].red;
     468          a_pixels[j].red := a_pixels[j - 1].red;
     469          a_pixels[j - 1].red := tempValue;
     470          Dec(j);
     471        end;
     472        j := i;
     473        while (j > 1) and (a_pixels[j].green < a_pixels[j - 1].green) do
     474        begin
     475          tempValue := a_pixels[j].green;
     476          a_pixels[j].green := a_pixels[j - 1].green;
     477          a_pixels[j - 1].green := tempValue;
     478          Dec(j);
     479        end;
     480        j := i;
     481        while (j > 1) and (a_pixels[j].blue < a_pixels[j - 1].blue) do
     482        begin
     483          tempValue := a_pixels[j].blue;
     484          a_pixels[j].blue := a_pixels[j - 1].blue;
     485          a_pixels[j - 1].blue := tempValue;
     486          Dec(j);
     487        end;
     488      end;
     489
     490      refPixel := a_pixels[n div 2];
     491
     492      if option in [moLowSmooth, moMediumSmooth, moHighSmooth] then
     493      begin
     494        sumR    := 0;
     495        sumG    := 0;
     496        sumB    := 0;
     497        sumA    := 0;
     498        BGRAdiv := 0;
     499        nbA     := 0;
     500
     501        case option of
     502          moHighSmooth, moMediumSmooth:
     503          begin
     504            j := 2;
     505            k := 2;
     506          end;
     507          else
     508          begin
     509            j := 1;
     510            k := 1;
     511          end;
     512        end;
     513
     514         {$hints off}
     515        for i := -k to j do
     516        begin
     517          tempPixel := a_pixels[n div 2 + i];
     518          tempAlpha := tempPixel.alpha;
     519          if (option = moMediumSmooth) and ((i = -k) or (i = j)) then
     520            tempAlpha := tempAlpha div 2;
     521
     522          sumR    += tempPixel.red * tempAlpha;
     523          sumG    += tempPixel.green * tempAlpha;
     524          sumB    += tempPixel.blue * tempAlpha;
     525          BGRAdiv += tempAlpha;
     526
     527          sumA += tempAlpha;
     528          Inc(nbA);
     529        end;
     530         {$hints on}
     531        if option = moMediumSmooth then
     532          Dec(nbA);
     533
     534        if (BGRAdiv = 0) then
     535          refPixel := BGRAPixelTransparent
     536        else
     537        begin
     538          refPixel.red   := round(sumR / BGRAdiv);
     539          refPixel.green := round(sumG / BGRAdiv);
     540          refPixel.blue  := round(sumB / BGRAdiv);
     541          refPixel.alpha := round(sumA / nbA);
     542        end;
     543      end;
     544
     545      pdest^ := refPixel;
     546      Inc(pdest);
     547    end;
     548  end;
     549end;
     550
     551//////////////////////// DEFORMATION FILTERS /////////////////////////////////
     552
     553{ Compute the distance for each pixel to the center of the bitmap,
     554  calculate the corresponding angle with arcsin, use this angle
     555  to determine a distance from the center in the source bitmap }
     556function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     557var
     558  cx, cy: single;
     559  scanner: TBGRASphereDeformationScanner;
     560begin
     561  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     562  cx     := bmp.Width / 2 - 0.5;
     563  cy     := bmp.Height / 2 - 0.5;
     564  scanner := TBGRASphereDeformationScanner.Create(bmp,PointF(cx,cy),bmp.Width/2,bmp.Height/2);
     565  result.FillEllipseAntialias(cx,cy,bmp.Width/2-0.5,bmp.Height/2-0.5,scanner);
     566  scanner.Free;
     567end;
     568
     569{ Applies twirl scanner. See TBGRATwirlScanner }
     570function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
     571var twirl: TBGRATwirlScanner;
     572begin
     573  twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);
     574  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     575  result.FillRect(ABounds, twirl, dmSet);
     576  twirl.free;
     577end;
     578
     579function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint;
     580  ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;
     581begin
     582  result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent);
     583end;
     584
     585{ Compute the distance for each pixel to the vertical axis of the bitmap,
     586  calculate the corresponding angle with arcsin, use this angle
     587  to determine a distance from the vertical axis in the source bitmap }
     588function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     589var
     590  cx: single;
     591  scanner: TBGRAVerticalCylinderDeformationScanner;
     592begin
     593  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     594  cx     := bmp.Width / 2 - 0.5;
     595  scanner := TBGRAVerticalCylinderDeformationScanner.Create(bmp,cx,bmp.Width/2);
     596  result.Fill(scanner);
     597  scanner.Free;
     598end;
     599
     600function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
     601const resampleGap=0.6;
     602var
     603  cy, x1, x2, y1, y2, z1, z2, h: single;
     604  yb: int32or64;
     605  resampledBmp: TBGRACustomBitmap;
     606  resampledBmpWidth: int32or64;
     607  resampledFactor,newResampleFactor: single;
     608  sub,resampledSub: TBGRACustomBitmap;
     609  partRect: TRect;
     610  resampleSizeY : int32or64;
     611begin
     612  resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch);
     613  resampledBmpWidth := resampledBmp.Width;
     614  resampledFactor := 2;
     615  Result := bmp.NewBitmap(bmp.Width, bmp.Height*2);
     616  cy     := result.Height / 2 - 0.5;
     617  h      := 1;
     618  for yb := 0 to ((Result.Height-1) div 2) do
     619  begin
     620    y1 := (cy - (yb-0.5)) / (cy+0.5);
     621    y2 := (cy - (yb+0.5)) / (cy+0.5);
     622    if y2 <= 0 then continue;
     623    z1 := h/y1;
     624    z2 := h/y2;
     625    newResampleFactor := 1/(z2-z1)*1.5;
     626
     627    x1 := (z1+1)/2;
     628    x2 := (z2+1)/2;
     629    if newResampleFactor <= resampledFactor*resampleGap then
     630    begin
     631      resampledFactor := newResampleFactor;
     632      if resampledBmp <> bmp then resampledBmp.Free;
     633      if (x2-x1 >= 1) then resampleSizeY := 1 else
     634        resampleSizeY := round(1+((x2-x1)-1)/(1/bmp.Height-1)*(bmp.Height-1));
     635      resampledBmp := bmp.Resample(max(1,round(bmp.Width*resampledFactor)),resampleSizeY,rmSimpleStretch);
     636      resampledBmpWidth := resampledBmp.Width;
     637    end;
     638
     639    partRect := Rect(round(-resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x1*resampledBmp.Height),
     640       round(resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x2*resampledBmp.Height)+1);
     641    if x2-x1 > 1 then
     642    begin
     643      partRect.Top := 0;
     644      partRect.Bottom := 1;
     645    end;
     646    sub := resampledBmp.GetPart(partRect);
     647    if sub <> nil then
     648    begin
     649      resampledSub := sub.Resample(bmp.Width,1,rmFineResample);
     650      result.PutImage(0,yb,resampledSub,dmSet);
     651      result.PutImage(0,Result.Height-1-yb,resampledSub,dmSet);
     652      resampledSub.free;
     653      sub.free;
     654    end;
     655  end;
     656  if resampledBmp <> bmp then resampledBmp.Free;
     657
     658  if result.Height <> bmp.Height then
     659  begin
     660    resampledBmp := result.Resample(bmp.Width,bmp.Height,rmSimpleStretch);
     661    result.free;
     662    result := resampledBmp;
     663  end;
     664end;
     665
     666{ Rotates the image. To do this, loop through the destination and
     667  calculates the position in the source bitmap with an affine transformation }
     668function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
     669  angle: single; correctBlur: boolean): TBGRACustomBitmap;
     670begin
     671  Result := bmp.NewBitmap(bmp.Width, bmp.Height);
     672  Result.PutImageAngle(0,0,bmp,angle,origin.x,origin.y,255,true,correctBlur);
     673end;
     674
     675///////////////////////// BLUR FILTERS //////////////////////////////////////
     676
     677function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: single; blurType: TRadialBlurType): TBGRACustomBitmap;
     678var task: TFilterTask;
     679begin
     680  task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radius,blurTYpe);
     681  result := task.Execute;
     682  task.Free;
     683end;
     684
     685function FilterBlurRadial(bmp: TBGRACustomBitmap; radiusX: single; radiusY: single; blurType: TRadialBlurType): TBGRACustomBitmap;
     686var task: TFilterTask;
     687begin
     688  task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radiusX,radiusY,blurTYpe);
     689  result := task.Execute;
     690  task.Free;
     691end;
     692
     693function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single; ABlurType: TRadialBlurType): TRadialBlurTask;
     694begin
     695  result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType);
     696end;
     697
     698function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     699  ARadiusX,ARadiusY: single; ABlurType: TRadialBlurType): TRadialBlurTask;
     700begin
     701  result := TRadialBlurTask.Create(ABmp,ABounds,ARadiusX,ARadiusY,ABlurType);
     702end;
     703
     704{ Precise blur }
     705
     706function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single): TBGRACustomBitmap;
     707var task: TRadialBlurTask;
     708begin
     709  task := CreateRadialBlurTask(bmp,rect(0,0,bmp.Width,bmp.Height),radius*10,rbPrecise);
     710  result := task.Execute;
     711  task.Free;
     712end;
     713
     714function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: single): TRadialBlurTask;
     715begin
     716  result := TRadialBlurTask.Create(ABmp,ABounds,ARadius*10,rbPrecise);
     717end;
     718
     719function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single; angle: single; oriented: boolean): TBGRACustomBitmap;
     720var task: TFilterTask;
     721begin
     722  task := CreateMotionBlurTask(bmp, rect(0,0,bmp.Width,bmp.Height), distance, angle, oriented);
     723  result := task.Execute;
     724  task.Free;
     725end;
     726
     727function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     728  ADistance, AAngle: single; AOriented: boolean): TMotionBlurTask;
     729begin
     730  result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented);
     731end;
     732
     733function FilterBlur(bmp: TBGRACustomBitmap; AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TBGRACustomBitmap;
     734var task: TFilterTask;
     735begin
     736  task := TCustomBlurTask.Create(bmp,rect(0,0,bmp.Width,bmp.Height), AMask, AMaskIsThreadSafe);
     737  result := task.Execute;
     738  task.Free;
     739end;
     740
     741function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
     742  AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean = false): TFilterTask;
     743begin
     744  result := TCustomBlurTask.Create(ABmp, ABounds, AMask, AMaskIsThreadSafe);
     745end;
     746
     747///////////////////////////////////// OTHER FILTERS ///////////////////////////
    209748
    210749function FilterSmartZoom3(bmp: TBGRACustomBitmap;
     
    364903end;
    365904
    366 { This filter compute for each pixel the mean of the eight surrounding pixels,
    367   then the difference between this average pixel and the pixel at the center
    368   of the square. Finally the difference is added to the new pixel, exagerating
    369   its difference with its neighbours. }
    370 function FilterSharpen(bmp: TBGRACustomBitmap; ABounds: TRect; AAmount: integer = 256): TBGRACustomBitmap;
    371 var
    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;
    378   bounds:   TRect;
    379   Amount256: boolean;
    380   lastXincluded: boolean;
    381   alpha,rgbDivShr1: uint32or64;
    382 begin
    383   if IsRectEmpty(ABounds) then exit;
    384   Amount256 := AAmount = 256;
    385   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    386 
    387   //determine where pixels are in the bitmap
    388   bounds := bmp.GetImageBounds;
    389   if not IntersectRect(bounds, bounds,ABounds) then exit;
    390   bounds.Left   := max(0, bounds.Left - 1);
    391   bounds.Top    := max(0, bounds.Top - 1);
    392   bounds.Right  := min(bmp.Width, bounds.Right + 1);
    393   bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
    394   lastXincluded:= bounds.Right < bmp.Width;
    395 
    396   //loop through the destination bitmap
    397   for yb := bounds.Top to bounds.Bottom - 1 do
    398   begin
    399     pdest := Result.scanline[yb] + bounds.Left;
    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 
    410       //for each pixel, read eight surrounding pixels in the source bitmap
    411       for dy := -1 to 1 do
    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;
    419 
    420       //compute sum
    421       sumR   := 0;
    422       sumG   := 0;
    423       sumB   := 0;
    424       sumA   := 0;
    425       //RGBdiv := 0;
    426       nbA    := 0;
    427 
    428        {$hints off}
    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;
    445        {$hints on}
    446 
    447       //we finally have an average pixel
    448       if ({RGBdiv}sumA = 0) then
    449         refPixel := BGRAPixelTransparent
    450       else
    451       begin
    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;
    456         refPixel.alpha := (sumA + nbA shr 1) div nbA;
    457       end;
    458 
    459       //read the pixel at the center of the square
    460       ptempPixel := a_pixels[0,0];
    461       if refPixel <> BGRAPixelTransparent then
    462       begin
    463         //compute sharpened pixel by adding the difference
    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^;
    480       Inc(pdest);
    481     end;
    482   end;
    483   Result.InvalidateBitmap;
    484 end;
    485 
    486 function FilterSharpen(bmp: TBGRACustomBitmap; AAmount: integer
    487   ): TBGRACustomBitmap;
    488 begin
    489   result := FilterSharpen(bmp,rect(0,0,bmp.Width,bmp.Height),AAmount);
    490 end;
    491 
    492 { Precise blur builds a blur mask with a gradient fill and use
    493   general purpose blur }
    494 procedure FilterBlurRadialPrecise(bmp: TBGRACustomBitmap;
    495   ABounds: TRect; radius: single; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    496 var
    497   blurShape: TBGRACustomBitmap;
    498   intRadius: integer;
    499 begin
    500   if radius = 0 then
    501   begin
    502     ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    503     exit;
    504   end;
    505   intRadius := ceil(radius);
    506   blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);
    507   blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
    508     BGRABlack, gtRadial, pointF(intRadius, intRadius), pointF(
    509     intRadius - radius - 1, intRadius), dmSet);
    510   FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    511   blurShape.Free;
    512 end;
    513 
    514 function FilterBlurRadialPrecise(bmp: TBGRACustomBitmap; radius: single
    515   ): TBGRACustomBitmap;
    516 begin
    517   result := bmp.NewBitmap(bmp.Width,bmp.Height);
    518   FilterBlurRadialPrecise(bmp, rect(0,0,bmp.Width,bmp.Height), radius, result, nil);
    519 end;
    520 
    521 function CreateRadialPreciseBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
    522   ARadius: single): TFilterTask;
    523 begin
    524   result := TRadialPreciseBlurTask.Create(ABmp,ABounds,ARadius);
    525 end;
    526 
    527 { This is a clever solution for fast computing of the blur
    528   effect : it stores an array of vertical sums forming a square
    529   around the pixel which moves with it. For each new pixel,
    530   the vertical sums are kept except for the last column of
    531   the square }
    532 procedure FilterBlurFast(bmp: TBGRACustomBitmap; ABounds: TRect;
    533   radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    534  {$IFDEF CPU64}{$DEFINE FASTBLUR_DOUBLE}{$ENDIF}
    535   type
    536     TRowSum = record
    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}
    559     result.alpha:= (sum.sumA+sum.aDiv shr 1) div sum.aDiv;
    560     result.red := (sum.sumR+sum.rgbDiv shr 1) div sum.rgbDiv;
    561     result.green := (sum.sumG+sum.rgbDiv shr 1) div sum.rgbDiv;
    562     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;
    585   end;
    586 
    587   {$I blurfast.inc}
    588 
    589 { Normal radial blur compute a blur mask with a GradientFill and
    590   then posterize to optimize general purpose blur }
    591 procedure FilterBlurRadialNormal(bmp: TBGRACustomBitmap;
    592   ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    593 var
    594   blurShape: TBGRACustomBitmap;
    595   n: Int32or64;
    596   p: PBGRAPixel;
    597 begin
    598   if radius = 0 then
    599   begin
    600     ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    601     exit;
    602   end;
    603   blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    604   blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height, BGRAWhite,
    605     BGRABlack, gtRadial, pointF(radius, radius), pointF(-0.5, radius), dmSet);
    606   p := blurShape.Data;
    607   for n := 0 to blurShape.NbPixels-1 do
    608   begin
    609     p^.red := p^.red and $F0;
    610     p^.green := p^.red;
    611     p^.blue := p^.red;
    612     inc(p);
    613   end;
    614   FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    615   blurShape.Free;
    616 end;
    617 
    618 { Blur disk creates a disk mask with a FillEllipse }
    619 procedure FilterBlurDisk(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    620 var
    621   blurShape: TBGRACustomBitmap;
    622 begin
    623   if radius = 0 then
    624   begin
    625     ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    626     exit;
    627   end;
    628   blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    629   blurShape.Fill(BGRABlack);
    630   blurShape.FillEllipseAntialias(radius, radius, radius + 0.5, radius + 0.5, BGRAWhite);
    631   FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    632   blurShape.Free;
    633 end;
    634 
    635 { Corona blur use a circle as mask }
    636 procedure FilterBlurCorona(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    637 var
    638   blurShape: TBGRACustomBitmap;
    639 begin
    640   if radius = 0 then
    641   begin
    642     ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    643     exit;
    644   end;
    645   blurShape := bmp.NewBitmap(2 * radius + 1, 2 * radius + 1);
    646   blurShape.Fill(BGRABlack);
    647   blurShape.EllipseAntialias(radius, radius, radius, radius, BGRAWhite, 1);
    648   FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    649   blurShape.Free;
    650 end;
    651 
    652 function FilterBlurBox(bmp: TBGRACustomBitmap; radius: integer; ADestination: TBGRACustomBitmap): TBGRACustomBitmap;
    653 var task: TBoxBlurTask;
    654 begin
    655   task := TBoxBlurTask.Create(bmp, rect(0,0,bmp.Width,bmp.Height), radius);
    656   task.Destination := ADestination;
    657   result := task.Execute;
    658   task.Free;
    659 end;
    660 
    661 procedure FilterBlurRadial(bmp: TBGRACustomBitmap; ABounds: TRect; radius: integer;
    662   blurType: TRadialBlurType; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    663 begin
    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;
    677 end;
    678 
    679 function FilterBlurRadial(bmp: TBGRACustomBitmap; radius: integer;
    680   blurType: TRadialBlurType): TBGRACustomBitmap;
    681 begin
    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;
    690 end;
    691 
    692 function CreateRadialBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect; ARadius: integer;
    693   ABlurType: TRadialBlurType): TFilterTask;
    694 begin
    695   if ABlurType = rbBox then
    696     result := TBoxBlurTask.Create(ABmp,ABounds,ARadius)
    697   else
    698     result := TRadialBlurTask.Create(ABmp,ABounds,ARadius,ABlurType);
    699 end;
    700 
    701 { This filter draws an antialiased line to make the mask, and
    702   if the motion blur is oriented, does a GradientFill to orient it }
    703 procedure FilterBlurMotion(bmp: TBGRACustomBitmap; ABounds: TRect; distance: single;
    704   angle: single; oriented: boolean; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    705 var
    706   blurShape: TBGRACustomBitmap;
    707   intRadius: integer;
    708   dx, dy, d: single;
    709 begin
    710   if distance < 1e-6 then
    711   begin
    712     ADestination.PutImagePart(ABounds.Left,ABounds.Top,bmp,ABounds,dmSet);
    713     exit;
    714   end;
    715   intRadius := ceil(distance / 2);
    716   blurShape := bmp.NewBitmap(2 * intRadius + 1, 2 * intRadius + 1);
    717   d  := distance / 2;
    718   dx := cos(angle * Pi / 180);
    719   dy := sin(angle * Pi / 180);
    720   blurShape.Fill(BGRABlack);
    721   blurShape.DrawLineAntialias(intRadius - dx * d, intRadius - dy *
    722     d, intRadius + dx * d, intRadius + dy * d, BGRAWhite, 1, True);
    723   if oriented then
    724     blurShape.GradientFill(0, 0, blurShape.Width, blurShape.Height,
    725       BGRAPixelTransparent, BGRABlack, gtRadial, pointF(intRadius -
    726       dx * d, intRadius - dy * d),
    727       pointF(intRadius + dx * (d + 0.5), intRadius + dy * (d + 0.5)),
    728       dmFastBlend, False);
    729   FilterBlur(bmp, ABounds, blurShape, ADestination, ACheckShouldStop);
    730   blurShape.Free;
    731 end;
    732 
    733 function FilterBlurMotion(bmp: TBGRACustomBitmap; distance: single;
    734   angle: single; oriented: boolean): TBGRACustomBitmap;
    735 begin
    736   result := bmp.NewBitmap(bmp.Width,bmp.Height);
    737   FilterBlurMotion(bmp,rect(0,0,bmp.Width,bmp.Height),distance,angle,oriented,result,nil);
    738 end;
    739 
    740 function CreateMotionBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
    741   ADistance, AAngle: single; AOriented: boolean): TFilterTask;
    742 begin
    743   result := TMotionBlurTask.Create(ABmp,ABounds,ADistance,AAngle,AOriented);
    744 end;
    745 
    746 { General purpose blur : compute pixel sum according to the mask and then
    747   compute only difference while scanning from the left to the right }
    748 procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    749   blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    750 procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    751   blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    752 procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
    753   blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    754 procedure FilterBlurMask64(bmp: TBGRACustomBitmap;
    755   blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc); forward;
    756 
    757 //make sure value is in the range 0..255
    758 function clampByte(value: Int32or64): byte; inline;
    759 begin
    760   if value < 0 then result := 0 else
    761   if value > 255 then result := 255 else
    762     result := value;
    763 end;
    764 
    765905function FilterPixelate(bmp: TBGRACustomBitmap; pixelSize: integer;
    766906  useResample: boolean; filter: TResampleFilter): TBGRACustomBitmap;
     
    820960end;
    821961
    822 function FilterBlur(bmp: TBGRACustomBitmap; blurMask: TBGRACustomBitmap): TBGRACustomBitmap;
    823 begin
    824   result := bmp.NewBitmap(bmp.Width,bmp.Height);
    825   FilterBlur(bmp,rect(0,0,bmp.Width,bmp.Height),blurMask,result,nil);
    826 end;
    827 
    828 function CreateBlurTask(ABmp: TBGRACustomBitmap; ABounds: TRect;
    829   AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean): TFilterTask;
    830 begin
    831   result := TCustomBlurTask.Create(ABmp,ABounds,AMask,AMaskIsThreadSafe);
    832 end;
    833 
    834 procedure FilterBlur(bmp: TBGRACustomBitmap;
    835   ABounds: TRect; blurMask: TBGRACustomBitmap; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    836 {$IFDEF CPU64}
    837 begin
    838     FilterBlurMask64(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
    839 end;
    840 {$ELSE}
    841 var
    842   maskSum: int64;
    843   i: Int32or64;
    844   p: PBGRAPixel;
    845   maskShift: integer;
    846 begin
    847   maskSum := 0;
    848   p := blurMask.data;
    849   for i := 0 to blurMask.NbPixels-1 do
    850   begin
    851     inc(maskSum,p^.red);
    852     inc(p);
    853   end;
    854   maskShift := 0;
    855   while maskSum > 32768 do
    856   begin
    857     inc(maskShift);
    858     maskSum := maskSum shr 1;
    859   end;
    860   //check if sum can be stored in a 32-bit signed integer
    861   if maskShift = 0 then
    862     FilterBlurSmallMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop) else
    863   {$IFDEF CPU32}
    864   if maskShift < 8 then
    865     FilterBlurSmallMaskWithShift(bmp,blurMask,maskShift,ABounds,ADestination,ACheckShouldStop) else
    866   {$ENDIF}
    867     FilterBlurBigMask(bmp,blurMask,ABounds,ADestination,ACheckShouldStop);
    868 end;
    869 {$ENDIF}
    870 
    871 //32-bit blur with shift
    872 procedure FilterBlurSmallMaskWithShift(bmp: TBGRACustomBitmap;
    873   blurMask: TBGRACustomBitmap; maskShift: integer; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    874 
    875   var
    876     sumR, sumG, sumB, sumA, Adiv, RGBdiv : integer;
    877 
    878   function ComputeAverage: TBGRAPixel; inline;
    879   begin
    880     result.alpha := (sumA + Adiv shr 1) div Adiv;
    881     if result.alpha = 0 then
    882       result := BGRAPixelTransparent
    883     else
    884     begin
    885       result.red   := clampByte((sumR + RGBdiv shr 1) div RGBdiv);
    886       result.green := clampByte((sumG + RGBdiv shr 1) div RGBdiv);
    887       result.blue  := clampByte((sumB + RGBdiv shr 1) div RGBdiv);
    888     end;
    889   end;
    890 
    891   {$define PARAM_MASKSHIFT}
    892   {$I blurnormal.inc}
    893 
    894 //32-bit blur
    895 procedure FilterBlurSmallMask(bmp: TBGRACustomBitmap;
    896   blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    897 
    898   var
    899     sumR, sumG, sumB, sumA, Adiv : integer;
    900 
    901   function ComputeAverage: TBGRAPixel; inline;
    902   begin
    903     result.alpha := (sumA + Adiv shr 1) div Adiv;
    904     if result.alpha = 0 then
    905       result := BGRAPixelTransparent
    906     else
    907     begin
    908       result.red   := clampByte((sumR + sumA shr 1) div sumA);
    909       result.green := clampByte((sumG + sumA shr 1) div sumA);
    910       result.blue  := clampByte((sumB + sumA shr 1) div sumA);
    911     end;
    912   end;
    913 
    914   {$I blurnormal.inc}
    915 
    916 //64-bit blur
    917 procedure 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 
    938 //floating point blur
    939 procedure FilterBlurBigMask(bmp: TBGRACustomBitmap;
    940   blurMask: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    941 
    942   var
    943     sumR, sumG, sumB, sumA, Adiv : single;
    944 
    945   function ComputeAverage: TBGRAPixel; inline;
    946   begin
    947     result.alpha := round(sumA/Adiv);
    948     if result.alpha = 0 then
    949       result := BGRAPixelTransparent
    950     else
    951     begin
    952       result.red   := clampByte(round(sumR/sumA));
    953       result.green := clampByte(round(sumG/sumA));
    954       result.blue  := clampByte(round(sumB/sumA));
    955     end;
    956   end;
    957 
    958   {$I blurnormal.inc}
    959 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single): TBGRACustomBitmap;
    960 begin
    961   result := FilterEmboss(bmp, angle, rect(0,0,bmp.Width,bmp.Height));
    962 end;
    963 
    964 { Emboss filter computes the difference between each pixel and the surrounding pixels
    965   in the specified direction. }
    966 function FilterEmboss(bmp: TBGRACustomBitmap; angle: single; ABounds: TRect): TBGRACustomBitmap;
    967 var
    968   yb, xb: Int32or64;
    969   dx, dy: single;
    970   idx1, idy1, idx2, idy2, idx3, idy3, idx4, idy4: Int32or64;
    971   w:      array[1..4] of single;
    972   iw:     uint32or64;
    973   c:      array[0..4] of TBGRAPixel;
    974 
    975   i:     Int32or64;
    976   sumR, sumG, sumB, sumA, RGBdiv, Adiv: UInt32or64;
    977   tempPixel, refPixel: TBGRAPixel;
    978   pdest: PBGRAPixel;
    979 
    980   bounds: TRect;
    981   onHorizBorder: boolean;
    982   psrc: array[-1..1] of PBGRAPixel;
    983 begin
    984   if IsRectEmpty(ABounds) then exit;
    985   //compute pixel position and weight
    986   dx   := cos(angle * Pi / 180);
    987   dy   := sin(angle * Pi / 180);
    988   idx1 := floor(dx);
    989   idy1 := floor(dy);
    990   idx2 := ceil(dx);
    991   idy2 := ceil(dy);
    992   idx3 := idx1;
    993   idy3 := idy2;
    994   idx4 := idx2;
    995   idy4 := idy1;
    996 
    997   w[1] := (1 - abs(idx1 - dx)) * (1 - abs(idy1 - dy));
    998   w[2] := (1 - abs(idx2 - dx)) * (1 - abs(idy2 - dy));
    999   w[3] := (1 - abs(idx3 - dx)) * (1 - abs(idy3 - dy));
    1000   w[4] := (1 - abs(idx4 - dx)) * (1 - abs(idy4 - dy));
    1001 
    1002   //fill with gray
    1003   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1004   Result.Fill(BGRA(128, 128, 128, 255));
    1005 
    1006   bounds := bmp.GetImageBounds;
    1007   if not IntersectRect(bounds, bounds, ABounds) then exit;
    1008   bounds.Left   := max(0, bounds.Left - 1);
    1009   bounds.Top    := max(0, bounds.Top - 1);
    1010   bounds.Right  := min(bmp.Width, bounds.Right + 1);
    1011   bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
    1012 
    1013   //loop through destination
    1014   for yb := bounds.Top to bounds.bottom - 1 do
    1015   begin
    1016     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;
    1021     for xb := bounds.Left to bounds.Right - 1 do
    1022     begin
    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;
    1037 
    1038       sumR   := 0;
    1039       sumG   := 0;
    1040       sumB   := 0;
    1041       sumA   := 0;
    1042       Adiv   := 0;
    1043       RGBdiv := 0;
    1044 
    1045       //compute sum
    1046        {$hints off}
    1047       for i := 1 to 4 do
    1048       begin
    1049         tempPixel := c[i];
    1050         if tempPixel.alpha = 0 then
    1051           tempPixel := c[0];
    1052         iw     := round(w[i] * tempPixel.alpha);
    1053         sumR   += tempPixel.red * iw;
    1054         sumG   += tempPixel.green * iw;
    1055         sumB   += tempPixel.blue * iw;
    1056         RGBdiv += iw;
    1057         sumA   += iw;
    1058         Adiv   += round(w[i] * 255);
    1059       end;
    1060        {$hints on}
    1061 
    1062       //average
    1063       if (Adiv = 0) or (RGBdiv = 0) then
    1064         refPixel := c[0]
    1065       else
    1066       begin
    1067         refPixel.red   := (sumR + RGBdiv shr 1) div RGBdiv;
    1068         refPixel.green := (sumG + RGBdiv shr 1) div RGBdiv;
    1069         refPixel.blue  := (sumB + RGBdiv shr 1) div RGBdiv;
    1070         refPixel.alpha := (sumA * 255 + Adiv shr 1) div Adiv;
    1071       end;
    1072 
    1073       //difference with center pixel
    1074        {$hints off}
    1075       tempPixel.red := max(0, min(512 * 255, 65536 + refPixel.red *
    1076         refPixel.alpha - c[0].red * c[0].alpha)) shr 9;
    1077       tempPixel.green := max(0, min(512 * 255, 65536 + refPixel.green *
    1078         refPixel.alpha - c[0].green * c[0].alpha)) shr 9;
    1079       tempPixel.blue := max(0, min(512 * 255, 65536 + refPixel.blue *
    1080         refPixel.alpha - c[0].blue * c[0].alpha)) shr 9;
    1081        {$hints on}
    1082       tempPixel.alpha := 255;
    1083       pdest^ := tempPixel;
    1084       Inc(pdest);
    1085       inc(psrc[0]);
    1086       if psrc[-1] <> nil then inc(psrc[-1]);
    1087       if psrc[1] <> nil then inc(psrc[1]);
    1088     end;
    1089   end;
    1090   Result.InvalidateBitmap;
    1091 end;
    1092 
    1093 { Like general emboss, but with fixed direction and automatic color with transparency }
    1094 function FilterEmbossHighlight(bmp: TBGRACustomBitmap;
    1095   FillSelection: boolean; DefineBorderColor: TBGRAPixel): TBGRACustomBitmap;
    1096 var
    1097   yb, xb: Int32or64;
    1098   c0,c1,c2,c3,c4,c5,c6: Int32or64;
    1099 
    1100   bmpWidth, bmpHeight: Int32or64;
    1101   slope, h: byte;
    1102   sum:      Int32or64;
    1103   tempPixel, highlight: TBGRAPixel;
    1104   pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
    1105 
    1106   bounds: TRect;
    1107   borderColorOverride: boolean;
    1108   borderColorLevel: Int32or64;
    1109 
    1110   currentBorderColor: Int32or64;
    1111 begin
    1112   borderColorOverride := DefineBorderColor.alpha <> 0;
    1113   borderColorLevel := DefineBorderColor.red;
    1114 
    1115   bmpWidth  := bmp.Width;
    1116   bmpHeight := bmp.Height;
    1117   Result    := bmp.NewBitmap(bmpWidth, bmpHeight);
    1118 
    1119   if borderColorOverride then
    1120     bounds := bmp.GetImageBounds(cRed, borderColorLevel)
    1121   else
    1122     bounds := bmp.GetImageBounds(cRed);
    1123   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1124     exit;
    1125   bounds.Left   := max(0, bounds.Left - 1);
    1126   bounds.Top    := max(0, bounds.Top - 1);
    1127   bounds.Right  := min(bmpWidth, bounds.Right + 1);
    1128   bounds.Bottom := min(bmpHeight, bounds.Bottom + 1);
    1129 
    1130   currentBorderColor := borderColorLevel;
    1131   for yb := bounds.Top to bounds.Bottom - 1 do
    1132   begin
    1133     pdest := Result.scanline[yb] + bounds.Left;
    1134 
    1135     if yb > 0 then
    1136       psrcUp := bmp.Scanline[yb - 1] + bounds.Left
    1137     else
    1138       psrcUp := nil;
    1139     psrc := bmp.scanline[yb] + bounds.Left;
    1140     if yb < bmpHeight - 1 then
    1141       psrcDown := bmp.scanline[yb + 1] + bounds.Left
    1142     else
    1143       psrcDown := nil;
    1144 
    1145     for xb := bounds.Left to bounds.Right - 1 do
    1146     begin
    1147       c0 := pbyte(psrc)^;
    1148       if not borderColorOverride then currentBorderColor := c0;
    1149       if (xb = 0) then
    1150       begin
    1151         c1 := currentBorderColor;
    1152         c2 := currentBorderColor;
    1153       end
    1154       else
    1155       begin
    1156         if psrcUp <> nil then
    1157           c1 := pbyte(psrcUp - 1)^
    1158         else
    1159           c1 := currentBorderColor;
    1160         c2 := pbyte(psrc - 1)^;
    1161       end;
    1162       if psrcUp <> nil then
    1163       begin
    1164         c3 := pbyte(psrcUp)^;
    1165         Inc(psrcUp);
    1166       end
    1167       else
    1168        c3 := currentBorderColor;
    1169 
    1170       if (xb = bmpWidth - 1) then
    1171       begin
    1172         c4 := currentBorderColor;
    1173         c5 := currentBorderColor;
    1174       end
    1175       else
    1176       begin
    1177         if psrcDown <> nil then
    1178           c4 := pbyte(psrcDown + 1)^
    1179         else
    1180           c4 := currentBorderColor;
    1181         c5 := pbyte(psrc + 1)^;
    1182       end;
    1183       if psrcDown <> nil then
    1184       begin
    1185         c6 := pbyte(psrcDown)^;
    1186         Inc(psrcDown);
    1187       end
    1188       else
    1189         c6 := currentBorderColor;
    1190       Inc(psrc);
    1191 
    1192       sum := c4+c5+c6-c1-c2-c3;
    1193       sum := 128 + sum div 3;
    1194       if sum > 255 then
    1195         slope := 255
    1196       else
    1197       if sum < 1 then
    1198         slope := 1
    1199       else
    1200         slope := sum;
    1201       h := c0;
    1202 
    1203       tempPixel.red   := slope;
    1204       tempPixel.green := slope;
    1205       tempPixel.blue  := slope;
    1206       tempPixel.alpha := abs(slope - 128) * 2;
    1207 
    1208       if fillSelection then
    1209       begin
    1210         highlight := BGRA(h shr 2, h shr 1, h, h shr 1);
    1211         if tempPixel.red < highlight.red then
    1212           tempPixel.red := highlight.red;
    1213         if tempPixel.green < highlight.green then
    1214           tempPixel.green := highlight.green;
    1215         if tempPixel.blue < highlight.blue then
    1216           tempPixel.blue := highlight.blue;
    1217         if tempPixel.alpha < highlight.alpha then
    1218           tempPixel.alpha := highlight.alpha;
    1219       end;
    1220 
    1221       pdest^ := tempPixel;
    1222       Inc(pdest);
    1223     end;
    1224   end;
    1225   Result.InvalidateBitmap;
    1226 end;
    1227 
    1228 function FilterEmbossHighlightOffset(bmp: TBGRACustomBitmap;
    1229   FillSelection: boolean; DefineBorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap;
    1230 var
    1231   yb, xb: int32or64;
    1232   c0,c1,c2,c3,c4,c5,c6: int32or64;
    1233 
    1234   bmpWidth, bmpHeight: int32or64;
    1235   slope, h: byte;
    1236   sum:      int32or64;
    1237   tempPixel, highlight: TBGRAPixel;
    1238   pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
    1239 
    1240   bounds: TRect;
    1241   borderColorOverride: boolean;
    1242   borderColorLevel: int32or64;
    1243 
    1244   currentBorderColor: int32or64;
    1245 begin
    1246   borderColorOverride := DefineBorderColor.alpha <> 0;
    1247   borderColorLevel := DefineBorderColor.red;
    1248 
    1249   bmpWidth  := bmp.Width;
    1250   bmpHeight := bmp.Height;
    1251 
    1252   if borderColorOverride then
    1253     bounds := bmp.GetImageBounds(cRed, borderColorLevel)
    1254   else
    1255     bounds := bmp.GetImageBounds(cRed);
    1256   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1257   begin
    1258     Result    := bmp.NewBitmap(0, 0);
    1259     exit;
    1260   end;
    1261   bounds.Left   := max(0, bounds.Left - 1);
    1262   bounds.Top    := max(0, bounds.Top - 1);
    1263   bounds.Right  := min(bmpWidth, bounds.Right + 1);
    1264   bounds.Bottom := min(bmpHeight, bounds.Bottom + 1);
    1265 
    1266   Result    := bmp.NewBitmap(bounds.Right-Bounds.Left+1, bounds.Bottom-Bounds.Top+1);
    1267   inc(Offset.X, bounds.Left);
    1268   inc(Offset.Y, bounds.Top);
    1269 
    1270   currentBorderColor := borderColorLevel;
    1271   for yb := bounds.Top to bounds.Bottom - 1 do
    1272   begin
    1273     pdest := Result.scanline[yb-Bounds.Top];
    1274 
    1275     if yb > 0 then
    1276       psrcUp := bmp.Scanline[yb - 1] + bounds.Left
    1277     else
    1278       psrcUp := nil;
    1279     psrc := bmp.scanline[yb] + bounds.Left;
    1280     if yb < bmpHeight - 1 then
    1281       psrcDown := bmp.scanline[yb + 1] + bounds.Left
    1282     else
    1283       psrcDown := nil;
    1284 
    1285     for xb := bounds.Left to bounds.Right - 1 do
    1286     begin
    1287       c0 := pbyte(psrc)^;
    1288       if not borderColorOverride then currentBorderColor := c0;
    1289       if (xb = 0) then
    1290       begin
    1291         c1 := currentBorderColor;
    1292         c2 := currentBorderColor;
    1293       end
    1294       else
    1295       begin
    1296         if psrcUp <> nil then
    1297           c1 := pbyte(psrcUp - 1)^
    1298         else
    1299           c1 := currentBorderColor;
    1300         c2 := pbyte(psrc - 1)^;
    1301       end;
    1302       if psrcUp <> nil then
    1303       begin
    1304         c3 := pbyte(psrcUp)^;
    1305         Inc(psrcUp);
    1306       end
    1307       else
    1308        c3 := currentBorderColor;
    1309 
    1310       if (xb = bmpWidth - 1) then
    1311       begin
    1312         c4 := currentBorderColor;
    1313         c5 := currentBorderColor;
    1314       end
    1315       else
    1316       begin
    1317         if psrcDown <> nil then
    1318           c4 := pbyte(psrcDown + 1)^
    1319         else
    1320           c4 := currentBorderColor;
    1321         c5 := pbyte(psrc + 1)^;
    1322       end;
    1323       if psrcDown <> nil then
    1324       begin
    1325         c6 := pbyte(psrcDown)^;
    1326         Inc(psrcDown);
    1327       end
    1328       else
    1329         c6 := currentBorderColor;
    1330       Inc(psrc);
    1331 
    1332       sum := c4+c5+c6-c1-c2-c3;
    1333       sum := 128 + sum div 3;
    1334       if sum > 255 then
    1335         slope := 255
    1336       else
    1337       if sum < 1 then
    1338         slope := 1
    1339       else
    1340         slope := sum;
    1341       h := c0;
    1342 
    1343       tempPixel.red   := slope;
    1344       tempPixel.green := slope;
    1345       tempPixel.blue  := slope;
    1346       tempPixel.alpha := abs(slope - 128) * 2;
    1347 
    1348       if fillSelection then
    1349       begin
    1350         highlight := BGRA(h shr 2, h shr 1, h, h shr 1);
    1351         if tempPixel.red < highlight.red then
    1352           tempPixel.red := highlight.red;
    1353         if tempPixel.green < highlight.green then
    1354           tempPixel.green := highlight.green;
    1355         if tempPixel.blue < highlight.blue then
    1356           tempPixel.blue := highlight.blue;
    1357         if tempPixel.alpha < highlight.alpha then
    1358           tempPixel.alpha := highlight.alpha;
    1359       end;
    1360 
    1361       pdest^ := tempPixel;
    1362       Inc(pdest);
    1363     end;
    1364   end;
    1365   Result.InvalidateBitmap;
    1366 end;
    1367 
    1368 function FilterNormalize(bmp: TBGRACustomBitmap; eachChannel: boolean
    1369   ): TBGRACustomBitmap;
    1370 begin
    1371   result := FilterNormalize(bmp, rect(0,0,bmp.Width,bmp.Height), eachChannel);
    1372 end;
    1373 
    1374 { Normalize compute min-max of specified channel and apply an affine transformation
    1375   to make it use the full range of values }
    1376 function FilterNormalize(bmp: TBGRACustomBitmap; ABounds: TRect;
    1377   eachChannel: boolean = True): TBGRACustomBitmap;
    1378 var
    1379   psrc, pdest: PBGRAPixel;
    1380   c: TExpandedPixel;
    1381   xcount,xb,yb: int32or64;
    1382   minValRed, maxValRed, minValGreen, maxValGreen, minValBlue, maxValBlue,
    1383   minAlpha, maxAlpha, addValRed, addValGreen, addValBlue, addAlpha: word;
    1384   factorValRed, factorValGreen, factorValBlue, factorAlpha: int32or64;
    1385 begin
    1386   if not IntersectRect(ABounds,ABounds,rect(0,0,bmp.Width,bmp.Height)) then exit;
    1387   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1388   bmp.LoadFromBitmapIfNeeded;
    1389   maxValRed := 0;
    1390   minValRed := 65535;
    1391   maxValGreen := 0;
    1392   minValGreen := 65535;
    1393   maxValBlue := 0;
    1394   minValBlue := 65535;
    1395   maxAlpha  := 0;
    1396   minAlpha  := 65535;
    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;
    1423   end;
    1424   if not eachChannel then
    1425   begin
    1426     minValRed   := min(min(minValRed, minValGreen), minValBlue);
    1427     maxValRed   := max(max(maxValRed, maxValGreen), maxValBlue);
    1428     minValGreen := minValRed;
    1429     maxValGreen := maxValRed;
    1430     minValBlue  := minValBlue;
    1431     maxValBlue  := maxValBlue;
    1432   end;
    1433   if maxValRed > minValRed then
    1434   begin
    1435     factorValRed := 268431360 div (maxValRed - minValRed);
    1436     addValRed    := 0;
    1437   end
    1438   else
    1439   begin
    1440     factorValRed := 0;
    1441     if minValRed = 0 then
    1442       addValRed := 0
    1443     else
    1444       addValRed := 65535;
    1445   end;
    1446   if maxValGreen > minValGreen then
    1447   begin
    1448     factorValGreen := 268431360 div (maxValGreen - minValGreen);
    1449     addValGreen    := 0;
    1450   end
    1451   else
    1452   begin
    1453     factorValGreen := 0;
    1454     if minValGreen = 0 then
    1455       addValGreen := 0
    1456     else
    1457       addValGreen := 65535;
    1458   end;
    1459   if maxValBlue > minValBlue then
    1460   begin
    1461     factorValBlue := 268431360 div (maxValBlue - minValBlue);
    1462     addValBlue    := 0;
    1463   end
    1464   else
    1465   begin
    1466     factorValBlue := 0;
    1467     if minValBlue = 0 then
    1468       addValBlue := 0
    1469     else
    1470       addValBlue := 65535;
    1471   end;
    1472   if maxAlpha > minAlpha then
    1473   begin
    1474     factorAlpha := 268431360 div (maxAlpha - minAlpha);
    1475     addAlpha    := 0;
    1476   end
    1477   else
    1478   begin
    1479     factorAlpha := 0;
    1480     if minAlpha = 0 then
    1481       addAlpha := 0
    1482     else
    1483       addAlpha := 65535;
    1484   end;
    1485 
    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;
    1501   end;
    1502   Result.InvalidateBitmap;
    1503 end;
    1504 
    1505 { Rotates the image. To do this, loop through the destination and
    1506   calculates the position in the source bitmap with an affine transformation }
    1507 function FilterRotate(bmp: TBGRACustomBitmap; origin: TPointF;
    1508   angle: single; correctBlur: boolean): TBGRACustomBitmap;
    1509 var
    1510   bounds:     TRect;
    1511   pdest:      PBGRAPixel;
    1512   xsrc, ysrc: single;
    1513   savexysrc, pt: TPointF;
    1514   dx, dy:     single;
    1515   xb, yb:     int32or64;
    1516   minx, miny, maxx, maxy: single;
    1517   rf : TResampleFilter;
    1518 
    1519   function RotatePos(x, y: single): TPointF;
    1520   var
    1521     px, py: single;
    1522   begin
    1523     px     := x - origin.x;
    1524     py     := y - origin.y;
    1525     Result := PointF(origin.x + px * dx + py * dy, origin.y - px * dy + py * dx);
    1526   end;
    1527 
    1528 begin
    1529   bounds := bmp.GetImageBounds;
    1530   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1531   begin
    1532     Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1533     exit;
    1534   end;
    1535 
    1536   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1537   if correctBlur then rf := rfHalfCosine else rf := rfLinear;
    1538 
    1539   //compute new bounding rectangle
    1540   dx   := cos(angle * Pi / 180);
    1541   dy   := -sin(angle * Pi / 180);
    1542   pt   := RotatePos(bounds.left, bounds.top);
    1543   minx := pt.x;
    1544   miny := pt.y;
    1545   maxx := pt.x;
    1546   maxy := pt.y;
    1547   pt   := RotatePos(bounds.Right - 1, bounds.top);
    1548   if pt.x < minx then
    1549     minx := pt.x
    1550   else
    1551   if pt.x > maxx then
    1552     maxx := pt.x;
    1553   if pt.y < miny then
    1554     miny := pt.y
    1555   else
    1556   if pt.y > maxy then
    1557     maxy := pt.y;
    1558   pt     := RotatePos(bounds.Right - 1, bounds.bottom - 1);
    1559   if pt.x < minx then
    1560     minx := pt.x
    1561   else
    1562   if pt.x > maxx then
    1563     maxx := pt.x;
    1564   if pt.y < miny then
    1565     miny := pt.y
    1566   else
    1567   if pt.y > maxy then
    1568     maxy := pt.y;
    1569   pt     := RotatePos(bounds.left, bounds.bottom - 1);
    1570   if pt.x < minx then
    1571     minx := pt.x
    1572   else
    1573   if pt.x > maxx then
    1574     maxx := pt.x;
    1575   if pt.y < miny then
    1576     miny := pt.y
    1577   else
    1578   if pt.y > maxy then
    1579     maxy := pt.y;
    1580 
    1581   bounds.left   := max(0, floor(minx));
    1582   bounds.top    := max(0, floor(miny));
    1583   bounds.right  := min(bmp.Width, ceil(maxx) + 1);
    1584   bounds.bottom := min(bmp.Height, ceil(maxy) + 1);
    1585 
    1586   //reciproqual
    1587   dy   := -dy;
    1588   pt   := RotatePos(bounds.left, bounds.top);
    1589   xsrc := pt.x;
    1590   ysrc := pt.y;
    1591   for yb := bounds.Top to bounds.bottom - 1 do
    1592   begin
    1593     pdest     := Result.scanline[yb] + bounds.left;
    1594     savexysrc := pointf(xsrc, ysrc);
    1595     for xb := bounds.left to bounds.right - 1 do
    1596     begin
    1597       pdest^ := bmp.GetPixel(xsrc, ysrc, rf);
    1598       Inc(pdest);
    1599       xsrc += dx;
    1600       ysrc -= dy;
    1601     end;
    1602     xsrc := savexysrc.x + dy;
    1603     ysrc := savexysrc.y + dx;
    1604   end;
    1605   Result.InvalidateBitmap;
    1606 end;
    1607 
    1608 { Filter grayscale applies BGRAToGrayscale function to all pixels }
    1609 procedure FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect; ADestination: TBGRACustomBitmap; ACheckShouldStop: TCheckShouldStopFunc);
    1610 var
    1611   pdest, psrc: PBGRAPixel;
    1612   xb, yb:      int32or64;
    1613 
    1614 begin
    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
    1623     begin
    1624       pdest^ := BGRAToGrayscale(psrc^);
    1625       Inc(pdest);
    1626       Inc(psrc);
    1627     end;
    1628   end;
    1629   ADestination.InvalidateBitmap;
    1630 end;
    1631 
    1632 function FilterGrayscale(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    1633 begin
    1634   result := FilterGrayscale(bmp, rect(0,0,bmp.width,bmp.Height));
    1635 end;
    1636 
    1637 function FilterGrayscale(bmp: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap;
    1638 begin
    1639   result := bmp.NewBitmap(bmp.Width,bmp.Height);
    1640   FilterGrayscale(bmp,ABounds,result,nil);
    1641 end;
    1642 
    1643 function CreateGrayscaleTask(bmp: TBGRACustomBitmap; ABounds: TRect
    1644   ): TFilterTask;
    1645 begin
    1646   result := TGrayscaleTask.Create(bmp,ABounds);
    1647 end;
    1648 
    1649 { Filter contour compute a grayscale image, then for each pixel
    1650   calculates the difference with surrounding pixels (in intensity and alpha)
    1651   and draw black pixels when there is a difference }
    1652 function FilterContour(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    1653 var
    1654   yb, xb: int32or64;
    1655   c:      array[0..8] of TBGRAPixel;
    1656 
    1657   i, bmpWidth, bmpHeight: int32or64;
    1658   slope: byte;
    1659   sum:   int32or64;
    1660   tempPixel: TBGRAPixel;
    1661   pdest, psrcUp, psrc, psrcDown: PBGRAPixel;
    1662 
    1663   bounds: TRect;
    1664   gray:   TBGRACustomBitmap;
    1665 begin
    1666   bmpWidth  := bmp.Width;
    1667   bmpHeight := bmp.Height;
    1668   Result    := bmp.NewBitmap(bmpWidth, bmpHeight);
    1669   gray      := bmp.FilterGrayscale;
    1670 
    1671   bounds := rect(0, 0, bmp.Width, bmp.Height);
    1672   for yb := bounds.Top to bounds.Bottom - 1 do
    1673   begin
    1674     pdest := Result.scanline[yb] + bounds.Left;
    1675 
    1676     if yb > 0 then
    1677       psrcUp := gray.Scanline[yb - 1] + bounds.Left
    1678     else
    1679       psrcUp := nil;
    1680     psrc := gray.scanline[yb] + bounds.Left;
    1681     if yb < bmpHeight - 1 then
    1682       psrcDown := gray.scanline[yb + 1] + bounds.Left
    1683     else
    1684       psrcDown := nil;
    1685 
    1686     for xb := bounds.Left to bounds.Right - 1 do
    1687     begin
    1688       c[0] := psrc^;
    1689       if (xb = 0) then
    1690       begin
    1691         c[1] := c[0];
    1692         c[2] := c[0];
    1693         c[4] := c[0];
    1694       end
    1695       else
    1696       begin
    1697         if psrcUp <> nil then
    1698           c[1] := (psrcUp - 1)^
    1699         else
    1700           c[1] := c[0];
    1701         c[2] := (psrc - 1)^;
    1702         if psrcDown <> nil then
    1703           c[4] := (psrcDown - 1)^
    1704         else
    1705           c[4] := c[0];
    1706       end;
    1707       if psrcUp <> nil then
    1708       begin
    1709         c[3] := psrcUp^;
    1710         Inc(psrcUp);
    1711       end
    1712       else
    1713         c[3] := c[0];
    1714 
    1715       if (xb = bmpWidth - 1) then
    1716       begin
    1717         c[5] := c[0];
    1718         c[6] := c[0];
    1719         c[8] := c[0];
    1720       end
    1721       else
    1722       begin
    1723         if psrcDown <> nil then
    1724           c[5] := (psrcDown + 1)^
    1725         else
    1726           c[5] := c[0];
    1727         c[6] := (psrc + 1)^;
    1728         if psrcUp <> nil then
    1729           c[8] := psrcUp^
    1730         else //+1 before
    1731           c[8] := c[0];
    1732       end;
    1733       if psrcDown <> nil then
    1734       begin
    1735         c[7] := psrcDown^;
    1736         Inc(psrcDown);
    1737       end
    1738       else
    1739         c[7] := c[0];
    1740       Inc(psrc);
    1741 
    1742       sum := 0;
    1743       for i := 1 to 4 do
    1744         sum += abs(c[i].red - c[i + 4].red) + abs(c[i].alpha - c[i + 4].alpha);
    1745 
    1746       if sum > 255 then
    1747         slope := 255
    1748       else
    1749       if sum < 0 then
    1750         slope := 0
    1751       else
    1752         slope := sum;
    1753 
    1754       tempPixel.red := 255 - slope;
    1755       tempPixel.green := 255 - slope;
    1756       tempPixel.blue := 255 - slope;
    1757       tempPixel.alpha := 255;
    1758       pdest^ := tempPixel;
    1759       Inc(pdest);
    1760     end;
    1761   end;
    1762   Result.InvalidateBitmap;
    1763   gray.Free;
    1764 end;
    1765 
    1766 { Compute the distance for each pixel to the center of the bitmap,
    1767   calculate the corresponding angle with arcsin, use this angle
    1768   to determine a distance from the center in the source bitmap }
    1769 function FilterSphere(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    1770 var
    1771   cx, cy, x, y, len, fact: single;
    1772   xb, yb: int32or64;
    1773   mask:   TBGRACustomBitmap;
    1774 begin
    1775   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1776   cx     := bmp.Width / 2 - 0.5;
    1777   cy     := bmp.Height / 2 - 0.5;
    1778   for yb := 0 to Result.Height - 1 do
    1779     for xb := 0 to Result.Width - 1 do
    1780     begin
    1781       x   := (xb - cx) / (cx + 0.5);
    1782       y   := (yb - cy) / (cy + 0.5);
    1783       len := sqrt(sqr(x) + sqr(y));
    1784       if (len <= 1) then
    1785       begin
    1786         if (len > 0) then
    1787         begin
    1788           fact := 1 / len * arcsin(len) / (Pi / 2);
    1789           x    *= fact;
    1790           y    *= fact;
    1791         end;
    1792         Result.setpixel(xb, yb, bmp.Getpixel(x * cx + cx, y * cy + cy));
    1793       end;
    1794     end;
    1795   mask := bmp.NewBitmap(bmp.Width, bmp.Height);
    1796   Mask.Fill(BGRABlack);
    1797   Mask.FillEllipseAntialias(cx, cy, cx, cy, BGRAWhite);
    1798   Result.ApplyMask(mask);
    1799   Mask.Free;
    1800 end;
    1801 
    1802 { Applies twirl scanner. See TBGRATwirlScanner }
    1803 function FilterTwirl(bmp: TBGRACustomBitmap; ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap;
    1804 var twirl: TBGRATwirlScanner;
    1805 begin
    1806   twirl := TBGRATwirlScanner.Create(bmp,ACenter,ARadius,ATurn,AExponent);
    1807   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1808   result.FillRect(ABounds, twirl, dmSet);
    1809   twirl.free;
    1810 end;
    1811 
    1812 function FilterTwirl(bmp: TBGRACustomBitmap; ACenter: TPoint;
    1813   ARadius: Single; ATurn: Single; AExponent: Single): TBGRACustomBitmap;
    1814 begin
    1815   result := FilterTwirl(bmp,rect(0,0,bmp.Width,bmp.Height),ACenter,ARadius,ATurn,AExponent);
    1816 end;
    1817 
    1818 { Compute the distance for each pixel to the vertical axis of the bitmap,
    1819   calculate the corresponding angle with arcsin, use this angle
    1820   to determine a distance from the vertical axis in the source bitmap }
    1821 function FilterCylinder(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    1822 var
    1823   cx, cy, x, y, len, fact: single;
    1824   xb, yb: int32or64;
    1825 begin
    1826   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1827   cx     := bmp.Width / 2 - 0.5;
    1828   cy     := bmp.Height / 2 - 0.5;
    1829   for yb := 0 to Result.Height - 1 do
    1830     for xb := 0 to Result.Width - 1 do
    1831     begin
    1832       x   := (xb - cx) / (cx + 0.5);
    1833       y   := (yb - cy) / (cy + 0.5);
    1834       len := abs(x);
    1835       if (len <= 1) then
    1836       begin
    1837         if (len > 0) then
    1838         begin
    1839           fact := 1 / len * arcsin(len) / (Pi / 2);
    1840           x    *= fact;
    1841         end;
    1842         Result.setpixel(xb, yb, bmp.Getpixel(x * cx + cx, y * cy + cy));
    1843       end;
    1844     end;
    1845 end;
    1846 
    1847 function FilterPlane(bmp: TBGRACustomBitmap): TBGRACustomBitmap;
    1848 const resampleGap=0.6;
    1849 var
    1850   cy, x1, x2, y1, y2, z1, z2, h: single;
    1851   yb: int32or64;
    1852   resampledBmp: TBGRACustomBitmap;
    1853   resampledBmpWidth: int32or64;
    1854   resampledFactor,newResampleFactor: single;
    1855   sub,resampledSub: TBGRACustomBitmap;
    1856   partRect: TRect;
    1857   resampleSizeY : int32or64;
    1858 begin
    1859   resampledBmp := bmp.Resample(bmp.Width*2,bmp.Height*2,rmSimpleStretch);
    1860   resampledBmpWidth := resampledBmp.Width;
    1861   resampledFactor := 2;
    1862   Result := bmp.NewBitmap(bmp.Width, bmp.Height*2);
    1863   cy     := result.Height / 2 - 0.5;
    1864   h      := 1;
    1865   for yb := 0 to ((Result.Height-1) div 2) do
    1866   begin
    1867     y1 := (cy - (yb-0.5)) / (cy+0.5);
    1868     y2 := (cy - (yb+0.5)) / (cy+0.5);
    1869     if y2 <= 0 then continue;
    1870     z1 := h/y1;
    1871     z2 := h/y2;
    1872     newResampleFactor := 1/(z2-z1)*1.5;
    1873 
    1874     x1 := (z1+1)/2;
    1875     x2 := (z2+1)/2;
    1876     if newResampleFactor <= resampledFactor*resampleGap then
    1877     begin
    1878       resampledFactor := newResampleFactor;
    1879       if resampledBmp <> bmp then resampledBmp.Free;
    1880       if (x2-x1 >= 1) then resampleSizeY := 1 else
    1881         resampleSizeY := round(1+((x2-x1)-1)/(1/bmp.Height-1)*(bmp.Height-1));
    1882       resampledBmp := bmp.Resample(max(1,round(bmp.Width*resampledFactor)),resampleSizeY,rmSimpleStretch);
    1883       resampledBmpWidth := resampledBmp.Width;
    1884     end;
    1885 
    1886     partRect := Rect(round(-resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x1*resampledBmp.Height),
    1887        round(resampledBmpWidth/2*z1+resampledBmpWidth/2),floor(x2*resampledBmp.Height)+1);
    1888     if x2-x1 > 1 then
    1889     begin
    1890       partRect.Top := 0;
    1891       partRect.Bottom := 1;
    1892     end;
    1893     sub := resampledBmp.GetPart(partRect);
    1894     if sub <> nil then
    1895     begin
    1896       resampledSub := sub.Resample(bmp.Width,1,rmFineResample);
    1897       result.PutImage(0,yb,resampledSub,dmSet);
    1898       result.PutImage(0,Result.Height-1-yb,resampledSub,dmSet);
    1899       resampledSub.free;
    1900       sub.free;
    1901     end;
    1902   end;
    1903   if resampledBmp <> bmp then resampledBmp.Free;
    1904 
    1905   if result.Height <> bmp.Height then
    1906   begin
    1907     resampledBmp := result.Resample(bmp.Width,bmp.Height,rmSimpleStretch);
    1908     result.free;
    1909     result := resampledBmp;
    1910   end;
    1911 end;
    1912 
    1913 { For each component, sort values to get the median }
    1914 function FilterMedian(bmp: TBGRACustomBitmap;
    1915   Option: TMedianOption): TBGRACustomBitmap;
    1916 
    1917   function ComparePixLt(p1, p2: TBGRAPixel): boolean;
    1918   begin
    1919     if (p1.red + p1.green + p1.blue = p2.red + p2.green + p2.blue) then
    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)
    1923     else
    1924       Result := (p1.red + p1.green + p1.blue) < (p2.red + p2.green + p2.blue);
    1925   end;
    1926 
    1927 const
    1928   nbpix = 9;
    1929 var
    1930   yb, xb:    int32or64;
    1931   dx, dy, n, i, j, k: int32or64;
    1932   a_pixels:  array[0..nbpix - 1] of TBGRAPixel;
    1933   tempPixel, refPixel: TBGRAPixel;
    1934   tempValue: byte;
    1935   sumR, sumG, sumB, sumA, BGRAdiv, nbA: uint32or64;
    1936   tempAlpha: word;
    1937   bounds:    TRect;
    1938   pdest:     PBGRAPixel;
    1939 begin
    1940   Result := bmp.NewBitmap(bmp.Width, bmp.Height);
    1941 
    1942   bounds := bmp.GetImageBounds;
    1943   if (bounds.Right <= bounds.Left) or (bounds.Bottom <= Bounds.Top) then
    1944     exit;
    1945   bounds.Left   := max(0, bounds.Left - 1);
    1946   bounds.Top    := max(0, bounds.Top - 1);
    1947   bounds.Right  := min(bmp.Width, bounds.Right + 1);
    1948   bounds.Bottom := min(bmp.Height, bounds.Bottom + 1);
    1949 
    1950   for yb := bounds.Top to bounds.bottom - 1 do
    1951   begin
    1952     pdest := Result.scanline[yb] + bounds.left;
    1953     for xb := bounds.left to bounds.right - 1 do
    1954     begin
    1955       n := 0;
    1956       for dy := -1 to 1 do
    1957         for dx := -1 to 1 do
    1958         begin
    1959           a_pixels[n] := bmp.GetPixel(xb + dx, yb + dy);
    1960           if a_pixels[n].alpha = 0 then
    1961             a_pixels[n] := BGRAPixelTransparent;
    1962           Inc(n);
    1963         end;
    1964       for i := 1 to n - 1 do
    1965       begin
    1966         j := i;
    1967         while (j > 1) and (a_pixels[j].alpha < a_pixels[j - 1].alpha) do
    1968         begin
    1969           tempValue := a_pixels[j].alpha;
    1970           a_pixels[j].alpha := a_pixels[j - 1].alpha;
    1971           a_pixels[j - 1].alpha := tempValue;
    1972           Dec(j);
    1973         end;
    1974         j := i;
    1975         while (j > 1) and (a_pixels[j].red < a_pixels[j - 1].red) do
    1976         begin
    1977           tempValue := a_pixels[j].red;
    1978           a_pixels[j].red := a_pixels[j - 1].red;
    1979           a_pixels[j - 1].red := tempValue;
    1980           Dec(j);
    1981         end;
    1982         j := i;
    1983         while (j > 1) and (a_pixels[j].green < a_pixels[j - 1].green) do
    1984         begin
    1985           tempValue := a_pixels[j].green;
    1986           a_pixels[j].green := a_pixels[j - 1].green;
    1987           a_pixels[j - 1].green := tempValue;
    1988           Dec(j);
    1989         end;
    1990         j := i;
    1991         while (j > 1) and (a_pixels[j].blue < a_pixels[j - 1].blue) do
    1992         begin
    1993           tempValue := a_pixels[j].blue;
    1994           a_pixels[j].blue := a_pixels[j - 1].blue;
    1995           a_pixels[j - 1].blue := tempValue;
    1996           Dec(j);
    1997         end;
    1998       end;
    1999 
    2000       refPixel := a_pixels[n div 2];
    2001 
    2002       if option in [moLowSmooth, moMediumSmooth, moHighSmooth] then
    2003       begin
    2004         sumR    := 0;
    2005         sumG    := 0;
    2006         sumB    := 0;
    2007         sumA    := 0;
    2008         BGRAdiv := 0;
    2009         nbA     := 0;
    2010 
    2011         case option of
    2012           moHighSmooth, moMediumSmooth:
    2013           begin
    2014             j := 2;
    2015             k := 2;
    2016           end;
    2017           else
    2018           begin
    2019             j := 1;
    2020             k := 1;
    2021           end;
    2022         end;
    2023 
    2024          {$hints off}
    2025         for i := -k to j do
    2026         begin
    2027           tempPixel := a_pixels[n div 2 + i];
    2028           tempAlpha := tempPixel.alpha;
    2029           if (option = moMediumSmooth) and ((i = -k) or (i = j)) then
    2030             tempAlpha := tempAlpha div 2;
    2031 
    2032           sumR    += tempPixel.red * tempAlpha;
    2033           sumG    += tempPixel.green * tempAlpha;
    2034           sumB    += tempPixel.blue * tempAlpha;
    2035           BGRAdiv += tempAlpha;
    2036 
    2037           sumA += tempAlpha;
    2038           Inc(nbA);
    2039         end;
    2040          {$hints on}
    2041         if option = moMediumSmooth then
    2042           Dec(nbA);
    2043 
    2044         if (BGRAdiv = 0) then
    2045           refPixel := BGRAPixelTransparent
    2046         else
    2047         begin
    2048           refPixel.red   := round(sumR / BGRAdiv);
    2049           refPixel.green := round(sumG / BGRAdiv);
    2050           refPixel.blue  := round(sumB / BGRAdiv);
    2051           refPixel.alpha := round(sumA / nbA);
    2052         end;
    2053       end;
    2054 
    2055       pdest^ := refPixel;
    2056       Inc(pdest);
    2057     end;
    2058   end;
    2059 end;
    2060 
    2061 constructor TBoxBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
    2062   radius: integer);
    2063 begin
    2064   FSource := bmp;
    2065   FBounds := ABounds;
    2066   FRadius := radius;
    2067 end;
    2068 
    2069 procedure TBoxBlurTask.DoExecute;
    2070 type
    2071   TVertical = record red,green,blue,alpha,count: NativeUint; end;
    2072   PVertical = ^TVertical;
    2073 var
    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 
    2215 begin
    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;
    2231 end;
    2232 
    2233 constructor TGrayscaleTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect);
    2234 begin
    2235   FSource := bmp;
    2236   FBounds := ABounds;
    2237 end;
    2238 
    2239 procedure TGrayscaleTask.DoExecute;
    2240 begin
    2241   FilterGrayscale(FSource,FBounds,Destination,@GetShouldStop);
    2242 end;
    2243 
    2244 { TCustomBlurTask }
    2245 
    2246 constructor TCustomBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
    2247   AMask: TBGRACustomBitmap; AMaskIsThreadSafe: boolean);
    2248 begin
    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;
    2260 end;
    2261 
    2262 destructor TCustomBlurTask.Destroy;
    2263 begin
    2264   If FMaskOwned then FreeAndNil(FMask);
    2265   inherited Destroy;
    2266 end;
    2267 
    2268 procedure TCustomBlurTask.DoExecute;
    2269 begin
    2270   FilterBlur(FSource,FBounds,FMask,Destination,@GetShouldStop);
    2271 end;
    2272 
    2273 constructor TMotionBlurTask.Create(ABmp: TBGRACustomBitmap; ABounds: TRect;
    2274   ADistance, AAngle: single; AOriented: boolean);
    2275 begin
    2276   FSource := ABmp;
    2277   FBounds := ABounds;
    2278   FDistance := ADistance;
    2279   FAngle := AAngle;
    2280   FOriented:= AOriented;
    2281 end;
    2282 
    2283 procedure TMotionBlurTask.DoExecute;
    2284 begin
    2285   FilterBlurMotion(FSource,FBounds,FDistance,FAngle,FOriented,Destination,@GetShouldStop);
    2286 end;
    2287 
    2288 constructor TRadialPreciseBlurTask.Create(bmp: TBGRACustomBitmap;
    2289   ABounds: TRect; radius: single);
    2290 begin
    2291   FSource := bmp;
    2292   FBounds := ABounds;
    2293   FRadius := radius;
    2294 end;
    2295 
    2296 procedure TRadialPreciseBlurTask.DoExecute;
    2297 begin
    2298   FilterBlurRadialPrecise(FSource,FBounds,FRadius,Destination,@GetShouldStop);
    2299 end;
    2300 
    2301 { TRadialBlurTask }
    2302 
    2303 constructor TRadialBlurTask.Create(bmp: TBGRACustomBitmap; ABounds: TRect;
    2304   radius: integer; blurType: TRadialBlurType);
    2305 begin
    2306   FSource := bmp;
    2307   FBounds := ABounds;
    2308   FRadius := radius;
    2309   FBlurType:= blurType;
    2310 end;
    2311 
    2312 procedure TRadialBlurTask.DoExecute;
    2313 begin
    2314   FilterBlurRadial(FSource,FBounds,FRadius,FBlurType,Destination,@GetShouldStop);
    2315 end;
    2316 
    2317 { TFilterTask }
    2318 
    2319 function TFilterTask.GetShouldStop(ACurrentY: integer): boolean;
    2320 begin
    2321   FCurrentY:= ACurrentY;
    2322   if Assigned(FCheckShouldStop) then
    2323     result := FCheckShouldStop(ACurrentY)
    2324   else
    2325     result := false;
    2326 end;
    2327 
    2328 function TFilterTask.Execute: TBGRACustomBitmap;
    2329 var DestinationOwned: boolean;
    2330 begin
    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;
    2349 end;
    2350 
    2351 procedure TFilterTask.SetDestination(AValue: TBGRACustomBitmap);
    2352 begin
    2353   if FDestination <> nil then
    2354     raise exception.Create('Destination is already defined');
    2355   FDestination := AValue;
    2356 end;
    2357 
    2358962end.
    2359963
  • GraphicTest/Packages/bgrabitmap/bgrafreetype.pas

    r472 r494  
    1111  to draw text like TBGRABitmap.TextOut will use the chosen renderer.
    1212
    13   >> Note that you need to defined the default FreeType font collection
    14   >> using LazFreeTypeFontCollection unit.
     13  >> Note that you need to define the default FreeType font collection
     14  >> using EasyLazFreeType unit.
    1515
    1616  To set the effects, keep a variable containing
     
    2626interface
    2727
     28{$i bgrabitmap.inc}
     29
    2830uses
    29   Types, Classes, SysUtils, Graphics, BGRABitmapTypes, EasyLazFreeType, FPimage, BGRAText, BGRATextFX, BGRAPhongTypes, LCLVersion;
     31  Types, Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, EasyLazFreeType, FPimage,
     32  BGRACustomTextFX, BGRAPhongTypes;
    3033
    3134type
     
    5760    ShadowRadius: integer;
    5861    ShadowOffset: TPoint;
     62    ShadowQuality: TRadialBlurType;
    5963
    6064    OutlineColor: TBGRAPixel;
     
    101105    ShadowRadius: integer;
    102106    ShadowOffset: TPoint;
     107    ShadowQuality: TRadialBlurType;
    103108
    104109    OutlineColor: TBGRAPixel;
     
    110115    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload;
    111116    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
    112     function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRATextEffect;
     117    { If this code does not compile, you probably have an older version of Lazarus. To fix the problem,
     118      go into "bgrabitmap.inc" and comment the compiler directives }
     119    {$IFDEF BGRABITMAP_USE_LCL12}
     120    procedure DrawTextWordBreak(AText: string; AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
     121    procedure DrawTextRect(AText: string; AFont: TFreeTypeRenderableFont; X1,Y1,X2,Y2: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
     122    {$ENDIF}
     123    {$IFDEF BGRABITMAP_USE_LCL15}
     124    procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); override; overload;
     125    procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload;
     126    procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
     127    {$ENDIF}
     128    function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect;
    113129    destructor Destroy; override;
    114130  end;
     
    117133implementation
    118134
    119 uses LCLType, BGRABlend, Math;
     135uses BGRABlend, Math;
    120136
    121137{ TBGRAFreeTypeFontRenderer }
     
    133149  result.ShadowRadius := ShadowRadius;
    134150  result.ShadowVisible := ShadowVisible;
     151  result.ShadowQuality := ShadowQuality;
    135152  result.ClearTypeRGBOrder := FontQuality <> fqFineClearTypeBGR;
    136153  result.Destination := ASurface;
     
    159176procedure TBGRAFreeTypeFontRenderer.UpdateFont;
    160177var fts: TFreeTypeStyles;
     178  filename: string;
    161179begin
    162180  fts := [];
     
    164182  if fsItalic in FontStyle then fts += [ftsItalic];
    165183  try
    166     {$IF (lcl_fullversion>=1010000)}
    167     FFont.SetNameAndStyle(FontName,fts);
     184    filename := FontName;
     185    {$IFDEF BGRABITMAP_USE_LCL12}
     186    FFont.SetNameAndStyle(filename,fts);
    168187    {$ELSE}
    169     FFont.Name := FontName;
     188    FFont.Name := filename;
    170189    FFont.Style := fts;
    171190    {$ENDIF}
     
    202221  end;
    203222  FFont.Hinted := FontHinted;
    204   {$IF (lcl_fullversion>=1010000)}
    205   FFont.StrikeOutDecoration := fsStrikeOut in FontStyle;
    206   FFont.UnderlineDecoration := fsUnderline in FontStyle;
     223  {$IFDEF BGRABITMAP_USE_LCL12}
     224    FFont.StrikeOutDecoration := fsStrikeOut in FontStyle;
     225    FFont.UnderlineDecoration := fsUnderline in FontStyle;
    207226  {$ENDIF}
    208227end;
     
    220239  ShadowOffset := Point(5,5);
    221240  ShadowRadius := 5;
     241  ShadowQuality:= rbFast;
    222242end;
    223243
     
    303323  end;
    304324  case style.Layout of
    305   {$IF (lcl_fullversion>=1010000)}
    306   tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end;
     325  {$IFDEF BGRABITMAP_USE_LCL12}
     326    tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end;
    307327  {$ENDIF}
    308328  tlBottom: begin ARect.top := y; align += [ftaBottom]; end;
     
    310330  end;
    311331  try
    312     {$IF (lcl_fullversion>=1010000)}
    313     if style.Wordbreak then
    314       GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align)
    315     else
     332    {$IFDEF BGRABITMAP_USE_LCL12}
     333      if style.Wordbreak then
     334        GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align)
     335      else
    316336    {$ENDIF}
    317337    begin
     
    345365function TBGRAFreeTypeFontRenderer.TextSize(s: string): TSize;
    346366begin
     367  UpdateFont;
    347368  result.cx := round(FFont.TextWidth(s));
    348369  result.cy := round(FFont.LineFullHeight);
     
    457478  ClearTypeRGBOrder:= true;
    458479  ShaderActive := true;
     480  ShadowQuality:= rbFast;
    459481end;
    460482
    461483procedure TBGRAFreeTypeDrawer.DrawText(AText: string;
    462484  AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor);
    463 var fx: TBGRATextEffect;
     485var fx: TBGRACustomTextEffect;
    464486  procedure DoOutline;
    465487  begin
     
    476498  begin
    477499    fx := CreateTextEffect(AText, AFont);
     500    fx.ShadowQuality := ShadowQuality;
    478501    y -= AFont.Ascent;
    479502    if ShadowActuallyVisible then fx.DrawShadow(Destination, round(x+ShadowOffset.X),round(y+ShadowOffset.Y), ShadowRadius, ShadowColor);
     
    518541end;
    519542
     543{$IFDEF BGRABITMAP_USE_LCL12}
     544procedure TBGRAFreeTypeDrawer.DrawTextWordBreak(AText: string;
     545  AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel;
     546  AAlign: TFreeTypeAlignments);
     547begin
     548  DrawTextWordBreak(AText,AFont,x,y,AMaxWidth,BGRAToFPColor(AColor),AAlign);
     549end;
     550
     551procedure TBGRAFreeTypeDrawer.DrawTextRect(AText: string;
     552  AFont: TFreeTypeRenderableFont; X1, Y1, X2, Y2: Single; AColor: TBGRAPixel;
     553  AAlign: TFreeTypeAlignments);
     554begin
     555  DrawTextRect(AText,AFont,X1,Y1,X2,Y2,BGRAToFPColor(AColor),AAlign);
     556end;
     557{$ENDIF}
     558
     559{$IFDEF BGRABITMAP_USE_LCL15}
     560procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
     561  AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor);
     562var f: TFreeTypeFont;
     563begin
     564  if not (AFont is TFreeTypeFont) then exit;
     565  f := TFreeTypeFont(Afont);
     566  FColor := FPColorToBGRA(AColor);
     567  if AFont.ClearType then
     568    f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectlyClearType)
     569  else
     570    f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectly);
     571end;
     572
     573procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
     574  AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel);
     575begin
     576  DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor));
     577end;
     578
     579procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
     580  AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel;
     581  AAlign: TFreeTypeAlignments);
     582begin
     583  DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor), AAlign);
     584end;
     585{$ENDIF}
     586
    520587function TBGRAFreeTypeDrawer.CreateTextEffect(AText: string;
    521   AFont: TFreeTypeRenderableFont): TBGRATextEffect;
     588  AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect;
    522589var
    523590  mask: TBGRACustomBitmap;
     
    545612    AFont.ClearType := tempClearType;
    546613    mask.ConvertToLinearRGB;
    547     result := TBGRATextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert));
     614    result := TBGRACustomTextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert));
    548615  finally
    549616    FInCreateTextEffect:= false;
  • GraphicTest/Packages/bgrabitmap/bgragradients.pas

    r472 r494  
    22
    33{$mode objfpc}{$H+}
    4 
     4{$i bgrabitmap.inc}
    55{$i bgrasse.inc}
    66
     
    1010
    1111uses
    12   Classes, Graphics, BGRABitmapTypes, BGRABitmap, BGRABlend, BGRAPhongTypes, BGRASSE;
    13 
    14 { Creates a bitmap with the specified text horizontally centered and with a shadow }
     12  Classes, BGRAGraphics, BGRABitmapTypes, BGRABitmap, BGRABlend, BGRAPhongTypes, BGRASSE;
     13
     14{$IFDEF BGRABITMAP_USE_LCL}{ Creates a bitmap with the specified text horizontally centered and with a shadow }
    1515function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
    1616  AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True): TBGRABitmap;
     17{$ENDIF}
    1718
    1819{----------------------------------------------------------------------}
     
    176177implementation
    177178
    178 uses GraphType, Types, SysUtils, BGRATextFX; {GraphType unit used by phongdraw.inc}
    179 
    180 function TextShadow(AWidth, AHeight: Integer; AText: String;
     179uses Types, SysUtils{$IFDEF BGRABITMAP_USE_LCL}, BGRATextFX{$ENDIF}; {GraphType unit used by phongdraw.inc}
     180
     181{$IFDEF BGRABITMAP_USE_LCL}function TextShadow(AWidth, AHeight: Integer; AText: String;
    181182  AFontHeight: Integer; ATextColor, AShadowColor: TBGRAPixel; AOffSetX,
    182183  AOffSetY: Integer; ARadius: Integer; AFontStyle: TFontStyles;
     
    184185begin
    185186  result := BGRATextFX.TextShadow(AWidth,AHeight,AText,AFontHeight,ATextColor,AShadowColor,AOffsetX,AOffsetY,ARadius,AFontStyle,AFontName,AShowText) as TBGRABitmap;
    186 end;
     187end;{$ENDIF}
    187188
    188189function nGradientInfo(StartColor, StopColor: TBGRAPixel;
     
    671672  //antialiased border
    672673  mask := TBGRABitmap.Create(width,height,BGRABlack);
    673   mask.FillPolyAntialias([PointF(rx,-0.5),PointF(0,height-0.5),PointF(width-0.5,height-0.5)],BGRAWhite);
     674  mask.FillPolyAntialias([PointF(width/2,-0.5),PointF(0,height-0.5),PointF(width-0.5,height-0.5)],BGRAWhite);
    674675  result.ApplyMask(mask);
    675676  mask.Free;
  • GraphicTest/Packages/bgrabitmap/bgragradientscanner.pas

    r472 r494  
    1616  private
    1717    FColor1,FColor2: TBGRAPixel;
     18    ec1,ec2: TExpandedPixel;
    1819  public
    1920    constructor Create(Color1,Color2: TBGRAPixel);
    2021    function GetColorAt(position: integer): TBGRAPixel; override;
    2122    function GetColorAtF(position: single): TBGRAPixel; override;
     23    function GetExpandedColorAt(position: integer): TExpandedPixel; override;
     24    function GetExpandedColorAtF(position: single): TExpandedPixel; override;
    2225    function GetAverageColor: TBGRAPixel; override;
    2326    function GetMonochrome: boolean; override;
     
    3538    function GetColorAtF(position: single): TBGRAPixel; override;
    3639    function GetAverageColor: TBGRAPixel; override;
     40    function GetExpandedColorAt(position: integer): TExpandedPixel; override;
     41    function GetExpandedColorAtF(position: single): TExpandedPixel; override;
     42    function GetAverageExpandedColor: TExpandedPixel; override;
    3743    function GetMonochrome: boolean; override;
    3844  end;
     
    4652  private
    4753    FColor1,FColor2: TBGRAPixel;
     54    ec1,ec2: TExpandedPixel;
    4855    hsla1,hsla2: THSLAPixel;
    4956    hue1,hue2: longword;
    5057    FOptions: THueGradientOptions;
    5158    procedure Init(c1,c2: THSLAPixel; AOptions: THueGradientOptions);
     59    function GetColorNoBoundCheck(position: integer): THSLAPixel;
    5260  public
    5361    constructor Create(Color1,Color2: TBGRAPixel; options: THueGradientOptions); overload;
     
    5765    function GetColorAtF(position: single): TBGRAPixel; override;
    5866    function GetAverageColor: TBGRAPixel; override;
     67    function GetExpandedColorAt(position: integer): TExpandedPixel; override;
     68    function GetExpandedColorAtF(position: single): TExpandedPixel; override;
     69    function GetAverageExpandedColor: TExpandedPixel; override;
    5970    function GetMonochrome: boolean; override;
    6071  end;
     72
     73  TGradientInterpolationFunction = function(t: single): single of object;
    6174
    6275  { TBGRAMultiGradient }
     
    6982    FEColors: array of TExpandedPixel;
    7083    FCycle: Boolean;
     84    FInterpolationFunction: TGradientInterpolationFunction;
    7185    procedure Init(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection, ACycle: boolean);
    7286  public
    7387    GammaCorrection: boolean;
     88    function CosineInterpolation(t: single): single;
     89    function HalfCosineInterpolation(t: single): single;
    7490    constructor Create(Colors: array of TBGRAPixel; Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean = false);
    7591    function GetColorAt(position: integer): TBGRAPixel; override;
     92    function GetExpandedColorAt(position: integer): TExpandedPixel; override;
    7693    function GetAverageColor: TBGRAPixel; override;
    7794    function GetMonochrome: boolean; override;
     95    property InterpolationFunction: TGradientInterpolationFunction read FInterpolationFunction write FInterpolationFunction;
    7896  end;
    7997
     
    88106    len,aFactor,aFactorF: single;
    89107    mergedColor: TBGRAPixel;
     108    mergedExpandedColor: TExpandedPixel;
    90109    FGradient: TBGRACustomGradient;
    91110    FGradientOwner: boolean;
    92111    FHorizColor: TBGRAPixel;
     112    FHorizExpandedColor: TExpandedPixel;
    93113    FVertical: boolean;
    94114    FDotProduct,FDotProductPerp: Single;
     
    96116    procedure InitScanInline(x,y: integer);
    97117    function ScanNextInline: TBGRAPixel; inline;
     118    function ScanNextExpandedInline: TExpandedPixel; inline;
    98119  public
    99120    constructor Create(c1, c2: TBGRAPixel; gtype: TGradientType; o1, o2: TPointF;
     
    103124    procedure ScanMoveTo(X, Y: Integer); override;
    104125    function ScanNextPixel: TBGRAPixel; override;
     126    function ScanNextExpandedPixel: TExpandedPixel; override;
    105127    function ScanAt(X, Y: Single): TBGRAPixel; override;
     128    function ScanAtExpanded(X, Y: Single): TExpandedPixel; override;
    106129    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
    107130    function IsScanPutPixelsDefined: boolean; override;
     
    140163    function ScanAt(X,Y: Single): TBGRAPixel; override;
    141164    function ScanNextPixel: TBGRAPixel; override;
     165    function ScanNextExpandedPixel: TExpandedPixel; override;
    142166  end;
    143167
     
    151175    FScanNext : TScanNextPixelFunction;
    152176    FScanAt : TScanAtFunction;
     177    FMemMask: packed array of TBGRAPixel;
    153178  public
    154179    constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ASolidColor: TBGRAPixel);
     
    172197    FMaskScanAt,FTextureScanAt : TScanAtFunction;
    173198    FGlobalOpacity: Byte;
     199    FMemMask, FMemTex: packed array of TBGRAPixel;
    174200  public
    175201    constructor Create(AMask: TBGRACustomBitmap; AOffset: TPoint; ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255);
     
    190216      FScanNext : TScanNextPixelFunction;
    191217      FScanAt : TScanAtFunction;
     218      FMemTex: packed array of TBGRAPixel;
    192219  public
    193220    constructor Create(ATexture: IBGRAScanner; AGlobalOpacity: Byte = 255);
     
    202229implementation
    203230
    204 uses BGRABlend;
     231uses BGRABlend, Math;
    205232
    206233{ TBGRAConstantScanner }
     
    247274  FColor1 := HSLAToBGRA(c1);
    248275  FColor2 := HSLAToBGRA(c2);
     276  ec1 := GammaExpansion(FColor1);
     277  ec2 := GammaExpansion(FColor2);
    249278  FOptions:= AOptions;
    250279  if (hgoLightnessCorrection in AOptions) then
     
    276305end;
    277306
     307function TBGRAHueGradient.GetColorNoBoundCheck(position: integer): THSLAPixel;
     308var b,b2: LongWord;
     309begin
     310  b      := position shr 2;
     311  b2     := 16384-b;
     312  result.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff;
     313  result.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14;
     314  result.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14;
     315  result.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14;
     316  if hgoLightnessCorrection in FOptions then
     317  begin
     318    if not (hgoHueCorrection in FOptions) then
     319      result.hue := HtoG(result.hue);
     320  end else
     321  begin
     322    if hgoHueCorrection in FOptions then
     323      result.hue := GtoH(result.hue);
     324  end;
     325end;
     326
    278327constructor TBGRAHueGradient.Create(Color1, Color2: TBGRAPixel;options: THueGradientOptions);
    279328begin
     
    293342
    294343function TBGRAHueGradient.GetColorAt(position: integer): TBGRAPixel;
    295 var b,b2: cardinal;
    296     interm: THSLAPixel;
     344var interm: THSLAPixel;
    297345begin
    298346  if hgoRepeat in FOptions then
     
    317365    end;
    318366  end;
    319   b      := position shr 2;
    320   b2     := 16384-b;
    321   interm.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff;
    322   interm.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14;
    323   interm.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14;
    324   interm.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14;
     367  interm := GetColorNoBoundCheck(position);
    325368  if hgoLightnessCorrection in FOptions then
    326   begin
    327     if not (hgoHueCorrection in FOptions) then
    328       interm.hue := HtoG(interm.hue);
    329     result := GSBAToBGRA(interm);
    330   end else
    331   begin
    332     if hgoHueCorrection in FOptions then
    333       interm.hue := GtoH(interm.hue);
     369    result := GSBAToBGRA(interm)
     370  else
    334371    result := HSLAToBGRA(interm);
    335   end;
    336372end;
    337373
    338374function TBGRAHueGradient.GetColorAtF(position: single): TBGRAPixel;
    339 var b,b2: cardinal;
    340     interm: THSLAPixel;
     375var interm: THSLAPixel;
    341376begin
    342377  if hgoRepeat in FOptions then
     
    361396    end;
    362397  end;
    363   b      := round(position*16384);
    364   b2     := 16384-b;
    365   interm.hue := ((hue1 * b2 + hue2 * b + 8191) shr 14) and $ffff;
    366   interm.saturation := (hsla1.saturation * b2 + hsla2.saturation * b + 8191) shr 14;
    367   interm.lightness := (hsla1.lightness * b2 + hsla2.lightness * b + 8191) shr 14;
    368   interm.alpha := (hsla1.alpha * b2 + hsla2.alpha * b + 8191) shr 14;
     398  interm := GetColorNoBoundCheck(round(position*65536));
    369399  if hgoLightnessCorrection in FOptions then
    370   begin
    371     if not (hgoHueCorrection in FOptions) then
    372       interm.hue := HtoG(interm.hue);
    373     result := GSBAToBGRA(interm);
    374   end else
    375   begin
    376     if hgoHueCorrection in FOptions then
    377       interm.hue := GtoH(interm.hue);
     400    result := GSBAToBGRA(interm)
     401  else
    378402    result := HSLAToBGRA(interm);
    379   end;
    380403end;
    381404
     
    383406begin
    384407  Result:= GetColorAt(32768);
     408end;
     409
     410function TBGRAHueGradient.GetExpandedColorAt(position: integer): TExpandedPixel;
     411var interm: THSLAPixel;
     412begin
     413  if hgoRepeat in FOptions then
     414  begin
     415    position := position and $ffff;
     416    if position = 0 then
     417    begin
     418      result := ec1;
     419      exit;
     420    end;
     421  end else
     422  begin
     423    if position <= 0 then
     424    begin
     425      result := ec1;
     426      exit
     427    end else
     428    if position >= 65536 then
     429    begin
     430      result := ec2;
     431      exit
     432    end;
     433  end;
     434  interm := GetColorNoBoundCheck(position);
     435  if hgoLightnessCorrection in FOptions then
     436    result := GSBAToExpanded(interm)
     437  else
     438    result := HSLAToExpanded(interm);
     439end;
     440
     441function TBGRAHueGradient.GetExpandedColorAtF(position: single): TExpandedPixel;
     442var interm: THSLAPixel;
     443begin
     444  if hgoRepeat in FOptions then
     445  begin
     446    position := frac(position);
     447    if position = 0 then
     448    begin
     449      result := ec1;
     450      exit;
     451    end;
     452  end else
     453  begin
     454    if position <= 0 then
     455    begin
     456      result := ec1;
     457      exit;
     458    end else
     459    if position >= 1 then
     460    begin
     461      result := ec2;
     462      exit
     463    end;
     464  end;
     465  interm := GetColorNoBoundCheck(round(position*65536));
     466  if hgoLightnessCorrection in FOptions then
     467    result := GSBAToExpanded(interm)
     468  else
     469    result := HSLAToExpanded(interm);
     470end;
     471
     472function TBGRAHueGradient.GetAverageExpandedColor: TExpandedPixel;
     473begin
     474  Result:= GetExpandedColorAt(32768);
    385475end;
    386476
     
    417507end;
    418508
     509function TBGRAMultiGradient.CosineInterpolation(t: single): single;
     510begin
     511  result := (1-cos(t*Pi))*0.5;
     512end;
     513
     514function TBGRAMultiGradient.HalfCosineInterpolation(t: single): single;
     515begin
     516  result := (1-cos(t*Pi))*0.25 + t*0.5;
     517end;
     518
    419519constructor TBGRAMultiGradient.Create(Colors: array of TBGRAPixel;
    420520  Positions0To1: array of single; AGammaCorrection: boolean; ACycle: boolean);
     
    424524
    425525function TBGRAMultiGradient.GetColorAt(position: integer): TBGRAPixel;
    426 var i: integer;
     526var i: NativeInt;
    427527    ec: TExpandedPixel;
     528    curPos,posDiff: NativeInt;
    428529begin
    429530  if FCycle then
     
    435536  begin
    436537    i := 0;
    437     while (i < high(FPositions)) and (position > FPositions[i+1]) do
     538    while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do
    438539      inc(i);
    439540
    440     if Position = FPositions[i+1] then
    441       result := FColors[i+1]
     541    if Position = FPositions[i] then
     542      result := FColors[i]
    442543    else
    443     if GammaCorrection then
    444     begin
    445       ec.red := FEColors[i].red + (position-FPositions[i])*(FEColors[i+1].red-FEColors[i].red) div (FPositions[i+1]-FPositions[i]);
    446       ec.green := FEColors[i].green + (position-FPositions[i])*(FEColors[i+1].green-FEColors[i].green) div (FPositions[i+1]-FPositions[i]);
    447       ec.blue := FEColors[i].blue + (position-FPositions[i])*(FEColors[i+1].blue-FEColors[i].blue) div (FPositions[i+1]-FPositions[i]);
    448       ec.alpha := FEColors[i].alpha + (position-FPositions[i])*(FEColors[i+1].alpha-FEColors[i].alpha) div (FPositions[i+1]-FPositions[i]);
    449       result := GammaCompression(ec);
    450     end else
    451     begin
    452       result.red := FColors[i].red + (position-FPositions[i])*(FColors[i+1].red-FColors[i].red) div (FPositions[i+1]-FPositions[i]);
    453       result.green := FColors[i].green + (position-FPositions[i])*(FColors[i+1].green-FColors[i].green) div (FPositions[i+1]-FPositions[i]);
    454       result.blue := FColors[i].blue + (position-FPositions[i])*(FColors[i+1].blue-FColors[i].blue) div (FPositions[i+1]-FPositions[i]);
    455       result.alpha := FColors[i].alpha + (position-FPositions[i])*(FColors[i+1].alpha-FColors[i].alpha) div (FPositions[i+1]-FPositions[i]);
     544    begin
     545      curPos := position-FPositions[i];
     546      posDiff := FPositions[i+1]-FPositions[i];
     547      if FInterpolationFunction <> nil then
     548      begin
     549        curPos := round(FInterpolationFunction(curPos/posDiff)*65536);
     550        posDiff := 65536;
     551      end;
     552      if GammaCorrection then
     553      begin
     554        if FEColors[i+1].red < FEColors[i].red then
     555          ec.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else
     556          ec.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff);
     557        if FEColors[i+1].green < FEColors[i].green then
     558          ec.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else
     559          ec.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff);
     560        if FEColors[i+1].blue < FEColors[i].blue then
     561          ec.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else
     562          ec.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff);
     563        if FEColors[i+1].alpha < FEColors[i].alpha then
     564          ec.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else
     565          ec.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff);
     566        result := GammaCompression(ec);
     567      end else
     568      begin
     569        result.red := FColors[i].red + (curPos)*(FColors[i+1].red-FColors[i].red) div (posDiff);
     570        result.green := FColors[i].green + (curPos)*(FColors[i+1].green-FColors[i].green) div (posDiff);
     571        result.blue := FColors[i].blue + (curPos)*(FColors[i+1].blue-FColors[i].blue) div (posDiff);
     572        result.alpha := FColors[i].alpha + (curPos)*(FColors[i+1].alpha-FColors[i].alpha) div (posDiff);
     573      end;
     574    end;
     575  end;
     576end;
     577
     578function TBGRAMultiGradient.GetExpandedColorAt(position: integer
     579  ): TExpandedPixel;
     580var i: NativeInt;
     581    curPos,posDiff: NativeInt;
     582    rw,gw,bw: NativeUInt;
     583begin
     584  if FCycle then
     585    position := (position-FPositions[0]) mod (FPositions[high(FPositions)] - FPositions[0]) + FPositions[0];
     586  if position <= FPositions[0] then
     587    result := FEColors[0] else
     588  if position >= FPositions[high(FPositions)] then
     589    result := FEColors[high(FColors)] else
     590  begin
     591    i := 0;
     592    while (i < high(FPositions)-1) and (position >= FPositions[i+1]) do
     593      inc(i);
     594
     595    if Position = FPositions[i] then
     596      result := FEColors[i]
     597    else
     598    begin
     599      curPos := position-FPositions[i];
     600      posDiff := FPositions[i+1]-FPositions[i];
     601      if FInterpolationFunction <> nil then
     602      begin
     603        curPos := round(FInterpolationFunction(curPos/posDiff)*65536);
     604        posDiff := 65536;
     605      end;
     606      if GammaCorrection then
     607      begin
     608        if FEColors[i+1].red < FEColors[i].red then
     609          result.red := FEColors[i].red - NativeUInt(curPos)*NativeUInt(FEColors[i].red-FEColors[i+1].red) div NativeUInt(posDiff) else
     610          result.red := FEColors[i].red + NativeUInt(curPos)*NativeUInt(FEColors[i+1].red-FEColors[i].red) div NativeUInt(posDiff);
     611        if FEColors[i+1].green < FEColors[i].green then
     612          result.green := FEColors[i].green - NativeUInt(curPos)*NativeUInt(FEColors[i].green-FEColors[i+1].green) div NativeUInt(posDiff) else
     613          result.green := FEColors[i].green + NativeUInt(curPos)*NativeUInt(FEColors[i+1].green-FEColors[i].green) div NativeUInt(posDiff);
     614        if FEColors[i+1].blue < FEColors[i].blue then
     615          result.blue := FEColors[i].blue - NativeUInt(curPos)*NativeUInt(FEColors[i].blue-FEColors[i+1].blue) div NativeUInt(posDiff) else
     616          result.blue := FEColors[i].blue + NativeUInt(curPos)*NativeUInt(FEColors[i+1].blue-FEColors[i].blue) div NativeUInt(posDiff);
     617        if FEColors[i+1].alpha < FEColors[i].alpha then
     618          result.alpha := FEColors[i].alpha - NativeUInt(curPos)*NativeUInt(FEColors[i].alpha-FEColors[i+1].alpha) div NativeUInt(posDiff) else
     619          result.alpha := FEColors[i].alpha + NativeUInt(curPos)*NativeUInt(FEColors[i+1].alpha-FEColors[i].alpha) div NativeUInt(posDiff);
     620      end else
     621      begin
     622        rw := NativeInt(FColors[i].red shl 8) + (((curPos) shl 8)*(FColors[i+1].red-FColors[i].red)) div (posDiff);
     623        gw := NativeInt(FColors[i].green shl 8) + (((curPos) shl 8)*(FColors[i+1].green-FColors[i].green)) div (posDiff);
     624        bw := NativeInt(FColors[i].blue shl 8) + (((curPos) shl 8)*(FColors[i+1].blue-FColors[i].blue)) div (posDiff);
     625
     626        if rw >= $ff00 then result.red := $ffff
     627        else result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8;
     628        if gw >= $ff00 then result.green := $ffff
     629        else result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8;
     630        if bw >= $ff00 then result.blue := $ffff
     631        else result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8;
     632        result.alpha := NativeInt(FColors[i].alpha shl 8) + (((curPos) shl 8)*(FColors[i+1].alpha-FColors[i].alpha)) div (posDiff);
     633        result.alpha := result.alpha + (result.alpha shr 8);
     634      end;
    456635    end;
    457636  end;
     
    544723end;
    545724
     725function TBGRASimpleGradientWithGammaCorrection.GetExpandedColorAt(
     726  position: integer): TExpandedPixel;
     727var b,b2: cardinal;
     728begin
     729  if position <= 0 then
     730    result := ec1 else
     731  if position >= 65536 then
     732    result := ec2 else
     733  begin
     734    b      := position;
     735    b2     := 65536-b;
     736    result.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16;
     737    result.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16;
     738    result.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16;
     739    result.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16;
     740  end;
     741end;
     742
     743function TBGRASimpleGradientWithGammaCorrection.GetExpandedColorAtF(
     744  position: single): TExpandedPixel;
     745var b,b2: cardinal;
     746begin
     747  if position <= 0 then
     748    result := ec1 else
     749  if position >= 1 then
     750    result := ec2 else
     751  begin
     752    b      := round(position*65536);
     753    b2     := 65536-b;
     754    result.red := (ec1.red * b2 + ec2.red * b + 32767) shr 16;
     755    result.green := (ec1.green * b2 + ec2.green * b + 32767) shr 16;
     756    result.blue := (ec1.blue * b2 + ec2.blue * b + 32767) shr 16;
     757    result.alpha := (ec1.alpha * b2 + ec2.alpha * b + 32767) shr 16;
     758  end;
     759end;
     760
     761function TBGRASimpleGradientWithGammaCorrection.GetAverageExpandedColor: TExpandedPixel;
     762begin
     763  result := MergeBGRA(ec1,ec2);
     764end;
     765
    546766function TBGRASimpleGradientWithGammaCorrection.GetMonochrome: boolean;
    547767begin
     
    556776  FColor1 := Color1;
    557777  FColor2 := Color2;
     778  ec1 := GammaExpansion(Color1);
     779  ec2 := GammaExpansion(Color2);
    558780end;
    559781
     
    577799
    578800function TBGRASimpleGradientWithoutGammaCorrection.GetColorAtF(position: single): TBGRAPixel;
    579 var b,b2: cardinal;
    580801begin
    581802  if position <= 0 then
     
    583804  if position >= 1 then
    584805    result := FColor2 else
    585   begin
    586     b      := round(position*1024);
     806    result := GetColorAt(round(position*65536));
     807end;
     808
     809function TBGRASimpleGradientWithoutGammaCorrection.GetExpandedColorAt(
     810  position: integer): TExpandedPixel;
     811var b,b2: cardinal;
     812    rw,gw,bw: word;
     813begin
     814  if position <= 0 then
     815    result := ec1 else
     816  if position >= 65536 then
     817    result := ec2 else
     818  begin
     819    b      := position shr 6;
    587820    b2     := 1024-b;
    588     result.red  := (FColor1.red * b2 + FColor2.red * b + 511) shr 10;
    589     result.green := (FColor1.green * b2 + FColor2.green * b + 511) shr 10;
    590     result.blue := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 10;
    591     result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 10;
    592   end;
     821    rw  := (FColor1.red * b2 + FColor2.red * b + 511) shr 2;
     822    gw := (FColor1.green * b2 + FColor2.green * b + 511) shr 2;
     823    bw := (FColor1.blue * b2 + FColor2.blue * b + 511) shr 2;
     824
     825    result.red := (GammaExpansionTab[rw shr 8]*NativeUInt(255 - (rw and 255)) + GammaExpansionTab[(rw shr 8)+1]*NativeUInt(rw and 255)) shr 8;
     826    result.green := (GammaExpansionTab[gw shr 8]*NativeUInt(255 - (gw and 255)) + GammaExpansionTab[(gw shr 8)+1]*NativeUInt(gw and 255)) shr 8;
     827    result.blue := (GammaExpansionTab[bw shr 8]*NativeUInt(255 - (bw and 255)) + GammaExpansionTab[(bw shr 8)+1]*NativeUInt(bw and 255)) shr 8;
     828    result.alpha := (FColor1.alpha * b2 + FColor2.alpha * b + 511) shr 2;
     829  end;
     830end;
     831
     832function TBGRASimpleGradientWithoutGammaCorrection.GetExpandedColorAtF(
     833  position: single): TExpandedPixel;
     834begin
     835  if position <= 0 then
     836    result := ec1 else
     837  if position >= 1 then
     838    result := ec2 else
     839    result := GetExpandedColorAt(round(position*65536));
    593840end;
    594841
     
    675922end;
    676923
     924function TBGRAGradientTriangleScanner.ScanNextExpandedPixel: TExpandedPixel;
     925var r,g,b,a: int64;
     926begin
     927  r := round(FCurColor[1]);
     928  g := round(FCurColor[2]);
     929  b := round(FCurColor[3]);
     930  a := round(FCurColor[4]);
     931  if r > 65535 then r := 65535 else
     932  if r < 0 then r := 0;
     933  if g > 65535 then g := 65535 else
     934  if g < 0 then g := 0;
     935  if b > 65535 then b := 65535 else
     936  if b < 0 then b := 0;
     937  if a > 65535 then a := 65535 else
     938  if a < 0 then a := 0;
     939  result.red := r;
     940  result.green := g;
     941  result.blue := b;
     942  result.alpha := a;
     943  FCurColor += FStep;
     944end;
     945
    677946{ TBGRAGradientScanner }
    678947
     
    704973  FVertical := (((gtype =gtLinear) or (gtype=gtReflected)) and (o1.x=o2.x)) or FGradient.Monochrome;
    705974  mergedColor := FGradient.GetAverageColor;
     975  mergedExpandedColor := FGradient.GetAverageExpandedColor;
    706976end;
    707977
     
    7631033end;
    7641034
     1035function TBGRAGradientScanner.ScanNextExpandedInline: TExpandedPixel;
     1036var
     1037  a,a2: single;
     1038  ai: integer;
     1039begin
     1040  if FGradientType >= gtDiamond then
     1041  begin
     1042    if FGradientType = gtRadial then
     1043    begin
     1044      a := sqrt(sqr(FDotProduct) + sqr(FDotProductPerp));
     1045      FDotProduct += u.x;
     1046      FDotProductPerp += u.y;
     1047    end else
     1048    begin
     1049      a   := abs(FDotProduct);
     1050      a2  := abs(FDotProductPerp);
     1051      if a2 > a then a := a2;
     1052      FDotProduct += u.x;
     1053      FDotProductPerp += u.y;
     1054    end;
     1055  end else
     1056  if FGradientType = gtReflected then
     1057  begin
     1058    a := abs(FDotProduct);
     1059    FDotProduct += u.x;
     1060  end else
     1061  begin
     1062    a := FDotProduct;
     1063    FDotProduct += u.x;
     1064  end;
     1065
     1066  if FSinus then
     1067  begin
     1068    a *= aFactor;
     1069    if a <= low(int64) then
     1070      result := FGradient.GetAverageExpandedColor
     1071    else
     1072    if a >= high(int64) then
     1073      result := FGradient.GetAverageExpandedColor
     1074    else
     1075    begin
     1076      ai := Sin65536(round(a));
     1077      result := FGradient.GetExpandedColorAt(ai);
     1078    end;
     1079  end else
     1080    result := FGradient.GetExpandedColorAtF(a*aFactorF);
     1081end;
     1082
    7651083constructor TBGRAGradientScanner.Create(c1, c2: TBGRAPixel;
    7661084  gtype: TGradientType; o1, o2: TPointF; gammaColorCorrection: boolean;
     
    8141132  InitScanInline(X,Y);
    8151133  if FVertical then
     1134  begin
    8161135    FHorizColor := ScanNextInline;
     1136    FHorizExpandedColor := ScanNextExpandedInline;
     1137  end;
    8171138end;
    8181139
     
    8231144  else
    8241145    result := ScanNextInline;
     1146end;
     1147
     1148function TBGRAGradientScanner.ScanNextExpandedPixel: TExpandedPixel;
     1149begin
     1150  if FVertical then
     1151    result := FHorizExpandedColor
     1152  else
     1153    result := ScanNextExpandedInline;
    8251154end;
    8261155
     
    8531182  begin
    8541183    a := a*aFactor;
    855     if a <= low(int64) then
    856       result := FGradient.GetAverageColor
    857     else
    858     if a >= high(int64) then
    859       result := FGradient.GetAverageColor
     1184    if (a <= low(int64)) or (a >= high(int64)) then
     1185      result := mergedColor
    8601186    else
    8611187    begin
     
    8651191  end else
    8661192    result := FGradient.GetColorAtF(a*aFactorF);
     1193end;
     1194
     1195function TBGRAGradientScanner.ScanAtExpanded(X, Y: Single): TExpandedPixel;
     1196var p: TPointF;
     1197    a,a2: single;
     1198    ai: integer;
     1199begin
     1200  if len = 0 then
     1201  begin
     1202    result := mergedExpandedColor;
     1203    exit;
     1204  end;
     1205
     1206  p.x := X - FOrigin1.x;
     1207  p.y := Y - FOrigin1.y;
     1208  case FGradientType of
     1209    gtLinear:    a := p.x * u.x + p.y * u.y;
     1210    gtReflected: a := abs(p.x * u.x + p.y * u.y);
     1211    gtDiamond:
     1212        begin
     1213          a   := abs(p.x * u.x + p.y * u.y);
     1214          a2  := abs(p.x * u.y - p.y * u.x);
     1215          if a2 > a then a := a2;
     1216        end;
     1217    gtRadial:    a := sqrt(sqr(p.x * u.x + p.y * u.y) + sqr(p.x * u.y - p.y * u.x));
     1218  end;
     1219
     1220  if FSinus then
     1221  begin
     1222    a := a*aFactor;
     1223    if (a <= low(int64)) or (a >= high(int64)) then
     1224      result := mergedExpandedColor
     1225    else
     1226    begin
     1227      ai := Sin65536(round(a));
     1228      result := FGradient.GetExpandedColorAt(ai);
     1229    end;
     1230  end else
     1231    result := FGradient.GetExpandedColorAtF(a*aFactorF);
    8671232end;
    8681233
     
    9611326var c: TBGRAPixel;
    9621327    alpha: byte;
    963     MemMask, pmask, MemTex, ptex: pbgrapixel;
     1328    pmask, ptex: pbgrapixel;
    9641329
    9651330  function GetNext: TBGRAPixel; inline;
     
    9821347
    9831348begin
    984   getmem(MemMask, count*sizeof(TBGRAPixel));
    985   ScannerPutPixels(FMask,MemMask,count,dmSet);
    986   getmem(MemTex, count*sizeof(TBGRAPixel));
    987   ScannerPutPixels(FTexture,MemTex,count,dmSet);
    988 
    989   pmask := MemMask;
    990   ptex := MemTex;
     1349  if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count));
     1350  if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count));
     1351  ScannerPutPixels(FMask,@FMemMask[0],count,dmSet);
     1352  ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet);
     1353
     1354  pmask := @FMemMask[0];
     1355  ptex := @FMemTex[0];
    9911356
    9921357  if FGlobalOpacity <> 255 then
     
    10711436    end;
    10721437  end;
    1073 
    1074   freemem(MemMask);
    1075   freemem(MemTex);
    10761438end;
    10771439
     
    11251487var c: TBGRAPixel;
    11261488    alpha: byte;
    1127     MemMask, pmask: pbgrapixel;
     1489    pmask: pbgrapixel;
    11281490
    11291491  function GetNext: TBGRAPixel; inline;
     
    11361498
    11371499begin
    1138   getmem(MemMask, count*sizeof(TBGRAPixel));
    1139   ScannerPutPixels(FMask,MemMask,count,dmSet);
    1140 
    1141   pmask := MemMask;
     1500  if count > length(FMemMask) then setlength(FMemMask, max(length(FMemMask)*2,count));
     1501  ScannerPutPixels(FMask,@FMemMask[0],count,dmSet);
     1502
     1503  pmask := @FMemMask[0];
    11421504
    11431505  case mode of
     
    11791541      end;
    11801542  end;
    1181 
    1182   freemem(MemMask);
    11831543end;
    11841544
     
    12291589  mode: TDrawMode);
    12301590var c: TBGRAPixel;
    1231     MemTex, ptex: pbgrapixel;
     1591    ptex: pbgrapixel;
    12321592
    12331593  function GetNext: TBGRAPixel; inline;
     
    12391599
    12401600begin
    1241   getmem(MemTex, count*sizeof(TBGRAPixel));
    1242   ScannerPutPixels(FTexture,MemTex,count,dmSet);
    1243 
    1244   ptex := MemTex;
     1601  if count > length(FMemTex) then setlength(FMemTex, max(length(FMemTex)*2,count));
     1602  ScannerPutPixels(FTexture,@FMemTex[0],count,dmSet);
     1603
     1604  ptex := @FMemTex[0];
    12451605
    12461606  case mode of
     
    12821642      end;
    12831643  end;
    1284 
    1285   freemem(MemTex);
    12861644end;
    12871645
  • GraphicTest/Packages/bgrabitmap/bgragrayscalemask.pas

    r472 r494  
    3939procedure DownSamplePutImageGrayscale(source: TBGRACustomBitmap; dest: TGrayscaleMask; ADestRect: TRect);
    4040
     41procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,
     42  y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel;
     43  texture: IBGRAScanner; RGBOrder: boolean);
     44
    4145implementation
    4246
    43 uses GraphType, BGRABlend;
     47uses BGRABlend;
     48
     49procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,
     50  y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel;
     51  texture: IBGRAScanner; RGBOrder: boolean);
     52var delta: NativeInt;
     53begin
     54  delta := mask.Width;
     55  BGRABlend.BGRAFillClearTypeMaskPtr(dest,x,y,xThird,mask.ScanLine[0],1,delta,mask.Width,mask.Height,color,texture,RGBOrder);
     56end;
    4457
    4558{ TGrayscaleMask }
     
    7790    pdest := FData;
    7891    Case AChannel of
    79       cAlpha: ofs := 3;
    80       cRed: ofs := 2;
    81       cGreen: ofs := 1;
     92      cAlpha: ofs := TBGRAPixel_AlphaByteOffset;
     93      cRed: ofs := TBGRAPixel_RedByteOffset;
     94      cGreen: ofs := TBGRAPixel_GreenByteOffset;
    8295    else
    83       ofs := 0;
     96      ofs := TBGRAPixel_BlueByteOffset;
    8497    end;
    8598    for y := 0 to FHeight-1 do
  • GraphicTest/Packages/bgrabitmap/bgragtkbitmap.pas

    r472 r494  
    2828
    2929uses
    30   Classes, SysUtils, BGRADefaultBitmap, Graphics,
     30  Classes, SysUtils, BGRALCLBitmap, Graphics,
    3131  GraphType;
    3232
     
    3434  { TBGRAGtkBitmap }
    3535
    36   TBGRAGtkBitmap = class(TBGRADefaultBitmap)
     36  TBGRAGtkBitmap = class(TBGRALCLBitmap)
    3737  private
    3838    FPixBuf: Pointer;
    39 {    procedure SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;
    40       ACanvas: TCanvas; ARect: TRect);}
    4139    procedure DrawTransparent(ACanvas: TCanvas; Rect: TRect);
    4240    procedure DrawOpaque(ACanvas: TCanvas; Rect: TRect);
     
    5755implementation
    5856
    59 uses BGRABitmapTypes, LCLType,
     57uses BGRABitmapTypes, BGRADefaultBitmap, LCLType,
    6058  LCLIntf, IntfGraphics,
    6159  {$IFDEF LCLgtk2}
     
    7068type TGtkDeviceContext = TGtk2DeviceContext;
    7169{$ENDIF}
    72 
    73 {procedure TBGRAGtkBitmap.SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;
    74   ACanvas: TCanvas; ARect: TRect);
    75 var
    76   background, temp: TBGRACustomBitmap;
    77   w, h: integer;
    78 
    79 begin
    80   w := ARect.Right - ARect.Left;
    81   h := ARect.Bottom - ARect.Top;
    82   background := NewBitmap(w, h);
    83   background.GetImageFromCanvas(ACanvas, ARect.Left, ARect.Top);
    84   if (ABitmap.Width = w) and (ABitmap.Height = h) then
    85     background.PutImage(0, 0, ABitmap, dmDrawWithTransparency)
    86   else
    87   begin
    88     temp := ABitmap.Resample(w, h, rmSimpleStretch);
    89     background.PutImage(0, 0, temp, dmDrawWithTransparency);
    90     temp.Free;
    91   end;
    92   background.Draw(ACanvas, ARect.Left, ARect.Top, True);
    93   background.Free;
    94 end;}
    9570
    9671procedure TBGRAGtkBitmap.ReallocData;
     
    141116  end;
    142117
    143   SwapRedBlue;
     118  If not TBGRAPixel_RGBAOrder then SwapRedBlue;
    144119 
    145120  P := Rect.TopLeft;
     
    152127    GDK_RGB_DITHER_NORMAL,0,0);   
    153128
    154   SwapRedBlue;
     129  If not TBGRAPixel_RGBAOrder then SwapRedBlue;
    155130end;
    156131
     
    252227  LPtoDP(dest, pos, 1);
    253228  If ALineOrder = riloBottomToTop then VerticalFlip;
    254   SwapRedBlue;
     229  If not TBGRAPixel_RGBAOrder then SwapRedBlue;
    255230  gdk_draw_rgb_32_image(TGtkDeviceContext(dest).Drawable,
    256231    TGtkDeviceContext(Dest).GC, pos.x,pos.y,
    257232    AWidth,AHeight, GDK_RGB_DITHER_NORMAL,
    258233    AData, AWidth*sizeof(TBGRAPixel));
    259   SwapRedBlue;
     234  If not TBGRAPixel_RGBAOrder then SwapRedBlue;
    260235  If ALineOrder = riloBottomToTop then VerticalFlip;
    261236end;
     
    297272    TGtkDeviceContext(CanvasSource.Handle).Drawable,
    298273    nil, P.X,P.Y,0,0,Width,Height);
    299   SwapRedBlue;
     274  If not TBGRAPixel_RGBAOrder then SwapRedBlue;
    300275  InvalidateBitmap;
    301276end;
  • GraphicTest/Packages/bgrabitmap/bgralayers.pas

    r472 r494  
    66
    77uses
    8   Graphics, Classes, SysUtils, Types, BGRABitmapTypes, BGRABitmap;
     8  BGRAGraphics, Classes, SysUtils, Types, BGRABitmapTypes, BGRABitmap;
    99
    1010type
     
    209209implementation
    210210
    211 uses LCLProc;
     211uses BGRAUTF8;
    212212
    213213var
  • GraphicTest/Packages/bgrabitmap/bgramatrix3d.pas

    r472 r494  
    44
    55{$i bgrasse.inc}
    6 {$ifdef BGRASSE_AVAILABLE}
     6
     7{$ifdef CPUI386}
    78  {$asmmode intel}
    8 {$endif}
     9{$ENDIF}
     10{$ifdef cpux86_64}
     11  {$asmmode intel}
     12{$ENDIF}
    913
    1014interface
    1115
    1216uses
    13   BGRABitmapTypes, BGRASSE;
     17  BGRABitmapTypes, BGRASSE,
     18  BGRATransform;
    1419
    1520type
    1621  TMatrix3D = packed array[1..3,1..4] of single;
     22  TMatrix4D = packed array[1..4,1..4] of single;
    1723  TProjection3D = packed record
    1824    Zoom, Center: TPointF;
    1925  end;
     26  TComputeProjectionFunc = function(AViewCoord: TPoint3D_128): TPointF of object;
    2027
    2128operator*(const A: TMatrix3D; const M: TPoint3D): TPoint3D;
     
    3542function MatrixRotateZ(angle: single): TMatrix3D;
    3643
     44operator *(const A, B: TMatrix4D): TMatrix4D;
     45function MatrixIdentity4D: TMatrix4D;
     46function AffineMatrixToMatrix4D(AValue: TAffineMatrix): TMatrix4D;
     47
    3748{$IFDEF BGRASSE_AVAILABLE}
    3849procedure Matrix3D_SSE_Load(const A: TMatrix3D);
     
    4556implementation
    4657
    47 procedure multiplyVectInline(const A : TMatrix3D; const vx,vy,vz,vt: single; out outx,outy,outz: single);
     58procedure multiplyVect3(const A : TMatrix3D; const vx,vy,vz,vt: single; out outx,outy,outz: single);
    4859begin
    4960  outx := vx * A[1,1] + vy * A[1,2] + vz * A[1,3] + vt * A[1,4];
     
    5263end;
    5364
     65procedure multiplyVect4(const A : TMatrix4D; const vx,vy,vz,vt: single; out outx,outy,outz,outt: single);
     66begin
     67  outx := vx * A[1,1] + vy * A[1,2] + vz * A[1,3] + vt * A[1,4];
     68  outy := vx * A[2,1] + vy * A[2,2] + vz * A[2,3] + vt * A[2,4];
     69  outz := vx * A[3,1] + vy * A[3,2] + vz * A[3,3] + vt * A[3,4];
     70  outt := vx * A[4,1] + vy * A[4,2] + vz * A[4,3] + vt * A[4,4];
     71end;
     72
    5473operator*(const A: TMatrix3D; const M: TPoint3D): TPoint3D;
    5574begin
     
    5776  result.y := M.x * A[2,1] + M.y * A[2,2] + M.z * A[2,3] + A[2,4];
    5877  result.z := M.x * A[3,1] + M.y * A[3,2] + M.z * A[3,3] + A[3,4];
     78end;
     79
     80operator*(const A, B: TMatrix4D): TMatrix4D;
     81begin
     82  multiplyVect4(A, B[1,1],B[2,1],B[3,1],B[4,1], result[1,1],result[2,1],result[3,1],result[4,1]);
     83  multiplyVect4(A, B[1,2],B[2,2],B[3,2],B[4,2], result[1,2],result[2,2],result[3,2],result[4,2]);
     84  multiplyVect4(A, B[1,3],B[2,3],B[3,3],B[4,3], result[1,3],result[2,3],result[3,3],result[4,3]);
     85  multiplyVect4(A, B[1,4],B[2,4],B[3,4],B[4,4], result[1,4],result[2,4],result[3,4],result[4,4]);
     86end;
     87
     88function MatrixIdentity4D: TMatrix4D;
     89begin
     90  result[1,1] := 1;  result[2,1] := 0;  result[3,1] := 0; result[4,1] := 0;
     91  result[1,2] := 0;  result[2,2] := 1;  result[3,2] := 0; result[4,2] := 0;
     92  result[1,3] := 0;  result[2,3] := 0;  result[3,3] := 1; result[4,3] := 0;
     93  result[1,4] := 0;  result[2,4] := 0;  result[3,4] := 0; result[4,4] := 1;
     94end;
     95
     96function AffineMatrixToMatrix4D(AValue: TAffineMatrix): TMatrix4D;
     97begin
     98  result[1,1] := AValue[1,1];  result[2,1] := AValue[1,2];  result[3,1] := 0; result[4,1] := AValue[1,3];
     99  result[1,2] := AValue[2,1];  result[2,2] := AValue[2,2];  result[3,2] := 0; result[4,2] := AValue[2,3];
     100  result[1,3] := 0;            result[2,3] := 0;            result[3,3] := 1; result[4,3] := 0;
     101  result[1,4] := 0;            result[2,4] := 0;            result[3,4] := 0; result[4,4] := 1;
    59102end;
    60103
     
    391434
    392435operator*(constref A: TMatrix3D; var M: TPoint3D_128): TPoint3D_128;
    393 {$IFDEF CPUI386}var oldMt: single; {$ENDIF}
    394 begin
    395   {$IFDEF CPUI386}
     436{$IFDEF BGRASSE_AVAILABLE}var oldMt: single; {$ENDIF}
     437begin
     438  {$IFDEF BGRASSE_AVAILABLE}
    396439  if UseSSE then
    397440  begin
     
    490533function MultiplyVect3DWithoutTranslation(constref A: TMatrix3D; constref M: TPoint3D_128): TPoint3D_128;
    491534begin
    492   {$IFDEF CPUI386}
     535  {$IFDEF BGRASSE_AVAILABLE}
    493536  if UseSSE then
    494537  begin
     
    583626operator*(A,B: TMatrix3D): TMatrix3D;
    584627begin
    585   multiplyVectInline(A, B[1,1],B[2,1],B[3,1],0, result[1,1],result[2,1],result[3,1]);
    586   multiplyVectInline(A, B[1,2],B[2,2],B[3,2],0, result[1,2],result[2,2],result[3,2]);
    587   multiplyVectInline(A, B[1,3],B[2,3],B[3,3],0, result[1,3],result[2,3],result[3,3]);
    588   multiplyVectInline(A, B[1,4],B[2,4],B[3,4],1, result[1,4],result[2,4],result[3,4]);
     628  multiplyVect3(A, B[1,1],B[2,1],B[3,1],0, result[1,1],result[2,1],result[3,1]);
     629  multiplyVect3(A, B[1,2],B[2,2],B[3,2],0, result[1,2],result[2,2],result[3,2]);
     630  multiplyVect3(A, B[1,3],B[2,3],B[3,3],0, result[1,3],result[2,3],result[3,3]);
     631  multiplyVect3(A, B[1,4],B[2,4],B[3,4],1, result[1,4],result[2,4],result[3,4]);
    589632end;
    590633
  • GraphicTest/Packages/bgrabitmap/bgraopenraster.pas

    r472 r494  
    8484implementation
    8585
    86 uses Graphics, XMLRead, XMLWrite, FPReadPNG, dialogs, BGRABitmapTypes, zstream, lazutf8classes,
     86uses XMLRead, XMLWrite, FPReadPNG, BGRABitmapTypes, zstream, BGRAUTF8,
    8787  UnzipperExt;
    8888
     
    132132
    133133function TFPReaderOpenRaster.InternalCheck(Stream: TStream): boolean;
    134 var {%h-}magic: packed array[0..3] of byte;
     134var magic: packed array[0..3] of byte;
    135135  OldPos,BytesRead: Int64;
    136136  doc : TBGRAOpenRasterDocument;
     
    139139  if Stream=nil then exit;
    140140  oldPos := stream.Position;
    141   BytesRead := Stream.Read({%h-}magic,sizeof(magic));
     141  {$PUSH}{$HINTS OFF}
     142  BytesRead := Stream.Read(magic,sizeof(magic));
     143  {$POP}
    142144  stream.Position:= OldPos;
    143145  if BytesRead<>sizeof(magic) then exit;
     
    303305              BlendOperation[idx] := boOverlay else
    304306            if opstr = 'svg:soft-light' then
    305               BlendOperation[idx] := boSoftLight else
     307              BlendOperation[idx] := boSvgSoftLight else
    306308            if opstr = 'svg:hard-light' then
    307309              BlendOperation[idx] := boHardLight else
     
    320322            if opstr = 'krita:divide' then
    321323              BlendOperation[idx] := boDivide else
     324            if opstr = 'bgra:soft-light' then
     325              BlendOperation[idx] := boSoftLight else
    322326            if opstr = 'bgra:nice-glow' then
    323327              BlendOperation[idx] := boNiceGlow else
     
    331335              BlendOperation[idx] := boXor else
    332336            begin
    333               messagedlg('Unknown blend operation : ' + attr.NodeValue,mtInformation,[mbOk],0);
     337              //messagedlg('Unknown blend operation : ' + attr.NodeValue,mtInformation,[mbOk],0);
     338              BlendOperation[idx] := boTransparent;
    334339            end;
    335340          end;
     
    408413        boMultiply: strval := 'svg:multiply';
    409414        boOverlay, boDarkOverlay: strval := 'svg:overlay';
    410         boSoftLight: strval := 'svg:soft-light';
     415        boSoftLight: strval := 'bgra:soft-light';
    411416        boHardLight: strval := 'svg:hard-light';
    412417        boDifference,boLinearDifference: strval := 'svg:difference';
     
    420425        boLinearNegation,boNegation: strval := 'bgra:negation';
    421426        boXor: strval := 'bgra:xor';
     427        boSvgSoftLight: strval := 'svg:soft-light';
    422428        else strval := 'svg:src-over';
    423429      end;
  • GraphicTest/Packages/bgrabitmap/bgrapaintnet.pas

    r472 r494  
    8080implementation
    8181
    82 uses zstream, Math, graphtype, Graphics, lazutf8classes, FileUtil;
     82uses zstream, Math, BGRAUTF8;
    8383
    8484{$hints off}
     
    285285  Stream.Position:= Stream.Position + XmlHeaderSize;
    286286     {$hints off}
    287   stream.Read(CompressionFormat, sizeof(CompressionFormat));
     287  stream.ReadBuffer(CompressionFormat, sizeof(CompressionFormat));
    288288     {$hints on}
    289289  CompressionFormat := LEToN(CompressionFormat);
     
    327327    begin
    328328        {$hints off}
    329       LayerData[i].Read(b, 1);
     329      LayerData[i].ReadBuffer(b, 1);
    330330        {$hints on}
    331331      Result += IntToHex(b, 2) + ' ';
     
    418418    layerData[layer].Position := 0;
    419419    layerData[layer].Read(Result.Data^, LayerData[layer].Size);
     420    if TBGRAPixel_RGBAOrder then result.SwapRedBlue;
    420421    Result.InvalidateBitmap;
    421422
     
    476477begin
    477478  {$hints off}
    478   src.Read(CompressionFlag, 1);
     479  src.ReadBuffer(CompressionFlag, 1);
    479480  {$hints on}
    480481  if CompressionFlag = 1 then
  • GraphicTest/Packages/bgrabitmap/bgrapalette.pas

    r472 r494  
    2424
    2525type
     26  TBGRAIndexedPaletteEntry = packed record
     27    Color: TBGRAPixel;
     28    Index: UInt32;
     29  end;
     30  PBGRAIndexedPaletteEntry = ^TBGRAIndexedPaletteEntry;
    2631  TBGRAWeightedPaletteEntry = packed record
    2732    Color: TBGRAPixel;
     
    3136  ArrayOfWeightedColor = array of TBGRAWeightedPaletteEntry;
    3237
     38  TBGRAPixelComparer = function (p1,p2 : PBGRAPixel): boolean;
     39
    3340  { TBGRACustomPalette }
    3441
    3542  TBGRACustomPalette = class
     43  private
     44    function GetDominantColor: TBGRAPixel;
    3645  protected
    3746    function GetCount: integer; virtual; abstract;
     
    4453    procedure AssignTo(AImage: TFPCustomImage); overload;
    4554    procedure AssignTo(APalette: TFPPalette); overload;
     55    property DominantColor: TBGRAPixel read GetDominantColor;
    4656    property Count: integer read GetCount;
    4757    property Color[AIndex: integer]: TBGRAPixel read GetColorByIndex;
     
    8696  public
    8797    constructor Create(ABitmap: TBGRACustomBitmap); virtual; overload;
     98    constructor Create(APalette: TBGRACustomPalette); virtual; overload;
     99    constructor Create(AColors: ArrayOfTBGRAPixel); virtual; overload;
     100    constructor Create(AColors: ArrayOfWeightedColor); virtual; overload;
    88101    function AddColor(AValue: TBGRAPixel): boolean; virtual;
     102    procedure AddColors(ABitmap: TBGRACustomBitmap); virtual; overload;
     103    procedure AddColors(APalette: TBGRACustomPalette); virtual; overload;
    89104    function RemoveColor(AValue: TBGRAPixel): boolean; virtual;
    90105    procedure LoadFromFile(AFilenameUTF8: string); virtual;
     
    97112  end;
    98113
     114  { TBGRAIndexedPalette }
     115
     116  TBGRAIndexedPalette = class(TBGRAPalette)
     117  private
     118    FCurrentIndex: UInt32;
     119  protected
     120    procedure NeedArray; override;
     121    function CreateEntry(AColor: TBGRAPixel): PBGRAPixel; override;
     122    procedure FreeEntry(AEntry: PBGRAPixel); override;
     123  public
     124    function RemoveColor({%H-}AValue: TBGRAPixel): boolean; override;
     125    function IndexOfColor(AValue: TBGRAPixel): integer; override;
     126    procedure Clear; override;
     127  end;
     128
    99129  { TBGRAWeightedPalette }
    100130
     
    107137    procedure IncludePixel(PPixel: PBGRAPixel); override;
    108138  public
     139    constructor Create(AColors: ArrayOfWeightedColor); override;
    109140    function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
    110141    function IncColor(AValue: TBGRAPixel; out NewWeight: UInt32): boolean;
     
    127158  TBGRACustomApproxPalette = class(TBGRACustomPalette)
    128159  private
    129     function FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel;
     160    function FindNearestColorIgnoreAlpha(AValue: TBGRAPixel): TBGRAPixel; inline;
     161    function FindNearestColorIndexIgnoreAlpha(AValue: TBGRAPixel): integer; inline;
     162  protected
     163    function GetWeightByIndex({%H-}AIndex: Integer): UInt32; virtual;
    130164  public
    131165    function FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel; overload;
    132     function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; virtual; abstract;
    133     function FindNearestColorIndex(AValue: TBGRAPixel): integer; virtual; abstract;
    134   end;
     166    function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; virtual; abstract; overload;
     167    function FindNearestColorIndex(AValue: TBGRAPixel; AIgnoreAlpha: boolean): integer; overload;
     168    function FindNearestColorIndex(AValue: TBGRAPixel): integer; virtual; abstract; overload;
     169    property Weight[AIndex: Integer]: UInt32 read GetWeightByIndex;
     170  end;
     171
     172  { TBGRA16BitPalette }
     173
     174  TBGRA16BitPalette = class(TBGRACustomApproxPalette)
     175  protected
     176    function GetCount: integer; override;
     177    function GetColorByIndex(AIndex: integer): TBGRAPixel; override;
     178  public
     179    function ContainsColor(AValue: TBGRAPixel): boolean; override;
     180    function IndexOfColor(AValue: TBGRAPixel): integer; override;
     181    function GetAsArrayOfColor: ArrayOfTBGRAPixel; override;
     182    function GetAsArrayOfWeightedColor: ArrayOfWeightedColor; override;
     183    function FindNearestColor(AValue: TBGRAPixel): TBGRAPixel; override;
     184    function FindNearestColorIndex(AValue: TBGRAPixel): integer; override;
     185  end;
     186
     187  { TBGRACustomColorQuantizer }
     188
     189  TBGRACustomColorQuantizer = class
     190  protected
     191    function GetDominantColor: TBGRAPixel; virtual;
     192    function GetPalette: TBGRACustomApproxPalette; virtual; abstract;
     193    function GetSourceColor(AIndex: integer): TBGRAPixel; virtual; abstract;
     194    function GetSourceColorCount: Integer; virtual; abstract;
     195    function GetReductionColorCount: integer; virtual; abstract;
     196    procedure SetReductionColorCount(AValue: Integer); virtual; abstract;
     197  public
     198    constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean); virtual; abstract; overload;
     199    constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption); virtual; abstract; overload;
     200    constructor Create(APalette: TBGRACustomPalette; ASeparateAlphaChannel: boolean; AReductionColorCount: integer); virtual; abstract; overload;
     201    constructor Create(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption; AReductionColorCount: integer); virtual; abstract; overload;
     202    procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect); virtual; abstract; overload;
     203    procedure ApplyDitheringInplace(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap); overload;
     204    function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; ABounds: TRect): TBGRACustomBitmap; virtual; abstract; overload;
     205    function GetDitheredBitmap(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap; overload;
     206    procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string); overload;
     207    procedure SaveBitmapToFile(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AFilenameUTF8: string; AFormat: TBGRAImageFormat); overload;
     208    procedure SaveBitmapToStream(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; AStream: TStream; AFormat: TBGRAImageFormat); virtual; abstract;
     209    function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; overload;
     210    function GetDitheredBitmapIndexedData(ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): Pointer; overload;
     211    function GetDitheredBitmapIndexedData(ABitDepth: integer; AByteOrder: TRawImageByteOrder; AAlgorithm: TDitheringAlgorithm;
     212      ABitmap: TBGRACustomBitmap; out AScanlineSize: PtrInt): Pointer; virtual; abstract; overload;
     213    property SourceColorCount: Integer read GetSourceColorCount;
     214    property SourceColor[AIndex: integer]: TBGRAPixel read GetSourceColor;
     215    property ReductionColorCount: Integer read GetReductionColorCount write SetReductionColorCount;
     216    property ReducedPalette: TBGRACustomApproxPalette read GetPalette;
     217    property DominantColor: TBGRAPixel read GetDominantColor;
     218  end;
     219
     220  TBGRAColorQuantizerAny = class of TBGRACustomColorQuantizer;
     221
     222var
     223  BGRAColorQuantizerFactory: TBGRAColorQuantizerAny;
    135224
    136225function BGRARequiredBitDepth(ABitmap: TBGRACustomBitmap; AAlpha: TAlphaChannelPaletteOption): integer; overload;
     
    146235function BGRARegisteredPaletteFormatFilter(AAllSupportedDescription: string) : string;
    147236
     237procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex,
     238  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     239
     240procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex,
     241  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     242
     243procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
     244  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     245
     246procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
     247  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     248
    148249implementation
    149250
    150 uses lazutf8classes, bufstream;
     251uses BGRAUTF8, bufstream;
     252
     253function IsDWordGreater(p1, p2: PBGRAPixel): boolean;
     254begin
     255  result := DWord(p1^) > DWord(p2^);
     256end;
     257
     258const
     259  InsertionSortLimit = 10;
     260
     261procedure ArrayOfWeightedColor_InsertionSort(AColors: ArrayOfWeightedColor; AMinIndex,
     262  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     263var i,j,insertPos: NativeInt;
     264  compared: TBGRAWeightedPaletteEntry;
     265begin
     266  if AComparer = nil then AComparer := @IsDWordGreater;
     267  for i := AMinIndex+1 to AMaxIndex do
     268  begin
     269    insertPos := i;
     270    compared := AColors[i];
     271    while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1].Color,@compared.Color) do
     272      dec(insertPos);
     273    if insertPos <> i then
     274    begin
     275      for j := i downto insertPos+1 do
     276        AColors[j] := AColors[j-1];
     277      AColors[insertPos] := compared;
     278    end;
     279  end;
     280end;
     281
     282procedure ArrayOfWeightedColor_QuickSort(AColors: ArrayOfWeightedColor; AMinIndex,
     283  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     284var Pivot: TBGRAPixel;
     285  CurMin,CurMax,i : NativeInt;
     286
     287  procedure Swap(a,b: NativeInt);
     288  var Temp: TBGRAWeightedPaletteEntry;
     289  begin
     290    if a = b then exit;
     291    Temp := AColors[a];
     292    AColors[a] := AColors[b];
     293    AColors[b] := Temp;
     294  end;
     295begin
     296  if AComparer = nil then AComparer := @IsDWordGreater;
     297  if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then
     298  begin
     299    ArrayOfWeightedColor_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer);
     300    exit;
     301  end;
     302  Pivot := AColors[(AMinIndex+AMaxIndex) shr 1].Color;
     303  CurMin := AMinIndex;
     304  CurMax := AMaxIndex;
     305  i := CurMin;
     306  while i < CurMax do
     307  begin
     308    if AComparer(@AColors[i].Color, @Pivot) then
     309    begin
     310      Swap(i, CurMax);
     311      dec(CurMax);
     312    end else
     313    begin
     314      if AComparer(@Pivot, @AColors[i].Color) then
     315      begin
     316        Swap(i, CurMin);
     317        inc(CurMin);
     318      end;
     319      inc(i);
     320    end;
     321  end;
     322  if AComparer(@Pivot, @AColors[i].Color) then
     323  begin
     324    Swap(i, CurMin);
     325    inc(CurMin);
     326  end;
     327  if CurMin > AMinIndex then ArrayOfWeightedColor_QuickSort(AColors,AMinIndex,CurMin,AComparer);
     328  if CurMax < AMaxIndex then ArrayOfWeightedColor_QuickSort(AColors,CurMax,AMaxIndex,AComparer);
     329end;
     330
     331procedure ArrayOfTBGRAPixel_InsertionSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
     332  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     333var i,j,insertPos: NativeInt;
     334  compared: TBGRAPixel;
     335begin
     336  if AComparer = nil then AComparer := @IsDWordGreater;
     337  for i := AMinIndex+1 to AMaxIndex do
     338  begin
     339    insertPos := i;
     340    compared := AColors[i];
     341    while (insertPos > AMinIndex) and AComparer(@AColors[insertPos-1],@compared) do
     342      dec(insertPos);
     343    if insertPos <> i then
     344    begin
     345      for j := i downto insertPos+1 do
     346        AColors[j] := AColors[j-1];
     347      AColors[insertPos] := compared;
     348    end;
     349  end;
     350end;
     351
     352procedure ArrayOfTBGRAPixel_QuickSort(AColors: ArrayOfTBGRAPixel; AMinIndex,
     353  AMaxIndex: NativeInt; AComparer: TBGRAPixelComparer = nil);
     354var Pivot: TBGRAPixel;
     355  CurMin,CurMax,i : NativeInt;
     356
     357  procedure Swap(a,b: NativeInt);
     358  var Temp: TBGRAPixel;
     359  begin
     360    if a = b then exit;
     361    Temp := AColors[a];
     362    AColors[a] := AColors[b];
     363    AColors[b] := Temp;
     364  end;
     365begin
     366  if AComparer = nil then AComparer := @IsDWordGreater;
     367  if AMaxIndex-AMinIndex+1 <= InsertionSortLimit then
     368  begin
     369    ArrayOfTBGRAPixel_InsertionSort(AColors,AMinIndex,AMaxIndex,AComparer);
     370    exit;
     371  end;
     372  Pivot := AColors[(AMinIndex+AMaxIndex) shr 1];
     373  CurMin := AMinIndex;
     374  CurMax := AMaxIndex;
     375  i := CurMin;
     376  while i < CurMax do
     377  begin
     378    if AComparer(@AColors[i], @Pivot) then
     379    begin
     380      Swap(i, CurMax);
     381      dec(CurMax);
     382    end else
     383    begin
     384      if AComparer(@Pivot, @AColors[i]) then
     385      begin
     386        Swap(i, CurMin);
     387        inc(CurMin);
     388      end;
     389      inc(i);
     390    end;
     391  end;
     392  if AComparer(@Pivot, @AColors[i]) then
     393  begin
     394    Swap(i, CurMin);
     395    inc(CurMin);
     396  end;
     397  if CurMin > AMinIndex then ArrayOfTBGRAPixel_QuickSort(AColors,AMinIndex,CurMin,AComparer);
     398  if CurMax < AMaxIndex then ArrayOfTBGRAPixel_QuickSort(AColors,CurMax,AMaxIndex,AComparer);
     399end;
    151400
    152401{$i paletteformats.inc}
     
    233482end;
    234483
     484{ TBGRA16BitPalette }
     485
     486function TBGRA16BitPalette.GetCount: integer;
     487begin
     488  result := 65537;
     489end;
     490
     491function TBGRA16BitPalette.GetColorByIndex(AIndex: integer): TBGRAPixel;
     492begin
     493  if (AIndex >= 65536) or (AIndex < 0) then
     494    result := BGRAPixelTransparent
     495  else
     496    result := Color16BitToBGRA(AIndex);
     497end;
     498
     499function TBGRA16BitPalette.ContainsColor(AValue: TBGRAPixel): boolean;
     500begin
     501  if AValue.alpha = 0 then
     502    result := true
     503  else
     504    result := (AValue.alpha = 255) and (FindNearestColor(AValue)=AValue);
     505end;
     506
     507function TBGRA16BitPalette.IndexOfColor(AValue: TBGRAPixel): integer;
     508var idx: integer;
     509begin
     510  if AValue.Alpha = 0 then
     511    result := 65536
     512  else
     513  begin
     514    idx := BGRAToColor16Bit(AValue);
     515    if Color16BitToBGRA(idx)=AValue then
     516      result := idx
     517    else
     518      result := -1;
     519  end;
     520end;
     521
     522function TBGRA16BitPalette.GetAsArrayOfColor: ArrayOfTBGRAPixel;
     523begin
     524  result := nil;
     525  raise exception.Create('Palette too big');
     526end;
     527
     528function TBGRA16BitPalette.GetAsArrayOfWeightedColor: ArrayOfWeightedColor;
     529begin
     530  result := nil;
     531  raise exception.Create('Palette too big');
     532end;
     533
     534function TBGRA16BitPalette.FindNearestColor(AValue: TBGRAPixel): TBGRAPixel;
     535begin
     536  if AValue.alpha = 0 then result := BGRAPixelTransparent
     537  else
     538    result := GetColorByIndex(BGRAToColor16Bit(AValue));
     539end;
     540
     541function TBGRA16BitPalette.FindNearestColorIndex(AValue: TBGRAPixel): integer;
     542begin
     543  result := BGRAToColor16Bit(AValue);
     544end;
     545
     546{ TBGRAIndexedPalette }
     547
     548procedure TBGRAIndexedPalette.NeedArray;
     549var Node: TAvgLvlTreeNode;
     550  n: UInt32;
     551begin
     552  n := Count;
     553  if UInt32(length(FArray)) <> n then
     554  begin
     555    setLength(FArray,n);
     556    for Node in FTree do
     557    with PBGRAIndexedPaletteEntry(Node.Data)^ do
     558    begin
     559      if Index < n then //index is unsigned so always >= 0
     560        FArray[Index] := @Color;
     561    end;
     562  end;
     563end;
     564
     565function TBGRAIndexedPalette.CreateEntry(AColor: TBGRAPixel): PBGRAPixel;
     566begin
     567  result := PBGRAPixel(GetMem(sizeOf(TBGRAIndexedPaletteEntry)));
     568  result^ := AColor;
     569  PBGRAIndexedPaletteEntry(result)^.Index := FCurrentIndex;
     570  Inc(FCurrentIndex);
     571end;
     572
     573procedure TBGRAIndexedPalette.FreeEntry(AEntry: PBGRAPixel);
     574begin
     575  FreeMem(AEntry);
     576end;
     577
     578function TBGRAIndexedPalette.RemoveColor(AValue: TBGRAPixel): boolean;
     579begin
     580  Result:= false;
     581  raise exception.Create('It is not possible to remove a color from an indexed palette');
     582end;
     583
     584function TBGRAIndexedPalette.IndexOfColor(AValue: TBGRAPixel): integer;
     585Var Node: TAvgLvlTreeNode;
     586begin
     587  Node := FTree.Find(@AValue);
     588  if Assigned(Node) then
     589    result := PBGRAIndexedPaletteEntry(Node.Data)^.Index
     590  else
     591    result := -1;
     592end;
     593
     594procedure TBGRAIndexedPalette.Clear;
     595begin
     596  inherited Clear;
     597  FCurrentIndex := 0;
     598end;
     599
     600{ TBGRACustomColorQuantizer }
     601
     602function TBGRACustomColorQuantizer.GetDominantColor: TBGRAPixel;
     603begin
     604  result := ReducedPalette.DominantColor;
     605end;
     606
     607procedure TBGRACustomColorQuantizer.ApplyDitheringInplace(
     608  AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap);
     609begin
     610  ApplyDitheringInplace(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));
     611end;
     612
     613function TBGRACustomColorQuantizer.GetDitheredBitmap(
     614  AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap
     615  ): TBGRACustomBitmap;
     616begin
     617  result := GetDitheredBitmap(AAlgorithm, ABitmap, rect(0,0,ABitmap.Width,ABitmap.Height));
     618end;
     619
     620procedure TBGRACustomColorQuantizer.SaveBitmapToFile(
     621  AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
     622  AFilenameUTF8: string);
     623begin
     624  SaveBitmapToFile(AAlgorithm, ABitmap, AFilenameUTF8, SuggestImageFormat(AFilenameUTF8));
     625end;
     626
     627procedure TBGRACustomColorQuantizer.SaveBitmapToFile(
     628  AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
     629  AFilenameUTF8: string; AFormat: TBGRAImageFormat);
     630var
     631  stream: TFileStreamUTF8;
     632begin
     633   stream := TFileStreamUTF8.Create(AFilenameUTF8,fmCreate);
     634   try
     635     SaveBitmapToStream(AAlgorithm, ABitmap, stream, AFormat);
     636   finally
     637     stream.Free;
     638   end;
     639end;
     640
     641function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData(
     642  ABitDepth: integer; AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap;
     643  out AScanlineSize: PtrInt): Pointer;
     644begin
     645  result := GetDitheredBitmapIndexedData(ABitDepth,
     646  {$IFDEF ENDIAN_LITTLE}riboLSBFirst{$ELSE}riboMSBFirst{$endif},
     647  AAlgorithm, ABitmap, AScanlineSize);
     648end;
     649
     650function TBGRACustomColorQuantizer.GetDitheredBitmapIndexedData(
     651  ABitDepth: integer; AAlgorithm: TDitheringAlgorithm;
     652  ABitmap: TBGRACustomBitmap): Pointer;
     653var dummy: PtrInt;
     654begin
     655  result := GetDitheredBitmapIndexedData(ABitDepth, AAlgorithm, ABitmap, dummy);
     656end;
     657
    235658{ TBGRACustomPalette }
     659
     660function TBGRACustomPalette.GetDominantColor: TBGRAPixel;
     661var
     662  w: ArrayOfWeightedColor;
     663  i: Integer;
     664  maxWeight, totalWeight: UInt32;
     665begin
     666  result := BGRAWhite;
     667  maxWeight := 0;
     668  w := GetAsArrayOfWeightedColor;
     669  totalWeight:= 0;
     670  for i := 0 to high(w) do
     671    inc(totalWeight, w[i].Weight);
     672  for i := 0 to high(w) do
     673    if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).saturation > 16000) then
     674    begin
     675      maxWeight:= w[i].Weight;
     676      result := w[i].Color;
     677    end;
     678  if maxWeight > totalWeight div 20 then exit;
     679  for i := 0 to high(w) do
     680    if (w[i].Weight > maxWeight) and (BGRAToGSBA(w[i].Color).lightness < 56000) and (BGRAToGSBA(w[i].Color).lightness > 16000) then
     681    begin
     682      maxWeight:= w[i].Weight;
     683      result := w[i].Color;
     684    end;
     685  if maxWeight > 0 then exit;
     686  for i := 0 to high(w) do
     687    if (w[i].Weight > maxWeight) then
     688    begin
     689      maxWeight:= w[i].Weight;
     690      result := w[i].Color;
     691    end;
     692end;
    236693
    237694procedure TBGRACustomPalette.AssignTo(AImage: TFPCustomImage);
     
    265722end;
    266723
     724function TBGRACustomApproxPalette.FindNearestColorIndexIgnoreAlpha(
     725  AValue: TBGRAPixel): integer;
     726const AlphaMask : DWord = {$IFDEF ENDIAN_LITTLE}$ff000000{$ELSE}$000000ff{$endif};
     727begin
     728  if AValue.alpha = 0 then
     729    result := -1
     730  else
     731  begin
     732    result := FindNearestColorIndex(TBGRAPixel(DWord(AValue) or AlphaMask));
     733  end;
     734end;
     735
     736function TBGRACustomApproxPalette.GetWeightByIndex(AIndex: Integer): UInt32;
     737begin
     738  result := 1;
     739end;
     740
    267741function TBGRACustomApproxPalette.FindNearestColor(AValue: TBGRAPixel; AIgnoreAlpha: boolean): TBGRAPixel;
    268742begin
     
    273747end;
    274748
     749function TBGRACustomApproxPalette.FindNearestColorIndex(AValue: TBGRAPixel;
     750  AIgnoreAlpha: boolean): integer;
     751begin
     752  if AIgnoreAlpha then
     753    result := FindNearestColorIndexIgnoreAlpha(AValue)
     754  else
     755    result := FindNearestColorIndex(AValue);
     756end;
     757
    275758{ TBGRAWeightedPalette }
    276759
    277 function TBGRAWeightedPalette.GetWeightByIndex(AIndex: integer): UInt32;
     760function TBGRAWeightedPalette.GetWeightByIndex(AIndex: Integer): UInt32;
    278761begin
    279762  NeedArray;
     
    288771begin
    289772  IncColor(PPixel^,dummy);
     773end;
     774
     775constructor TBGRAWeightedPalette.Create(AColors: ArrayOfWeightedColor);
     776var
     777  i: Integer;
     778begin
     779  inherited Create;
     780  for i := 0 to high(AColors) do
     781    with AColors[i] do IncColor(Color,Weight);
    290782end;
    291783
     
    6451137end;
    6461138
     1139constructor TBGRAPalette.Create(APalette: TBGRACustomPalette);
     1140begin
     1141  inherited Create;
     1142  AddColors(APalette);
     1143end;
     1144
     1145constructor TBGRAPalette.Create(AColors: ArrayOfTBGRAPixel);
     1146var
     1147  i: Integer;
     1148begin
     1149  inherited Create;
     1150  for i := 0 to high(AColors) do
     1151    AddColor(AColors[i]);
     1152end;
     1153
     1154constructor TBGRAPalette.Create(AColors: ArrayOfWeightedColor);
     1155var
     1156  i: Integer;
     1157begin
     1158  inherited Create;
     1159  for i := 0 to high(AColors) do
     1160    AddColor(AColors[i].Color);
     1161end;
     1162
    6471163function TBGRAPalette.AddColor(AValue: TBGRAPixel): boolean;
    6481164Var Node: TAvgLvlTreeNode;
     
    6681184    AddLastColor(Entry);
    6691185  end;
     1186end;
     1187
     1188procedure TBGRAPalette.AddColors(ABitmap: TBGRACustomBitmap);
     1189var p: PBGRAPixel;
     1190  n: integer;
     1191begin
     1192  n := ABitmap.NbPixels;
     1193  p := ABitmap.Data;
     1194  while n > 0 do
     1195  begin
     1196    AddColor(p^);
     1197    inc(p);
     1198    dec(n);
     1199  end;
     1200end;
     1201
     1202procedure TBGRAPalette.AddColors(APalette: TBGRACustomPalette);
     1203var i: NativeInt;
     1204begin
     1205  for i := 0 to APalette.Count- 1 do
     1206    AddColor(APalette.Color[i]);
    6701207end;
    6711208
  • GraphicTest/Packages/bgrabitmap/bgrapath.pas

    r472 r494  
    44
    55interface
     6
     7//todo: tangent interpolation
    68
    79{ There are different conventions for angles.
     
    3941
    4042type
    41   TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath, peQuadraticBezierTo, peCubicBezierTo, peArc);
    42   PBGRAPathElementType = ^TBGRAPathElementType;
     43  TBGRAPathElementType = (peNone, peMoveTo, peLineTo, peCloseSubPath,
     44    peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline,
     45    peClosedSpline);
     46
     47  TBGRAPathDrawProc = procedure(const APoints: array of TPointF; AClosed: boolean; AData: Pointer) of object;
     48  TBGRAPathFillProc = procedure(const APoints: array of TPointF; AData: pointer) of object;
     49
     50  TBGRAPath = class;
     51
     52  { TBGRAPathCursor }
     53
     54  TBGRAPathCursor = class(TBGRACustomPathCursor)
     55  protected
     56    FPath: TBGRAPath;
     57    FDataPos: IntPtr;
     58    FAcceptedDeviation: single;
     59    FPathLength: single;
     60    FPathLengthComputed: boolean;
     61    FBounds: TRectF;
     62    FBoundsComputed: boolean;
     63    FArcPos: Single;
     64
     65    FStartCoordinate: TPointF;
     66    FEndCoordinate: TPointF;
     67    FLoopClosedShapes,FLoopPath: boolean;
     68
     69    FCurrentElementType: TBGRAPathElementType;
     70    FCurrentElement: Pointer;
     71    FCurrentElementArcPos,
     72    FCurrentElementArcPosScale: single;
     73    FCurrentElementStartCoord,
     74    FCurrentElementEndCoord: TPointF;
     75    FCurrentElementLength: single;
     76    FCurrentElementPoints: array of TPointF;
     77    FCurrentSegment: NativeInt;
     78    FCurrentSegmentPos: single;
     79    function GoToNextElement(ACanJump: boolean): boolean;
     80    function GoToPreviousElement(ACanJump: boolean): boolean;
     81    procedure MoveToEndOfElement;
     82    procedure MoveForwardInElement(ADistance: single);
     83    procedure MoveBackwardInElement(ADistance: single);
     84    function NeedPolygonalApprox: boolean;
     85    procedure OnPathFree; virtual;
     86
     87    function GetLoopClosedShapes: boolean; override;
     88    function GetLoopPath: boolean; override;
     89    function GetStartCoordinate: TPointF; override;
     90    procedure SetLoopClosedShapes(AValue: boolean); override;
     91    procedure SetLoopPath(AValue: boolean); override;
     92
     93    function GetArcPos: single; override;
     94    function GetCurrentTangent: TPointF; override;
     95    procedure SetArcPos(AValue: single); override;
     96    function GetBounds: TRectF; override;
     97    function GetPathLength: single; override;
     98    procedure PrepareCurrentElement; virtual;
     99    function GetCurrentCoord: TPointF; override;
     100    function GetPath: TBGRAPath; virtual;
     101  public
     102    constructor Create(APath: TBGRAPath; AAcceptedDeviation: single = 0.1);
     103    function MoveForward(ADistance: single; ACanJump: boolean = true): single; override;
     104    function MoveBackward(ADistance: single; ACanJump: boolean = true): single; override;
     105    destructor Destroy; override;
     106    property CurrentCoordinate: TPointF read GetCurrentCoord;
     107    property CurrentTangent: TPointF read GetCurrentTangent;
     108    property Position: single read GetArcPos write SetArcPos;
     109    property PathLength: single read GetPathLength;
     110    property Path: TBGRAPath read GetPath;
     111    property Bounds: TRectF read GetBounds;
     112    property StartCoordinate: TPointF read GetStartCoordinate;
     113    property LoopClosedShapes: boolean read GetLoopClosedShapes write SetLoopClosedShapes;
     114    property LoopPath: boolean read GetLoopPath write SetLoopPath;
     115    property AcceptedDeviation: single read FAcceptedDeviation;
     116  end;
    43117
    44118  { TBGRAPath }
    45119
    46120  TBGRAPath = class(IBGRAPath)
    47   private
    48     function GetSvgString: string;
    49     procedure SetSvgString(const AValue: string);
    50121  protected
    51     FData: pbyte;
    52     FDataSize: integer;
    53     FDataPos: integer;
    54     FLastElementType: TBGRAPathElementType;
    55     FLastCoord,
    56     FStartCoord: TPointF;
    57     FExpectedControlPoint: TPointF;
     122    FData: PByte;
     123    FDataCapacity: PtrInt;
     124    FDataPos: PtrInt;
     125    FLastSubPathElementType, FLastStoredElementType: TBGRAPathElementType;
     126    FLastMoveToDataPos: PtrInt;
     127    FLastCoord,FLastTransformedCoord,
     128    FSubPathStartCoord, FSubPathTransformedStartCoord: TPointF;
     129    FExpectedTransformedControlPoint: TPointF;
    58130    FMatrix: TAffineMatrix; //this matrix must have a base of vectors
    59131                            //orthogonal, of same length and with positive
    60132                            //orientation in order to preserve arcs
    61133    FScale,FAngleRadCW: single;
     134    FCursors: array of TBGRAPathCursor;
     135    FInternalDrawOffset: TPointF;
     136    procedure OnModify;
     137    procedure OnMatrixChange;
    62138    procedure NeedSpace(count: integer);
    63     procedure StoreCoord(const pt: TPointF);
    64     function ReadCoord: TPointF;
    65     procedure StoreElementType(value: TBGRAPathElementType);
    66     function ReadElementType: TBGRAPathElementType;
    67     function ReadArcDef: TArcDef;
    68     procedure RewindFloat;
     139    function AllocateElement(AElementType: TBGRAPathElementType;
     140  AExtraBytes: PtrInt = 0): Pointer;
    69141    procedure Init;
     142    procedure DoClear;
     143    function CheckElementType(AElementType: TBGRAPathElementType): boolean;
     144    function GoToNextElement(var APos: PtrInt): boolean;
     145    function GoToPreviousElement(var APos: PtrInt): boolean;
     146    function PeekNextElement(APos: PtrInt): TBGRAPathElementType;
     147    function GetElementStartCoord(APos: PtrInt): TPointF;
     148    function GetElementEndCoord(APos: PtrInt): TPointF;
     149    function GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single;
     150    procedure GetElementAt(APos: PtrInt;
     151      out AElementType: TBGRAPathElementType; out AElement: pointer);
     152    function GetSvgString: string; virtual;
     153    procedure SetSvgString(const AValue: string); virtual;
     154    procedure RegisterCursor(ACursor: TBGRAPathCursor);
     155    procedure UnregisterCursor(ACursor: TBGRAPathCursor);
     156    function SetLastCoord(ACoord: TPointF): TPointF; inline;
     157    procedure ClearLastCoord;
     158    procedure BezierCurveFromTransformed(tcp1, cp2, pt:TPointF);
     159    procedure QuadraticCurveFromTransformed(tcp, pt: TPointF);
     160    function LastCoordDefined: boolean; inline;
     161    function GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF;
     162    function getPoints: ArrayOfTPointF;
     163    function getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF;
     164    function getCursor: TBGRACustomPathCursor;
     165    procedure InternalDraw(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer);
     166    procedure BitmapDrawSubPathProc(const APoints: array of TPointF; AClosed: boolean; AData: pointer);
     167    function CorrectAcceptedDeviation(AAcceptedDeviation: single; const AMatrix: TAffineMatrix): single;
    70168  public
    71169    constructor Create; overload;
    72170    constructor Create(ASvgString: string); overload;
     171    constructor Create(const APoints: ArrayOfTPointF); overload;
     172    constructor Create(APath: IBGRAPath); overload;
    73173    destructor Destroy; override;
    74174    procedure beginPath;
     175    procedure beginSubPath;
    75176    procedure closePath;
    76177    procedure translate(x,y: single);
     
    85186    procedure moveTo(const pt: TPointF); overload;
    86187    procedure lineTo(const pt: TPointF); overload;
     188    procedure polyline(const pts: array of TPointF);
    87189    procedure polylineTo(const pts: array of TPointF);
     190    procedure polygon(const pts: array of TPointF);
    88191    procedure quadraticCurveTo(cpx,cpy,x,y: single); overload;
    89192    procedure quadraticCurveTo(const cp,pt: TPointF); overload;
    90193    procedure quadraticCurve(const curve: TQuadraticBezierCurve); overload;
     194    procedure quadraticCurve(p1,cp,p2: TPointF); overload;
    91195    procedure smoothQuadraticCurveTo(x,y: single); overload;
    92196    procedure smoothQuadraticCurveTo(const pt: TPointF); overload;
     
    94198    procedure bezierCurveTo(const cp1,cp2,pt: TPointF); overload;
    95199    procedure bezierCurve(const curve: TCubicBezierCurve); overload;
     200    procedure bezierCurve(p1,cp1,cp2,p2: TPointF); overload;
    96201    procedure smoothBezierCurveTo(cp2x,cp2y,x,y: single); overload;
    97202    procedure smoothBezierCurveTo(const cp2,pt: TPointF); overload;
     
    105210    procedure arcTo(const p1,p2: TPointF; radius: single); overload;
    106211    procedure arc(const arcDef: TArcDef); overload;
    107     procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;
     212    procedure arc(cx, cy, rx,ry: single; xAngleRadCW, startAngleRadCW, endAngleRadCW: single); overload;
    108213    procedure arc(cx, cy, rx,ry, xAngleRadCW, startAngleRadCW, endAngleRadCW: single; anticlockwise: boolean); overload;
    109214    procedure arcTo(rx,ry, xAngleRadCW: single; largeArc, anticlockwise: boolean; x,y:single);
     
    111216    procedure addPath(const AValue: string); overload;
    112217    procedure addPath(source: IBGRAPath); overload;
     218    procedure openedSpline(const pts: array of TPointF; style: TSplineStyle);
     219    procedure closedSpline(const pts: array of TPointF; style: TSplineStyle);
    113220    property SvgString: string read GetSvgString write SetSvgString;
     221    function ComputeLength(AAcceptedDeviation: single = 0.1): single;
     222    function ToPoints(AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     223    function ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     224    function IsEmpty: boolean;
     225    function GetBounds(AAcceptedDeviation: single = 0.1): TRectF;
     226    procedure SetPoints(const APoints: ArrayOfTPointF);
     227    procedure stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1);
     228    procedure stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1);
     229    procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1);
     230    procedure stroke(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1);
     231    procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single = 0.1);
     232    procedure stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single = 0.1);
     233    procedure stroke(ADrawProc: TBGRAPathDrawProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil);
     234    procedure fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1);
     235    procedure fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1);
     236    procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1);
     237    procedure fill(ABitmap: TBGRACustomBitmap; x,y: single; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1);
     238    procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; AColor: TBGRAPixel; AAcceptedDeviation: single = 0.1);
     239    procedure fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AAcceptedDeviation: single = 0.1);
     240    procedure fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix; AAcceptedDeviation: single = 0.1; AData: pointer = nil);
     241    function CreateCursor(AAcceptedDeviation: single = 0.1): TBGRAPathCursor;
     242    procedure Fit(ARect: TRectF; AAcceptedDeviation: single = 0.1);
     243    procedure FitInto(ADest: TBGRAPath; ARect: TRectF; AAcceptedDeviation: single = 0.1);
    114244  protected
    115245    function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     
    121251
    122252function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single;
    123 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload;
    124 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload;
    125 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; overload;
    126 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; overload;
    127 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle): ArrayOfTPointF;
    128 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25): ArrayOfTPointF;
     253function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     254function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     255function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     256function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     257function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
     258function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single = 0.25; AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
     259function ClosedSplineStartPoint(const points: array of TPointF; Style: TSplineStyle): TPointF;
    129260
    130261{ Compute points to draw an antialiased ellipse }
     
    147278uses Math, BGRAResample, SysUtils;
    148279
     280type
     281  TStrokeData = record
     282    Bitmap: TBGRACustomBitmap;
     283    Texture: IBGRAScanner;
     284    Color: TBGRAPixel;
     285    Width: Single;
     286  end;
     287
     288  PPathElementHeader = ^TPathElementHeader;
     289  TPathElementHeader = record
     290    ElementType: TBGRAPathElementType;
     291    PreviousElementType: TBGRAPathElementType;
     292  end;
     293  PMoveToElement = ^TMoveToElement;
     294  TMoveToElement = record
     295    StartCoordinate: TPointF;
     296    LoopDataPos: PtrInt; //if the path is closed
     297  end;
     298  PClosePathElement = ^TClosePathElement;
     299  TClosePathElement = type TMoveToElement;
     300  PQuadraticBezierToElement = ^TQuadraticBezierToElement;
     301  TQuadraticBezierToElement = record
     302    ControlPoint, Destination: TPointF;
     303  end;
     304  PCubicBezierToElement = ^TCubicBezierToElement;
     305  TCubicBezierToElement = record
     306    ControlPoint1, ControlPoint2, Destination: TPointF;
     307  end;
     308  PArcElement = ^TArcElement;
     309  TArcElement = TArcDef;
     310
     311  PSplineElement = ^TSplineElement;
     312  TSplineElement = record
     313    SplineStyle: TSplineStyle;
     314    NbControlPoints: integer;
     315  end;
     316
     317const
     318  PathElementSize : array[TBGRAPathElementType] of PtrInt =
     319  (0, Sizeof(TMoveToElement), Sizeof(TClosePathElement), sizeof(TPointF),
     320   sizeof(TQuadraticBezierToElement), sizeof(TCubicBezierToElement),
     321   sizeof(TArcElement), sizeof(TSplineElement)+sizeof(integer),
     322   sizeof(TSplineElement)+sizeof(integer));
     323
    149324function SplineVertexToSide(y0, y1, y2, y3: single; t: single): single;
    150325var
     
    160335end;
    161336
    162 function ComputeCurvePrecision(pt1, pt2, pt3, pt4: TPointF): integer;
     337function ComputeCurvePartPrecision(pt1, pt2, pt3, pt4: TPointF; AAcceptedDeviation: single = 0.1): integer;
    163338var
    164339  len: single;
     
    167342  len    := max(len, sqr(pt3.x - pt2.x) + sqr(pt3.y - pt2.y));
    168343  len    := max(len, sqr(pt3.x - pt4.x) + sqr(pt3.y - pt4.y));
    169   Result := round(sqrt(sqrt(len)) * 2);
     344  Result := round(sqrt(sqrt(len)/AAcceptedDeviation) * 0.9);
    170345  if Result<=0 then Result:=1;
    171346end;
    172347
    173 function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; overload;
    174 var
    175   t,f1,f2,f3,f4: single;
    176   i,nb: Integer;
    177 begin
    178   nb := ComputeCurvePrecision(curve.p1,curve.c1,curve.c2,curve.p2);
    179   if nb <= 1 then nb := 2;
    180   setlength(result,nb);
    181   result[0] := curve.p1;
    182   result[nb-1] := curve.p2;
    183   for i := 1 to nb-2 do
    184   begin
    185     t := i/(nb-1);
    186     f1 := (1-t);
    187     f2 := f1*f1;
    188     f1 *= f2;
    189     f2 *= t*3;
    190     f4 := t*t;
    191     f3 := f4*(1-t)*3;
    192     f4 *= t;
    193 
    194     result[i] := PointF(f1*curve.p1.x + f2*curve.c1.x +
    195                   f3*curve.c2.x + f4*curve.p2.x,
    196                   f1*curve.p1.y + f2*curve.c1.y +
    197                   f3*curve.c2.y + f4*curve.p2.y);
    198   end;
    199 end;
    200 
    201 function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload;
    202 var
    203   t,f1,f2,f3: single;
    204   i,nb: Integer;
    205 begin
    206   nb := ComputeCurvePrecision(curve.p1,curve.c,curve.c,curve.p2);
    207   if nb <= 1 then nb := 2;
    208   setlength(result,nb);
    209   result[0] := curve.p1;
    210   result[nb-1] := curve.p2;
    211   for i := 1 to nb-2 do
    212   begin
    213     t := i/(nb-1);
    214     f1 := (1-t);
    215     f3 := t;
    216     f2 := f1*f3*2;
    217     f1 *= f1;
    218     f3 *= f3;
    219     result[i] := PointF(f1*curve.p1.x + f2*curve.c.x + f3*curve.p2.x,
    220                   f1*curve.p1.y + f2*curve.c.y + f3*curve.p2.y);
    221   end;
    222 end;
    223 
    224 function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF;
     348function ComputeBezierCurve(const curve: TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     349begin
     350  result := curve.ToPoints(AAcceptedDeviation);
     351end;
     352
     353function ComputeBezierCurve(const curve: TQuadraticBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF; overload;
     354begin
     355  result := curve.ToPoints(AAcceptedDeviation);
     356end;
     357
     358function ComputeBezierSpline(const spline: array of TCubicBezierCurve; AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
    225359var
    226360  curves: array of array of TPointF;
     
    250384  setlength(curves, length(spline));
    251385  for i := 0 to high(spline) do
    252     curves[i] := ComputeBezierCurve(spline[i]);
     386    curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation);
    253387  nb := length(curves[0]);
    254388  lastPt := curves[0][high(curves[0])];
     
    271405end;
    272406
    273 function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve
    274   ): ArrayOfTPointF;
     407function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve;
     408  AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
    275409var
    276410  curves: array of array of TPointF;
     
    300434  setlength(curves, length(spline));
    301435  for i := 0 to high(spline) do
    302     curves[i] := ComputeBezierCurve(spline[i]);
     436    curves[i] := ComputeBezierCurve(spline[i],AAcceptedDeviation);
    303437  nb := length(curves[0]);
    304438  lastPt := curves[0][high(curves[0])];
     
    321455end;
    322456
    323 function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle): ArrayOfTPointF;
     457function ComputeClosedSpline(const points: array of TPointF; Style: TSplineStyle; AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
    324458var
    325459  i, j, nb, idx, pre: integer;
     
    344478    ptNext  := points[(i + 1) mod length(points)];
    345479    ptNext2 := points[(i + 2) mod length(points)];
    346     nb      += ComputeCurvePrecision(ptPrev2, ptPrev, ptNext, ptNext2);
     480    nb      += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation);
    347481  end;
    348482
    349483  kernel := CreateInterpolator(style);
    350484  setlength(Result, nb);
     485  idx := 0;
    351486  for i := 0 to high(points) do
    352487  begin
     
    355490    ptNext  := points[(i + 1) mod length(points)];
    356491    ptNext2 := points[(i + 2) mod length(points)];
    357     pre     := ComputeCurvePrecision(ptPrev2, ptPrev, ptNext, ptNext2);
     492    pre     := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation);
    358493    if i=0 then
    359     begin
    360       j := 0;
    361       idx := 0;
    362     end else j := 1;
     494      j := 0
     495    else
     496      j := 1;
    363497    while j <= pre do
    364498    begin
     
    373507end;
    374508
    375 function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single): ArrayOfTPointF;
     509function ComputeOpenedSpline(const points: array of TPointF; Style: TSplineStyle; EndCoeff: single; AAcceptedDeviation: single = 0.1): ArrayOfTPointF;
    376510var
    377511  i, j, nb, idx, pre: integer;
     
    403537    else
    404538      ptNext2 := points[i + 2];
    405     nb      += ComputeCurvePrecision(ptPrev2, ptPrev, ptNext, ptNext2);
     539    nb      += ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation);
    406540  end;
    407541
     
    430564    else
    431565      ptNext2 := points[i + 2];
    432     pre     := ComputeCurvePrecision(ptPrev2, ptPrev, ptNext, ptNext2);
     566    pre     := ComputeCurvePartPrecision(ptPrev2, ptPrev, ptNext, ptNext2, AAcceptedDeviation);
    433567    if i=0 then
    434568    begin
     
    447581  if Style in[ssInsideWithEnds,ssCrossingWithEnds] then
    448582    result[idx] := points[high(points)];
     583end;
     584
     585function ClosedSplineStartPoint(const points: array of TPointF;
     586  Style: TSplineStyle): TPointF;
     587var
     588  kernel: TWideKernelFilter;
     589  ptPrev2: TPointF;
     590  ptPrev: TPointF;
     591  ptNext: TPointF;
     592  ptNext2: TPointF;
     593begin
     594  if length(points) = 0 then
     595    result := EmptyPointF
     596  else
     597  if length(points)<=2 then
     598    result := points[0]
     599  else
     600  begin
     601    kernel := CreateInterpolator(style);
     602    ptPrev2 := points[high(points)];
     603    ptPrev  := points[0];
     604    ptNext  := points[1];
     605    ptNext2 := points[2];
     606    result := ptPrev2*kernel.Interpolation(1) + ptPrev*kernel.Interpolation(0) +
     607              ptNext*kernel.Interpolation(-1)  + ptNext2*kernel.Interpolation(-2);
     608    kernel.free;
     609  end;
    449610end;
    450611
     
    707868end;
    708869
     870{ TBGRAPathCursor }
     871
     872function TBGRAPathCursor.GetCurrentCoord: TPointF;
     873begin
     874  case FCurrentElementType of
     875    peNone: result := EmptyPointF;
     876    peMoveTo,peLineTo,peCloseSubPath:
     877      if FCurrentElementLength <= 0 then
     878        result := FCurrentElementStartCoord
     879      else
     880        result := FCurrentElementStartCoord + (FCurrentElementEndCoord-FCurrentElementStartCoord)*(FCurrentElementArcPos/FCurrentElementLength);
     881    peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline:
     882      begin
     883        NeedPolygonalApprox;
     884        if FCurrentSegment >= high(FCurrentElementPoints) then
     885          result := FCurrentElementEndCoord
     886        else
     887          result := FCurrentElementPoints[FCurrentSegment]+
     888          (FCurrentElementPoints[FCurrentSegment+1]-
     889           FCurrentElementPoints[FCurrentSegment])*FCurrentSegmentPos;
     890      end;
     891    else
     892      raise Exception.Create('Unknown element type');
     893  end;
     894end;
     895
     896function TBGRAPathCursor.GetPath: TBGRAPath;
     897begin
     898  if not Assigned(FPath) then
     899    raise exception.Create('Path does not exist');
     900  result := FPath;
     901end;
     902
     903procedure TBGRAPathCursor.MoveToEndOfElement;
     904begin
     905  FCurrentElementArcPos := FCurrentElementLength;
     906  if not NeedPolygonalApprox then exit;
     907  if length(FCurrentElementPoints) > 1 then
     908  begin
     909    FCurrentSegment := high(FCurrentElementPoints)-1;
     910    FCurrentSegmentPos := 1;
     911  end else
     912  begin
     913    FCurrentSegment := high(FCurrentElementPoints);
     914    FCurrentSegmentPos := 0;
     915  end;
     916end;
     917
     918procedure TBGRAPathCursor.MoveForwardInElement(ADistance: single);
     919var segLen,rightSpace,remaining: single;
     920begin
     921  if not NeedPolygonalApprox then exit;
     922  ADistance *= FCurrentElementArcPosScale;
     923  remaining := ADistance;
     924  while remaining > 0 do
     925  begin
     926    if FCurrentSegment < high(FCurrentElementPoints) then
     927      segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment])
     928    else
     929      segLen := 0;
     930    rightSpace := segLen*(1-FCurrentSegmentPos);
     931    if (segLen > 0) and (remaining <= rightSpace) then
     932    begin
     933      FCurrentSegmentPos += remaining/segLen;
     934      exit;
     935    end else
     936    begin
     937      remaining -= rightSpace;
     938      if FCurrentSegment < high(FCurrentElementPoints)-1 then
     939      begin
     940        inc(FCurrentSegment);
     941        FCurrentSegmentPos := 0;
     942      end else
     943      begin
     944        FCurrentSegmentPos := 1;
     945        exit;
     946      end;
     947    end;
     948  end;
     949end;
     950
     951procedure TBGRAPathCursor.MoveBackwardInElement(ADistance: single);
     952var
     953  segLen,leftSpace,remaining: Single;
     954begin
     955  if not NeedPolygonalApprox then exit;
     956  ADistance *= FCurrentElementArcPosScale;
     957  remaining := ADistance;
     958  while remaining > 0 do
     959  begin
     960    if FCurrentSegment < high(FCurrentElementPoints) then
     961      segLen := VectLen(FCurrentElementPoints[FCurrentSegment+1]-FCurrentElementPoints[FCurrentSegment])
     962    else
     963      segLen := 0;
     964    leftSpace := segLen*FCurrentSegmentPos;
     965    if (segLen > 0) and (remaining <= leftSpace) then
     966    begin
     967      FCurrentSegmentPos -= remaining/segLen;
     968      exit;
     969    end else
     970    begin
     971      remaining -= leftSpace;
     972      if FCurrentSegment > 0 then
     973      begin
     974        dec(FCurrentSegment);
     975        FCurrentSegmentPos := 1;
     976      end else
     977      begin
     978        FCurrentSegmentPos := 0;
     979        exit;
     980      end;
     981    end;
     982  end;
     983end;
     984
     985function TBGRAPathCursor.NeedPolygonalApprox: boolean;
     986begin
     987  if not (FCurrentElementType in[peQuadraticBezierTo,peCubicBezierTo,peArc,
     988  peOpenedSpline,peClosedSpline])
     989  then
     990  begin
     991    result := false;
     992    exit;
     993  end;
     994  result := true;
     995  if FCurrentElementPoints = nil then
     996  begin
     997    FCurrentElementPoints := Path.GetPolygonalApprox(FDataPos, FAcceptedDeviation, True);
     998    if FCurrentElementType = peQuadraticBezierTo then
     999    begin
     1000      if FCurrentElementLength <> 0 then
     1001        FCurrentElementArcPosScale := PolylineLen(FCurrentElementPoints)/FCurrentElementLength;
     1002    end;
     1003  end;
     1004end;
     1005
     1006function TBGRAPathCursor.GetArcPos: single;
     1007var pos: PtrInt;
     1008begin
     1009  if FArcPos = EmptySingle then
     1010  begin
     1011    FArcPos := FCurrentElementArcPos;
     1012    pos := FDataPos;
     1013    while Path.GoToPreviousElement(pos) do
     1014      FArcPos += Path.GetElementLength(pos, FAcceptedDeviation);
     1015  end;
     1016  result := FArcPos;
     1017end;
     1018
     1019function TBGRAPathCursor.GetCurrentTangent: TPointF;
     1020var idxStart,idxEnd: integer;
     1021  seg: TPointF;
     1022begin
     1023  while FCurrentElementLength <= 0 do
     1024  begin
     1025    if not GoToNextElement(False) then
     1026    begin
     1027      result := EmptyPointF;
     1028      exit;
     1029    end;
     1030  end;
     1031  case FCurrentElementType of
     1032    peMoveTo,peLineTo,peCloseSubPath:
     1033      result := (FCurrentElementEndCoord-FCurrentElementStartCoord)*(1/FCurrentElementLength);
     1034    peCubicBezierTo,peQuadraticBezierTo,peArc,peOpenedSpline,peClosedSpline:
     1035      begin
     1036        NeedPolygonalApprox;
     1037        idxStart := FCurrentSegment;
     1038        if idxStart >= high(FCurrentElementPoints) then
     1039          idxStart:= high(FCurrentElementPoints)-1;
     1040        idxEnd := idxStart+1;
     1041        if idxStart < 0 then
     1042        begin
     1043          result := EmptyPointF;
     1044          exit;
     1045        end;
     1046        seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart];
     1047        while (seg.x = 0) and (seg.y = 0) and (idxEnd < high(FCurrentElementPoints)) do
     1048        begin
     1049          inc(idxEnd);
     1050          seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart];
     1051        end;
     1052        while (seg.x = 0) and (seg.y = 0) and (idxStart > 0) do
     1053        begin
     1054          dec(idxStart);
     1055          seg := FCurrentElementPoints[idxEnd] - FCurrentElementPoints[idxStart];
     1056        end;
     1057        if (seg.x = 0) and (seg.y = 0) then
     1058          result := EmptyPointF
     1059        else
     1060          result := seg*(1/VectLen(seg));
     1061      end;
     1062    else result := EmptyPointF;
     1063  end;
     1064end;
     1065
     1066procedure TBGRAPathCursor.SetArcPos(AValue: single);
     1067var oldLoopClosedShapes,oldLoopPath: boolean;
     1068begin
     1069  if GetArcPos=AValue then Exit;
     1070  if (AValue > PathLength) and (PathLength <> 0) then
     1071    AValue := AValue - trunc(AValue/PathLength)*PathLength
     1072  else if (AValue < 0) then
     1073    AValue := AValue + (trunc(-AValue/PathLength)+1)*PathLength;
     1074  oldLoopClosedShapes:= LoopClosedShapes;
     1075  oldLoopPath:= LoopPath;
     1076  LoopClosedShapes:= false;
     1077  LoopPath:= false;
     1078  MoveForward(AValue-GetArcPos, True);
     1079  LoopClosedShapes:= oldLoopClosedShapes;
     1080  LoopPath:= oldLoopPath;
     1081end;
     1082
     1083function TBGRAPathCursor.GetPathLength: single;
     1084begin
     1085  if not FPathLengthComputed then
     1086  begin
     1087    FPathLength := Path.ComputeLength(FAcceptedDeviation);
     1088    FPathLengthComputed := true;
     1089  end;
     1090  result := FPathLength;
     1091end;
     1092
     1093procedure TBGRAPathCursor.OnPathFree;
     1094begin
     1095  FPath := nil;
     1096end;
     1097
     1098function TBGRAPathCursor.GetLoopClosedShapes: boolean;
     1099begin
     1100  result := FLoopClosedShapes;
     1101end;
     1102
     1103function TBGRAPathCursor.GetLoopPath: boolean;
     1104begin
     1105  result := FLoopPath;
     1106end;
     1107
     1108function TBGRAPathCursor.GetStartCoordinate: TPointF;
     1109begin
     1110  result := FStartCoordinate;
     1111end;
     1112
     1113procedure TBGRAPathCursor.SetLoopClosedShapes(AValue: boolean);
     1114begin
     1115  FLoopClosedShapes := AValue;
     1116end;
     1117
     1118procedure TBGRAPathCursor.SetLoopPath(AValue: boolean);
     1119begin
     1120  FLoopPath := AValue;
     1121end;
     1122
     1123procedure TBGRAPathCursor.PrepareCurrentElement;
     1124begin
     1125  Path.GetElementAt(FDataPos, FCurrentElementType, FCurrentElement);
     1126  FCurrentElementLength := 0;
     1127  FCurrentElementArcPos := 0;
     1128  FCurrentElementPoints := nil;
     1129  FCurrentSegment := 0;
     1130  FCurrentSegmentPos := 0;
     1131  FCurrentElementArcPosScale := 1;
     1132  if FCurrentElementType = peNone then
     1133  begin
     1134    FCurrentElementStartCoord := EmptyPointF;
     1135    FCurrentElementEndCoord := EmptyPointF;
     1136  end
     1137  else
     1138  begin
     1139    FCurrentElementStartCoord := Path.GetElementStartCoord(FDataPos);
     1140    case FCurrentElementType of
     1141      peLineTo, peCloseSubPath:
     1142        begin
     1143          FCurrentElementEndCoord := PPointF(FCurrentElement)^;
     1144          FCurrentElementLength := VectLen(FCurrentElementEndCoord - FCurrentElementStartCoord);
     1145        end;
     1146      peQuadraticBezierTo: with PQuadraticBezierToElement(FCurrentElement)^ do
     1147        begin
     1148          FCurrentElementEndCoord := Destination;
     1149          FCurrentElementLength := BGRABitmapTypes.BezierCurve(FCurrentElementStartCoord,ControlPoint,Destination).ComputeLength;
     1150        end;
     1151      peCubicBezierTo,peArc,peOpenedSpline,peClosedSpline:
     1152        begin
     1153          NeedPolygonalApprox;
     1154          FCurrentElementEndCoord := FCurrentElementPoints[high(FCurrentElementPoints)];
     1155          FCurrentElementLength := PolylineLen(FCurrentElementPoints);
     1156        end;
     1157    else
     1158      FCurrentElementEndCoord := FCurrentElementStartCoord;
     1159    end;
     1160  end;
     1161end;
     1162
     1163function TBGRAPathCursor.GetBounds: TRectF;
     1164begin
     1165  if not FBoundsComputed then
     1166  begin
     1167    FBounds:= Path.GetBounds(FAcceptedDeviation);
     1168    FBoundsComputed := true;
     1169  end;
     1170  result := FBounds;
     1171end;
     1172
     1173function TBGRAPathCursor.GoToNextElement(ACanJump: boolean): boolean;
     1174begin
     1175  if (FCurrentElementType = peCloseSubPath) and
     1176   (PClosePathElement(FCurrentElement)^.LoopDataPos <> -1) and
     1177   (  FLoopClosedShapes or
     1178      (FLoopPath and (PClosePathElement(FCurrentElement)^.LoopDataPos = 0))
     1179   ) then
     1180  begin
     1181    if PClosePathElement(FCurrentElement)^.LoopDataPos <> FDataPos then
     1182    begin
     1183      result := true;
     1184      FDataPos := PClosePathElement(FCurrentElement)^.LoopDataPos;
     1185      FArcPos := EmptySingle;
     1186      PrepareCurrentElement;
     1187    end else
     1188      result := false;
     1189  end;
     1190  if not ACanJump and ((FCurrentElementType = peCloseSubPath)
     1191   or (Path.PeekNextElement(FDataPos) = peMoveTo)) then
     1192  begin
     1193    result := false;
     1194    exit;
     1195  end;
     1196  if Path.GoToNextElement(FDataPos) then
     1197  begin
     1198    result := true;
     1199    PrepareCurrentElement;
     1200  end
     1201  else
     1202  begin
     1203    if ACanJump and FLoopPath and (FDataPos > 0) then
     1204    begin
     1205      result := true;
     1206      FDataPos := 0;
     1207      FArcPos := EmptySingle;
     1208      PrepareCurrentElement;
     1209    end else
     1210      result := false;
     1211  end;
     1212end;
     1213
     1214function TBGRAPathCursor.GoToPreviousElement(ACanJump: boolean): boolean;
     1215var lastElemPos: IntPtr;
     1216begin
     1217  if (FCurrentElementType = peMoveTo) and (PMoveToElement(FCurrentElement)^.LoopDataPos <> -1) and
     1218    ( FLoopClosedShapes or
     1219      (FLoopPath and (FDataPos = 0))
     1220    ) then
     1221  with PMoveToElement(FCurrentElement)^ do
     1222  begin
     1223    if LoopDataPos <> -1 then
     1224    begin
     1225      result := true;
     1226      FDataPos := LoopDataPos;
     1227      FArcPos := EmptySingle;
     1228      PrepareCurrentElement;
     1229    end;
     1230  end;
     1231  if not ACanJump and (FCurrentElementType = peMoveTo) then
     1232  begin
     1233    result := false;
     1234    exit;
     1235  end;
     1236  if Path.GoToPreviousElement(FDataPos) then
     1237  begin
     1238    result := true;
     1239    PrepareCurrentElement;
     1240  end
     1241  else
     1242  begin
     1243    if FLoopPath then
     1244    begin
     1245      lastElemPos := FPath.FDataPos;
     1246      if (lastElemPos > 0) and FPath.GoToPreviousElement(lastElemPos) then
     1247      begin
     1248        if lastElemPos > 0 then
     1249        begin
     1250          result := true;
     1251          FDataPos := lastElemPos;
     1252          PrepareCurrentElement;
     1253          FArcPos := EmptySingle;
     1254          exit;
     1255        end;
     1256      end;
     1257    end;
     1258    result := false;
     1259  end;
     1260end;
     1261
     1262constructor TBGRAPathCursor.Create(APath: TBGRAPath; AAcceptedDeviation: single);
     1263begin
     1264  FPath := APath;
     1265  FPathLengthComputed := false;
     1266  FBoundsComputed:= false;
     1267  FDataPos := 0;
     1268  FArcPos:= 0;
     1269  FAcceptedDeviation:= AAcceptedDeviation;
     1270  Path.RegisterCursor(self);
     1271  PrepareCurrentElement;
     1272
     1273  FStartCoordinate := FCurrentElementStartCoord;
     1274  if isEmptyPointF(FStartCoordinate) then
     1275    raise exception.Create('Path does not has a starting coordinate');
     1276  FEndCoordinate := Path.FLastTransformedCoord;
     1277  if isEmptyPointF(FEndCoordinate) then
     1278    raise exception.Create('Path does not has an ending coordinate');
     1279end;
     1280
     1281function TBGRAPathCursor.MoveForward(ADistance: single; ACanJump: boolean): single;
     1282var newArcPos,step,remaining: single;
     1283begin
     1284  if ADistance < 0 then
     1285  begin
     1286    result := -MoveBackward(-ADistance, ACanJump);
     1287    exit;
     1288  end;
     1289  result := 0;
     1290  remaining := ADistance;
     1291  while remaining > 0 do
     1292  begin
     1293    newArcPos := FCurrentElementArcPos + remaining;
     1294    if newArcPos > FCurrentElementLength then
     1295    begin
     1296      step := FCurrentElementLength - FCurrentElementArcPos;
     1297      result += step;
     1298      remaining -= step;
     1299      if not GoToNextElement(ACanJump) then
     1300      begin
     1301        MoveForwardInElement(step);
     1302        FCurrentElementArcPos := FCurrentElementLength;
     1303        FArcPos := PathLength;
     1304        exit;
     1305      end;
     1306    end else
     1307    begin
     1308      MoveForwardInElement(remaining);
     1309      FCurrentElementArcPos := newArcPos;
     1310      result := ADistance;
     1311      break;
     1312    end;
     1313  end;
     1314  if FArcPos <> EmptySingle then
     1315    FArcPos += result;
     1316end;
     1317
     1318function TBGRAPathCursor.MoveBackward(ADistance: single; ACanJump: boolean = true): single;
     1319var
     1320  remaining: Single;
     1321  newArcPos: Single;
     1322  step: Single;
     1323begin
     1324  if ADistance = 0 then
     1325  begin
     1326    result := 0;
     1327    exit;
     1328  end;
     1329  if ADistance < 0 then
     1330  begin
     1331    result := -MoveForward(-ADistance, ACanJump);
     1332    exit;
     1333  end;
     1334  result := 0;
     1335  remaining := ADistance;
     1336  while remaining > 0 do
     1337  begin
     1338    newArcPos := FCurrentElementArcPos - remaining;
     1339    if newArcPos < 0 then
     1340    begin
     1341      step := FCurrentElementArcPos;
     1342      result += step;
     1343      remaining -= step;
     1344      if not GoToPreviousElement(ACanJump) then
     1345      begin
     1346        MoveBackwardInElement(step);
     1347        FCurrentElementArcPos := 0;
     1348        FArcPos := 0;
     1349        exit;
     1350      end else
     1351        MoveToEndOfElement;
     1352    end else
     1353    begin
     1354      MoveBackwardInElement(remaining);
     1355      FCurrentElementArcPos := newArcPos;
     1356      result := ADistance;
     1357      break;
     1358    end;
     1359  end;
     1360  if FArcPos <> EmptySingle then
     1361    FArcPos -= result;
     1362end;
     1363
     1364destructor TBGRAPathCursor.Destroy;
     1365begin
     1366  if Assigned(FPath) then
     1367  begin
     1368    FPath.UnregisterCursor(self);
     1369  end;
     1370  inherited Destroy;
     1371end;
     1372
    7091373{ TBGRAPath }
     1374
     1375function TBGRAPath.ComputeLength(AAcceptedDeviation: single): single;
     1376var pos: PtrInt;
     1377begin
     1378  pos := 0;
     1379  result := 0;
     1380  repeat
     1381    result += GetElementLength(pos, AAcceptedDeviation);
     1382  until not GoToNextElement(pos);
     1383end;
     1384
     1385function TBGRAPath.ToPoints(AAcceptedDeviation: single): ArrayOfTPointF;
     1386var sub: array of ArrayOfTPointF;
     1387    temp: ArrayOfTPointF;
     1388    nbSub,nbPts,curPt,curSub: NativeInt;
     1389    startPos,pos: PtrInt;
     1390    elemType: TBGRAPathElementType;
     1391    elem: pointer;
     1392begin
     1393  pos := 0;
     1394  nbSub := 0;
     1395  repeat
     1396    GetElementAt(pos, elemType, elem);
     1397    if elem = nil then break;
     1398    case elemType of
     1399      peMoveTo,peLineTo,peCloseSubPath: begin
     1400          inc(nbSub);
     1401          while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1402            GoToNextElement(pos);
     1403        end;
     1404      peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub);
     1405    end;
     1406  until not GoToNextElement(pos);
     1407
     1408  pos := 0;
     1409  setlength(sub, nbSub);
     1410  curSub := 0;
     1411  repeat
     1412    GetElementAt(pos, elemType, elem);
     1413    if elem = nil then break;
     1414    case elemType of
     1415      peMoveTo,peLineTo,peCloseSubPath: begin
     1416          startPos := pos;
     1417          if (elemType = peMoveTo) and (curSub > 0) then
     1418            nbPts := 2
     1419          else
     1420            nbPts := 1;
     1421          while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1422          begin
     1423            GoToNextElement(pos);
     1424            inc(nbPts);
     1425          end;
     1426          setlength(temp, nbPts);
     1427          pos := startPos;
     1428          if (elemType = peMoveTo) and (curSub > 0) then
     1429          begin
     1430            temp[0] := EmptyPointF;
     1431            temp[1] := PPointF(elem)^;
     1432            curPt := 2;
     1433          end else
     1434          begin
     1435            temp[0] := PPointF(elem)^;
     1436            curPt := 1;
     1437          end;
     1438          while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1439          begin
     1440            GoToNextElement(pos);
     1441            GetElementAt(pos, elemType, elem);
     1442            temp[curPt] := PPointF(elem)^;
     1443            inc(curPt);
     1444          end;
     1445          sub[curSub] := temp;
     1446          inc(curSub);
     1447          temp := nil;
     1448        end;
     1449      peQuadraticBezierTo,peCubicBezierTo,peArc,
     1450      peOpenedSpline, peClosedSpline:
     1451        begin
     1452          sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False);
     1453          inc(curSub);
     1454        end;
     1455    end;
     1456  until not GoToNextElement(pos) or (curSub = nbSub);
     1457  result := ConcatPointsF(sub);
     1458end;
     1459
     1460function TBGRAPath.ToPoints(AMatrix: TAffineMatrix; AAcceptedDeviation: single): ArrayOfTPointF;
     1461begin
     1462  AAcceptedDeviation:= CorrectAcceptedDeviation(AAcceptedDeviation,AMatrix);
     1463  result := ToPoints(AAcceptedDeviation);
     1464  if not IsAffineMatrixIdentity(AMatrix) then
     1465    result := AMatrix*result;
     1466end;
     1467
     1468function TBGRAPath.IsEmpty: boolean;
     1469begin
     1470  result := FDataPos = 0;
     1471end;
     1472
     1473function TBGRAPath.GetBounds(AAcceptedDeviation: single): TRectF;
     1474var empty: boolean;
     1475    pos: PtrInt;
     1476    elemType: TBGRAPathElementType;
     1477    elem: pointer;
     1478    temp: array of TPointF;
     1479    i: integer;
     1480
     1481  procedure Include(pt: TPointF);
     1482  begin
     1483    if empty then
     1484    begin
     1485      result.TopLeft := pt;
     1486      result.BottomRight := pt;
     1487      empty := false;
     1488    end else
     1489    begin
     1490      if pt.x < result.Left then result.Left := pt.x
     1491      else if pt.x > result.Right then result.Right := pt.x;
     1492      if pt.y < result.Top then result.Top := pt.y
     1493      else if pt.y > result.Bottom then result.Bottom := pt.y;
     1494    end;
     1495  end;
     1496
     1497  procedure IncludeRect(r: TRectF);
     1498  begin
     1499    Include(r.TopLeft);
     1500    Include(r.BottomRight);
     1501  end;
     1502
     1503begin
     1504  empty := true;
     1505  result := RectF(0,0,0,0);
     1506  pos := 0;
     1507  repeat
     1508    GetElementAt(pos, elemType, elem);
     1509    if elem = nil then break;
     1510    case elemType of
     1511      peMoveTo,peLineTo,peCloseSubPath: begin
     1512          Include(PPointF(elem)^);
     1513          while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1514          begin
     1515            GoToNextElement(pos);
     1516            GetElementAt(pos, elemType, elem);
     1517            Include(PPointF(elem)^);
     1518          end;
     1519        end;
     1520      peCubicBezierTo:
     1521        with PCubicBezierToElement(elem)^ do
     1522          IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint1,ControlPoint2,Destination).GetBounds);
     1523      peQuadraticBezierTo:
     1524        with PQuadraticBezierToElement(elem)^ do
     1525          IncludeRect(BGRABitmapTypes.BezierCurve(GetElementStartCoord(pos),ControlPoint,Destination).GetBounds);
     1526      peArc, peOpenedSpline, peClosedSpline:
     1527        begin
     1528          temp := GetPolygonalApprox(pos, AAcceptedDeviation, False);
     1529          for i := 0 to high(temp) do
     1530            Include(temp[i]);
     1531        end;
     1532    end;
     1533  until not GoToNextElement(pos);
     1534  if empty then raise exception.Create('Path is empty');
     1535end;
     1536
     1537procedure TBGRAPath.SetPoints(const APoints: ArrayOfTPointF);
     1538var i: integer;
     1539    nextIsMoveTo: boolean;
     1540    startPoint: TPointF;
     1541begin
     1542  beginPath;
     1543  if length(APoints) = 0 then exit;
     1544  NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(APoints));
     1545  nextIsMoveTo:= true;
     1546  startPoint := EmptyPointF;
     1547  for i := 0 to high(APoints) do
     1548  begin
     1549    if isEmptyPointF(APoints[i]) then
     1550      nextIsMoveTo:= true
     1551    else
     1552    if nextIsMoveTo then
     1553    begin
     1554      startPoint := APoints[i];
     1555      moveTo(startPoint);
     1556      nextIsMoveTo:= false;
     1557    end
     1558    else
     1559    begin
     1560      with APoints[i] do
     1561        if (x = startPoint.x) and (y = startPoint.y) then
     1562          closePath
     1563        else
     1564          lineTo(APoints[i]);
     1565    end;
     1566  end;
     1567end;
     1568
     1569procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel;
     1570  AWidth: single; AAcceptedDeviation: single);
     1571begin
     1572  stroke(ABitmap,AffineMatrixIdentity,AColor,AWidth,AAcceptedDeviation);
     1573end;
     1574
     1575procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner;
     1576  AWidth: single; AAcceptedDeviation: single);
     1577begin
     1578  stroke(ABitmap,AffineMatrixIdentity,ATexture,AWidth,AAcceptedDeviation);
     1579end;
     1580
     1581procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single;
     1582  AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single);
     1583begin
     1584  stroke(ABitmap,AffineMatrixTranslation(x,y),AColor,AWidth,AAcceptedDeviation);
     1585end;
     1586
     1587procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; x, y: single;
     1588  ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single);
     1589begin
     1590  stroke(ABitmap,AffineMatrixTranslation(x,y),ATexture,AWidth,AAcceptedDeviation);
     1591end;
     1592
     1593procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix;
     1594  AColor: TBGRAPixel; AWidth: single; AAcceptedDeviation: single);
     1595var data: TStrokeData;
     1596begin
     1597  data.Bitmap := ABitmap;
     1598  data.Texture := nil;
     1599  data.Color := AColor;
     1600  data.Width := AWidth;
     1601  InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data);
     1602end;
     1603
     1604procedure TBGRAPath.stroke(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix;
     1605  ATexture: IBGRAScanner; AWidth: single; AAcceptedDeviation: single);
     1606var data: TStrokeData;
     1607begin
     1608  data.Bitmap := ABitmap;
     1609  data.Texture := ATexture;
     1610  data.Color := BGRAPixelTransparent;
     1611  data.Width := AWidth;
     1612  InternalDraw(@BitmapDrawSubPathProc, AMatrix, AAcceptedDeviation, @data);
     1613end;
     1614
     1615procedure TBGRAPath.stroke(ADrawProc: TBGRAPathDrawProc;
     1616  const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer);
     1617begin
     1618  InternalDraw(ADrawProc,AMatrix,AAcceptedDeviation,AData);
     1619end;
     1620
     1621procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; AColor: TBGRAPixel;
     1622  AAcceptedDeviation: single);
     1623begin
     1624  fill(ABitmap,AffineMatrixIdentity,AColor,AAcceptedDeviation);
     1625end;
     1626
     1627procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; ATexture: IBGRAScanner;
     1628  AAcceptedDeviation: single);
     1629begin
     1630  fill(ABitmap,AffineMatrixIdentity,ATexture,AAcceptedDeviation);
     1631end;
     1632
     1633procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single;
     1634  AColor: TBGRAPixel; AAcceptedDeviation: single);
     1635begin
     1636  fill(ABitmap,AffineMatrixTranslation(x,y),AColor,AAcceptedDeviation);
     1637end;
     1638
     1639procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; x, y: single;
     1640  ATexture: IBGRAScanner; AAcceptedDeviation: single);
     1641begin
     1642  fill(ABitmap,AffineMatrixTranslation(x,y),ATexture,AAcceptedDeviation);
     1643end;
     1644
     1645procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix;
     1646  AColor: TBGRAPixel; AAcceptedDeviation: single);
     1647begin
     1648  ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), AColor);
     1649end;
     1650
     1651procedure TBGRAPath.fill(ABitmap: TBGRACustomBitmap; const AMatrix: TAffineMatrix;
     1652  ATexture: IBGRAScanner; AAcceptedDeviation: single);
     1653begin
     1654  ABitmap.FillPolyAntialias(ToPoints(AMatrix,AAcceptedDeviation), ATexture);
     1655end;
     1656
     1657procedure TBGRAPath.fill(AFillProc: TBGRAPathFillProc; const AMatrix: TAffineMatrix;
     1658  AAcceptedDeviation: single; AData: pointer);
     1659begin
     1660  AFillProc(ToPoints(AMatrix,AAcceptedDeviation), AData);
     1661end;
     1662
     1663function TBGRAPath.CreateCursor(AAcceptedDeviation: single): TBGRAPathCursor;
     1664begin
     1665  result := TBGRAPathCursor.Create(self, AAcceptedDeviation);
     1666end;
     1667
     1668procedure TBGRAPath.Fit(ARect: TRectF; AAcceptedDeviation: single);
     1669var
     1670  temp: TBGRAPath;
     1671begin
     1672  temp := TBGRAPath.Create;
     1673  copyTo(temp);
     1674  temp.FitInto(self, ARect, AAcceptedDeviation);
     1675  temp.Free;
     1676end;
     1677
     1678procedure TBGRAPath.FitInto(ADest: TBGRAPath; ARect: TRectF;
     1679  AAcceptedDeviation: single);
     1680var bounds: TRectF;
     1681    zoomX,zoomY: single;
     1682begin
     1683  bounds := GetBounds(AAcceptedDeviation);
     1684  ADest.beginPath;
     1685  ADest.translate((ARect.Left+ARect.Right)*0.5, (ARect.Bottom+ARect.Top)*0.5);
     1686  if bounds.Right-bounds.Left <> 0 then
     1687  begin
     1688    zoomX := (ARect.Right-ARect.Left)/(bounds.Right-bounds.Left);
     1689    if bounds.Bottom-bounds.Top > 0 then
     1690    begin
     1691      zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top);
     1692      if zoomY < zoomX then ADest.scale(zoomY) else ADest.scale(zoomX);
     1693    end else
     1694      ADest.scale(zoomX);
     1695  end else
     1696  if bounds.Bottom-bounds.Top > 0 then
     1697  begin
     1698    zoomY := (ARect.Bottom-ARect.Top)/(bounds.Bottom-bounds.Top);
     1699    ADest.scale(zoomY);
     1700  end;
     1701  ADest.translate(-(bounds.Left+bounds.Right)*0.5, -(bounds.Bottom+bounds.Top)*0.5);
     1702  copyTo(ADest);
     1703  ADest.resetTransform;
     1704end;
    7101705
    7111706function TBGRAPath.GetSvgString: string;
    7121707const RadToDeg = 180/Pi;
    713 var savedPos: integer;
    714     a: TArcDef;
    715     formats: TFormatSettings;
    716     lastPos,p1: TPointF;
    717     implicitCommand: char;
     1708var
     1709  formats: TFormatSettings;
     1710  lastPosF: TPointF;
     1711  implicitCommand: char;
    7181712
    7191713  function FloatToString(value: single): string;
     
    7241718  function CoordToString(const pt: TPointF): string;
    7251719  begin
    726     lastPos := pt;
     1720    lastPosF := pt;
    7271721    result := FloatToString(pt.x)+FloatToString(pt.y);
    7281722  end;
     
    7451739  end;
    7461740
    747 var param: string;
    748 
     1741var elemType: TBGRAPathElementType;
     1742    elem: pointer;
     1743    a: PArcElement;
     1744    Pos: PtrInt;
     1745    p1: TPointF;
     1746    pts: array of TPointF;
     1747    i: integer;
    7491748begin
    7501749  formats := DefaultFormatSettings;
     
    7521751
    7531752  result := '';
    754   savedPos:= FDataPos;
    755   FDataPos := 0;
    756   lastPos := EmptyPointF;
     1753  Pos := 0;
     1754  lastPosF := EmptyPointF;
    7571755  implicitCommand := #0;
    758   while FDataPos < savedPos do
    759   begin
    760     case ReadElementType of
    761     peMoveTo: addCommand('M',CoordToString(ReadCoord));
    762     peLineTo: addCommand('L',CoordToString(ReadCoord));
    763     peCloseSubPath: addCommand('z','');
    764     peQuadraticBezierTo:
    765       begin
    766         param := CoordToString(ReadCoord);
    767         param += CoordToString(ReadCoord);
    768         addCommand('Q',param);
    769       end;
    770     peCubicBezierTo:
    771       begin
    772         param := CoordToString(ReadCoord);
    773         param += CoordToString(ReadCoord);
    774         param += CoordToString(ReadCoord);
    775         addCommand('C',param);
    776       end;
    777     peArc:
    778       begin
    779         a := ReadArcDef;
    780         p1 := ArcStartPoint(a);
    781         if isEmptyPointF(lastPos) or (p1 <> lastPos) then
    782           addCommand('L',CoordToString(p1));
    783         param := CoordToString(a.radius);
    784         param += FloatToString(a.xAngleRadCW*RadToDeg);
    785         param += BoolToString(IsLargeArc(a));
    786         param += BoolToString(not a.anticlockwise);
    787         param += CoordToString(ArcEndPoint(a));
    788         addCommand('A',param);
    789       end;
    790     end;
    791   end;
    792   FDataPos := savedPos;
     1756  repeat
     1757    GetElementAt(Pos, elemType, elem);
     1758    if elem = nil then break;
     1759    case elemType of
     1760      peMoveTo: addCommand('M',CoordToString(PPointF(elem)^));
     1761      peLineTo: addCommand('L',CoordToString(PPointF(elem)^));
     1762      peCloseSubPath: addCommand('z','');
     1763      peQuadraticBezierTo:
     1764        with PQuadraticBezierToElement(elem)^ do
     1765          addCommand('Q',CoordToString(ControlPoint)+CoordToString(Destination));
     1766      peCubicBezierTo:
     1767        with PCubicBezierToElement(elem)^ do
     1768          addCommand('C',CoordToString(ControlPoint1)+
     1769               CoordToString(ControlPoint2)+CoordToString(Destination));
     1770      peArc:
     1771        begin
     1772          a := PArcElement(elem);
     1773          p1 := ArcStartPoint(a^);
     1774          if isEmptyPointF(lastPosF) or (p1 <> lastPosF) then
     1775            addCommand('L',CoordToString(p1));
     1776          addCommand('A',CoordToString(a^.radius)+
     1777             FloatToString(a^.xAngleRadCW*RadToDeg)+
     1778             BoolToString(IsLargeArc(a^))+
     1779             BoolToString(not a^.anticlockwise)+
     1780             CoordToString(ArcEndPoint(a^)));
     1781        end;
     1782      peOpenedSpline, peClosedSpline:
     1783        begin
     1784          pts := GetPolygonalApprox(Pos, 0.1,True);
     1785          for i := 0 to high(pts) do
     1786          begin
     1787            if isEmptyPointF(lastPosF) then
     1788              addCommand('M',CoordToString(pts[i]))
     1789            else
     1790              addCommand('L',CoordToString(pts[i]));
     1791          end;
     1792        end;
     1793    end;
     1794  until not GoToNextElement(Pos);
    7931795end;
    7941796
     
    8001802end;
    8011803
     1804procedure TBGRAPath.RegisterCursor(ACursor: TBGRAPathCursor);
     1805begin
     1806  setlength(FCursors, length(FCursors)+1);
     1807  FCursors[high(FCursors)] := ACursor;
     1808end;
     1809
     1810procedure TBGRAPath.UnregisterCursor(ACursor: TBGRAPathCursor);
     1811var
     1812  i,j: Integer;
     1813begin
     1814  for i := high(FCursors) downto 0 do
     1815    if FCursors[i] = ACursor then
     1816    begin
     1817      for j := i to high(FCursors)-1 do
     1818        FCursors[j] := FCursors[j+1];
     1819      setlength(FCursors, length(FCursors)-1);
     1820      exit;
     1821    end;
     1822end;
     1823
     1824function TBGRAPath.SetLastCoord(ACoord: TPointF): TPointF;
     1825begin
     1826  FLastCoord := ACoord;
     1827  FLastTransformedCoord := FMatrix*ACoord;
     1828  result := FLastTransformedCoord;
     1829end;
     1830
     1831procedure TBGRAPath.ClearLastCoord;
     1832begin
     1833  FLastCoord := EmptyPointF;
     1834  FLastTransformedCoord := EmptyPointF;
     1835end;
     1836
     1837procedure TBGRAPath.BezierCurveFromTransformed(tcp1, cp2, pt: TPointF);
     1838begin
     1839  with PCubicBezierToElement(AllocateElement(peCubicBezierTo))^ do
     1840  begin
     1841    ControlPoint1 := tcp1;
     1842    ControlPoint2 := FMatrix*cp2;
     1843    Destination := SetLastCoord(pt);
     1844    FExpectedTransformedControlPoint := Destination + (Destination-ControlPoint2);
     1845  end;
     1846end;
     1847
     1848procedure TBGRAPath.QuadraticCurveFromTransformed(tcp, pt: TPointF);
     1849begin
     1850  with PQuadraticBezierToElement(AllocateElement(peQuadraticBezierTo))^ do
     1851  begin
     1852    ControlPoint := tcp;
     1853    Destination := SetLastCoord(pt);
     1854    FExpectedTransformedControlPoint := Destination+(Destination-ControlPoint);
     1855  end;
     1856end;
     1857
     1858function TBGRAPath.LastCoordDefined: boolean;
     1859begin
     1860  result := not isEmptyPointF(FLastTransformedCoord);
     1861end;
     1862
     1863function TBGRAPath.GetPolygonalApprox(APos: IntPtr; AAcceptedDeviation: single; AIncludeFirstPoint: boolean): ArrayOfTPointF;
     1864var pts: ArrayOfTPointF;
     1865  elemType: TBGRAPathElementType;
     1866  elem: pointer;
     1867  pt : TPointF;
     1868  i: NativeInt;
     1869begin
     1870  GetElementAt(APos, elemType, elem);
     1871  case elemType of
     1872    peQuadraticBezierTo:
     1873      with PQuadraticBezierToElement(elem)^ do
     1874        result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint);
     1875    peCubicBezierTo:
     1876      with PCubicBezierToElement(elem)^ do
     1877        result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ToPoints(AAcceptedDeviation, AIncludeFirstPoint);
     1878    peArc:
     1879      begin
     1880        result := ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation);
     1881        pt := GetElementStartCoord(APos);
     1882        if pt <> result[0] then
     1883        begin
     1884          setlength(result, length(result)+1);
     1885          for i := high(result) downto 1 do
     1886            result[i] := result[i-1];
     1887          result[0] := pt;
     1888        end;
     1889      end;
     1890    peOpenedSpline, peClosedSpline:
     1891      with PSplineElement(elem)^ do
     1892      begin
     1893        setlength(pts, NbControlPoints);
     1894        move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF));
     1895        if elemType = peOpenedSpline then
     1896          result := ComputeOpenedSpline(pts, SplineStyle, 0.25, AAcceptedDeviation)
     1897        else
     1898          result := ComputeClosedSpline(pts, SplineStyle, AAcceptedDeviation);
     1899      end;
     1900  end;
     1901end;
     1902
     1903function TBGRAPath.getPoints: ArrayOfTPointF;
     1904begin
     1905  result := ToPoints;
     1906end;
     1907
     1908function TBGRAPath.getPoints(AMatrix: TAffineMatrix): ArrayOfTPointF;
     1909begin
     1910  result := ToPoints(AMatrix);
     1911end;
     1912
     1913function TBGRAPath.getCursor: TBGRACustomPathCursor;
     1914begin
     1915  result := CreateCursor;
     1916end;
     1917
     1918procedure TBGRAPath.InternalDraw(ADrawProc: TBGRAPathDrawProc;
     1919  const AMatrix: TAffineMatrix; AAcceptedDeviation: single; AData: pointer);
     1920var
     1921  nbSub: NativeInt;
     1922
     1923  procedure OutputSub(subPathStartPos, subPathEndPos: IntPtr);
     1924  var
     1925    sub: array of ArrayOfTPointF;
     1926    temp: ArrayOfTPointF;
     1927    startPos,pos,nbPts,curPt,curSub: NativeInt;
     1928    elemType: TBGRAPathElementType;
     1929    elem: pointer;
     1930  begin
     1931    pos := subPathStartPos;
     1932    setlength(sub, nbSub);
     1933    curSub := 0;
     1934    while (pos <= subPathEndPos) and (curSub < nbSub) do
     1935    begin
     1936      GetElementAt(pos, elemType, elem);
     1937      if elem = nil then break;
     1938      case elemType of
     1939        peMoveTo,peLineTo,peCloseSubPath: begin
     1940            startPos := pos;
     1941            if (elemType = peMoveTo) and (curSub > 0) then
     1942              nbPts := 2
     1943            else
     1944              nbPts := 1;
     1945            while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1946            begin
     1947              GoToNextElement(pos);
     1948              inc(nbPts);
     1949            end;
     1950            setlength(temp, nbPts);
     1951            pos := startPos;
     1952            if (elemType = peMoveTo) and (curSub > 0) then
     1953            begin
     1954              temp[0] := EmptyPointF;
     1955              temp[1] := PPointF(elem)^;
     1956              curPt := 2;
     1957            end else
     1958            begin
     1959              temp[0] := PPointF(elem)^;
     1960              curPt := 1;
     1961            end;
     1962            while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     1963            begin
     1964              GoToNextElement(pos);
     1965              GetElementAt(pos, elemType, elem);
     1966              temp[curPt] := PPointF(elem)^;
     1967              inc(curPt);
     1968            end;
     1969            sub[curSub] := temp;
     1970            inc(curSub);
     1971            temp := nil;
     1972          end;
     1973        peQuadraticBezierTo,peCubicBezierTo,peArc,
     1974        peOpenedSpline, peClosedSpline:
     1975          begin
     1976            sub[curSub] := GetPolygonalApprox(pos, AAcceptedDeviation, False);
     1977            inc(curSub);
     1978          end;
     1979      end;
     1980      GoToNextElement(pos);
     1981    end;
     1982    temp := ConcatPointsF(sub);
     1983    if not IsAffineMatrixIdentity(AMatrix) then
     1984      temp := AMatrix*temp;
     1985    if (elemType = peCloseSubPath) or ((curSub = 2) and (elemType = peClosedSpline)) then
     1986      ADrawProc(temp, True, AData)
     1987    else
     1988      ADrawProc(temp, False, AData);
     1989  end;
     1990
     1991var
     1992  subPathStartPos: IntPtr;
     1993  prevPos,pos: PtrInt;
     1994  elemType: TBGRAPathElementType;
     1995  elem: pointer;
     1996begin
     1997  AAcceptedDeviation := CorrectAcceptedDeviation(AAcceptedDeviation, AMatrix);
     1998  pos := 0;
     1999  nbSub := 0;
     2000  subPathStartPos := pos;
     2001  repeat
     2002    prevPos := pos;
     2003    GetElementAt(pos, elemType, elem);
     2004    if elem = nil then
     2005    begin
     2006      pos := prevPos;
     2007      break;
     2008    end;
     2009    if (elemType = peMoveTo) and (nbSub > 0) then
     2010    begin
     2011      OutputSub(subPathStartPos,prevPos);
     2012      nbSub := 0;
     2013      subPathStartPos := pos;
     2014    end;
     2015    case elemType of
     2016      peMoveTo,peLineTo,peCloseSubPath: begin
     2017          inc(nbSub);
     2018          while PeekNextElement(pos) in[peLineTo,peCloseSubPath] do
     2019            GoToNextElement(pos);
     2020        end;
     2021      peQuadraticBezierTo, peCubicBezierTo, peArc, peOpenedSpline, peClosedSpline: inc(nbSub);
     2022    end;
     2023  until not GoToNextElement(pos);
     2024  if nbSub > 0 then OutputSub(subPathStartPos,pos);
     2025end;
     2026
    8022027procedure TBGRAPath.addPath(const AValue: string);
    8032028var p: integer;
    8042029    numberError: boolean;
     2030    startCoord,lastCoord: TPointF;
    8052031
    8062032  function parseFloat: single;
     
    8122038    if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p);
    8132039    while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p);
    814     if (p <= length(AValue)) and (AValue[p] in['e','E']) then inc(p);
    815     if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p);
    816     while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p);
     2040    if (p <= length(AValue)) and (AValue[p] in['e','E']) then
     2041    begin
     2042      inc(p);
     2043      if (p <= length(AValue)) and (AValue[p] in['+','-']) then inc(p);
     2044      while (p <= length(AValue)) and (AValue[p] in['0'..'9','.']) do inc(p);
     2045    end;
    8172046    val(copy(AValue,numberStart,p-numberStart),result,errPos);
    8182047    if errPos <> 0 then numberError := true;
     
    8212050  function parseCoord(relative: boolean): TPointF;
    8222051  begin
    823     result := PointF(parseFloat,parseFloat);
    824     if relative and not isEmptyPointF(FLastCoord) then result += FLastCoord;
     2052    result.x := parseFloat;
     2053    result.y := parseFloat;
     2054    if relative and not isEmptyPointF(lastCoord) then result += lastCoord;
     2055    if isEmptyPointF(lastCoord) then startCoord := result;
    8252056  end;
    8262057
     
    8322063  largeArc: boolean;
    8332064begin
    834   FLastCoord := EmptyPointF;
    835   FStartCoord := EmptyPointF;
     2065  BeginSubPath;
     2066  lastCoord := EmptyPointF;
     2067  startCoord := EmptyPointF;
    8362068  p := 1;
    8372069  implicitCommand:= #0;
     
    8532085           closePath;
    8542086           implicitCommand:= #0;
     2087           lastCoord := startCoord;
    8552088         end;
    8562089    'M': begin
    8572090           p1 := parseCoord(relative);
    858            if not numberError then moveTo(p1);
     2091           if not numberError then
     2092           begin
     2093             moveTo(p1);
     2094             lastCoord := p1;
     2095           end;
    8592096           if relative then implicitCommand:= 'l' else
    8602097             implicitCommand:= 'L';
     
    8622099    'L': begin
    8632100           p1 := parseCoord(relative);
    864            if not numberError then lineTo(p1);
     2101           if not numberError then
     2102           begin
     2103             lineTo(p1);
     2104             lastCoord := p1;
     2105           end;
    8652106      end;
    8662107    'H': begin
    867         if not isEmptyPointF(FLastCoord) then p1 := FLastCoord
    868         else p1 := PointF(0,0);
    869         if relative then p1.x += parseFloat
    870         else p1.x := parseFloat;
    871         if not numberError then lineTo(p1);
     2108        if not isEmptyPointF(lastCoord) then
     2109        begin
     2110          p1 := lastCoord;
     2111          if relative then p1.x += parseFloat
     2112          else p1.x := parseFloat;
     2113        end else
     2114        begin
     2115          p1 := PointF(parseFloat,0);
     2116          lastCoord := p1;
     2117          startCoord := p1;
     2118        end;
     2119        if not numberError then
     2120        begin
     2121          lineTo(p1);
     2122          lastCoord := p1;
     2123        end;
    8722124      end;
    8732125    'V': begin
    874         if not isEmptyPointF(FLastCoord) then p1 := FLastCoord
    875         else p1 := PointF(0,0);
    876         if relative then p1.y += parseFloat
    877         else p1.y := parseFloat;
    878         if not numberError then lineTo(p1);
     2126        if not isEmptyPointF(lastCoord) then
     2127        begin
     2128          p1 := lastCoord;
     2129          if relative then p1.y += parseFloat
     2130          else p1.y := parseFloat;
     2131        end else
     2132        begin
     2133          p1 := PointF(0,parseFloat);
     2134          lastCoord := p1;
     2135          startCoord := p1;
     2136        end;
     2137        if not numberError then
     2138        begin
     2139          lineTo(p1);
     2140          lastCoord := p1;
     2141        end;
    8792142      end;
    8802143    'C': begin
     
    8822145        c2 := parseCoord(relative);
    8832146        p1 := parseCoord(relative);
    884         if not numberError then bezierCurveTo(c1,c2,p1);
     2147        if not numberError then
     2148        begin
     2149          bezierCurveTo(c1,c2,p1);
     2150          lastCoord := p1;
     2151        end;
    8852152      end;
    8862153    'S': begin
    8872154        c2 := parseCoord(relative);
    8882155        p1 := parseCoord(relative);
    889         if not numberError then smoothBezierCurveTo(c2,p1);
     2156        if not numberError then
     2157        begin
     2158          smoothBezierCurveTo(c2,p1);
     2159          lastCoord := p1;
     2160        end;
    8902161      end;
    8912162    'Q': begin
    8922163        c1 := parseCoord(relative);
    8932164        p1 := parseCoord(relative);
    894         if not numberError then quadraticCurveTo(c1,p1);
     2165        if not numberError then
     2166        begin
     2167          quadraticCurveTo(c1,p1);
     2168          lastCoord := p1;
     2169        end;
    8952170      end;
    8962171    'T': begin
    8972172        p1 := parseCoord(relative);
    898         if not numberError then smoothQuadraticCurveTo(p1);
    899       end;
    900     'A': begin
    901         a.radius := parseCoord(false);
     2173        if not numberError then
     2174        begin
     2175          smoothQuadraticCurveTo(p1);
     2176          lastCoord := p1;
     2177        end;
     2178    end;
     2179    'A':
     2180      begin
     2181        a.radius.x := parseFloat;
     2182        a.radius.y := parseFloat;
    9022183        a.xAngleRadCW := parseFloat*Pi/180;
    9032184        largeArc := parseFloat<>0;
    9042185        a.anticlockwise:= parseFloat=0;
    9052186        p1 := parseCoord(relative);
    906         arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y);
     2187        if not numberError then
     2188        begin
     2189          arcTo(a.radius.x,a.radius.y,a.xAngleRadCW,largeArc,a.anticlockwise,p1.x,p1.y);
     2190          lastCoord := p1;
     2191        end;
    9072192      end;
    9082193    end;
     
    9152200end;
    9162201
     2202procedure TBGRAPath.openedSpline(const pts: array of TPointF;
     2203  style: TSplineStyle);
     2204var elem: PSplineElement;
     2205  i: NativeInt;
     2206  p: PPointF;
     2207begin
     2208  if length(pts) <= 2 then
     2209  begin
     2210    polyline(pts);
     2211    exit;
     2212  end;
     2213  if not LastCoordDefined then moveTo(pts[0]);
     2214  elem := AllocateElement(peOpenedSpline, length(pts)*sizeof(TPointF));
     2215  elem^.NbControlPoints := length(pts);
     2216  elem^.SplineStyle := style;
     2217  p := PPointF(elem+1);
     2218  for i := 0 to high(pts)-1 do
     2219  begin
     2220    p^ := FMatrix*pts[i];
     2221    inc(p);
     2222  end;
     2223  p^ := SetLastCoord(pts[high(pts)]);
     2224  inc(p);
     2225  PInteger(p)^ := length(pts);
     2226end;
     2227
     2228procedure TBGRAPath.closedSpline(const pts: array of TPointF;
     2229  style: TSplineStyle);
     2230var elem: PSplineElement;
     2231  i: NativeInt;
     2232  p: PPointF;
     2233begin
     2234  if length(pts) = 0 then exit;
     2235  if not LastCoordDefined then moveTo(ClosedSplineStartPoint(pts, style));
     2236  if length(pts) <= 2 then exit;
     2237  elem := AllocateElement(peClosedSpline, length(pts)*sizeof(TPointF));
     2238  elem^.NbControlPoints := length(pts);
     2239  elem^.SplineStyle := style;
     2240  p := PPointF(elem+1);
     2241  for i := 0 to high(pts) do
     2242  begin
     2243    p^ := FMatrix*pts[i];
     2244    inc(p);
     2245  end;
     2246  PInteger(p)^ := length(pts);
     2247end;
     2248
     2249procedure TBGRAPath.BitmapDrawSubPathProc(const APoints: array of TPointF;
     2250  AClosed: boolean; AData: pointer);
     2251begin
     2252  with TStrokeData(AData^) do
     2253  if AClosed then
     2254  begin
     2255    if Texture <> nil then
     2256      Bitmap.DrawPolygonAntialias(APoints, Texture, Width)
     2257    else
     2258      Bitmap.DrawPolygonAntialias(APoints, Color, Width);
     2259  end else
     2260  begin
     2261    if Texture <> nil then
     2262      Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Texture, Width)
     2263    else
     2264      Bitmap.DrawPolyLineAntialiasAutocycle(APoints, Color, Width);
     2265  end;
     2266end;
     2267
     2268function TBGRAPath.CorrectAcceptedDeviation(AAcceptedDeviation: single;
     2269  const AMatrix: TAffineMatrix): single;
     2270var maxZoom: single;
     2271begin
     2272  //determine the zoom of the matrix
     2273  maxZoom := Max(VectLen(PointF(AMatrix[1,1],AMatrix[2,1])),
     2274     VectLen(PointF(AMatrix[1,2],AMatrix[2,2])));
     2275  //make the accepted deviation smaller if the matrix zooms to avoid that
     2276  // curves would look angular
     2277  if maxZoom = 0 then
     2278    result:= 1e10
     2279  else
     2280    result := AAcceptedDeviation / maxZoom;
     2281end;
     2282
     2283procedure TBGRAPath.OnModify;
     2284begin
     2285  if length(FCursors)> 0 then
     2286      raise Exception.Create('You cannot modify the path when there are cursors');
     2287end;
     2288
     2289procedure TBGRAPath.OnMatrixChange;
     2290begin
     2291  //transformed coord are not changed,
     2292  //but original coords are lost in the process.
     2293  //this has a consequence when using
     2294  //arc functions that rely on the previous
     2295  //coordinate
     2296  FLastCoord := EmptyPointF;
     2297  FSubPathStartCoord := EmptyPointF;
     2298end;
     2299
    9172300procedure TBGRAPath.NeedSpace(count: integer);
    9182301begin
    919   if FDataPos + count > FDataSize then
    920   begin
    921     FDataSize := FDataSize*2+8;
    922     ReAllocMem(FData, FDataSize);
    923   end;
    924 end;
    925 
    926 procedure TBGRAPath.StoreCoord(const pt: TPointF);
    927 begin
    928   NeedSpace(sizeof(single)*2);
    929   with FMatrix*pt do
    930   begin
    931     PSingle(FData+FDataPos)^ := x;
    932     PSingle(FData+FDataPos+sizeof(single))^ := y;
    933   end;
    934   Inc(FDataPos, sizeof(single)*2);
    935   FLastCoord := pt;
    936 end;
    937 
    938 function TBGRAPath.ReadCoord: TPointF;
    939 begin
    940   result := PPointF(FData+FDataPos)^;
    941   inc(FDataPos,sizeof(TPointF));
    942 end;
    943 
    944 procedure TBGRAPath.StoreElementType(value: TBGRAPathElementType);
    945 begin
    946   NeedSpace(sizeof(TBGRAPathElementType));
    947   PBGRAPathElementType(FData+FDataPos)^ := value;
    948   Inc(FDataPos, sizeof(TBGRAPathElementType));
    949   FLastElementType:= value;
    950 end;
    951 
    952 function TBGRAPath.ReadElementType: TBGRAPathElementType;
    953 begin
    954   result := PBGRAPathElementType(FData+FDataPos)^;
    955   inc(FDataPos,sizeof(TBGRAPathElementType));
    956 end;
    957 
    958 function TBGRAPath.ReadArcDef: TArcDef;
    959 begin
    960   result := PArcDef(FData+FDataPos)^;
    961   inc(FDataPos,sizeof(TArcDef));
    962 end;
    963 
    964 procedure TBGRAPath.RewindFloat;
    965 begin
    966   if FDataPos >= sizeof(single) then dec(FDataPos, sizeof(Single));
     2302  OnModify;
     2303  if FDataPos + count > FDataCapacity then
     2304  begin
     2305    FDataCapacity := (FDataCapacity shl 1)+8;
     2306    if FDataPos + count + 8 > FDataCapacity then
     2307      FDataCapacity := FDataPos + count + 8;
     2308    ReAllocMem(FData, FDataCapacity);
     2309  end;
     2310end;
     2311
     2312function TBGRAPath.AllocateElement(AElementType: TBGRAPathElementType;
     2313  AExtraBytes: PtrInt): Pointer;
     2314var t: PtrInt;
     2315begin
     2316  if not (AElementType in [succ(peNone)..high(TBGRAPathElementType)]) then
     2317    raise exception.Create('Invalid element type');
     2318  OnModify;
     2319  t := PathElementSize[AElementType]+AExtraBytes;
     2320  NeedSpace(SizeOf(TPathElementHeader)+t);
     2321  with PPathElementHeader(FData+FDataPos)^ do
     2322  begin
     2323    ElementType:= AElementType;
     2324    PreviousElementType := FLastStoredElementType;
     2325  end;
     2326  result := FData+(FDataPos+SizeOf(TPathElementHeader));
     2327  FLastSubPathElementType:= AElementType;
     2328  FLastStoredElementType:= AElementType;
     2329  Inc(FDataPos, sizeof(TPathElementHeader)+t);
    9672330end;
    9682331
     
    9702333begin
    9712334  FData := nil;
    972   FDataSize := 0;
    973   FDataPos := 0;
    974   FLastElementType := peNone;
    975   FLastCoord := EmptyPointF;
    976   FStartCoord := EmptyPointF;
    977   FExpectedControlPoint := EmptyPointF;
     2335  FDataCapacity := 0;
     2336  FLastMoveToDataPos := -1;
     2337  beginPath;
    9782338  resetTransform;
     2339end;
     2340
     2341function TBGRAPath.GoToNextElement(var APos: PtrInt): boolean;
     2342var newPos: PtrInt;
     2343  p: PSplineElement;
     2344  elemType: TBGRAPathElementType;
     2345begin
     2346  if APos >= FDataPos then
     2347    result := false
     2348  else
     2349  begin
     2350    elemType := PPathElementHeader(FData+APos)^.ElementType;
     2351    newPos := APos + sizeof(TPathElementHeader) + PathElementSize[elemType];
     2352    if elemType in[peOpenedSpline,peClosedSpline] then
     2353    begin
     2354      p := PSplineElement(FData+(APos+sizeof(TPathElementHeader)));
     2355      newPos += p^.NbControlPoints * sizeof(TPointF); //extra
     2356    end;
     2357    if newPos < FDataPos then
     2358    begin
     2359      result := true;
     2360      APos := newPos;
     2361      if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or
     2362        not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then
     2363          raise exception.Create('Internal structure error');
     2364    end
     2365    else
     2366      result := false;
     2367  end;
     2368end;
     2369
     2370function TBGRAPath.GoToPreviousElement(var APos: PtrInt): boolean;
     2371var lastElemType: TBGRAPathElementType;
     2372begin
     2373  if APos <= 0 then
     2374    result := false
     2375  else
     2376  begin
     2377    result := true;
     2378    if (APos = FDataPos) then
     2379      lastElemType := FLastStoredElementType
     2380    else
     2381      lastElemType := PPathElementHeader(FData+APos)^.PreviousElementType;
     2382
     2383    if lastElemType in [peOpenedSpline,peClosedSpline] then
     2384      dec(APos, (PInteger(FData+APos)-1)^ *sizeof(TPointF)); //extra
     2385    dec(APos, sizeof(TPathElementHeader) + PathElementSize[lastElemType]);
     2386
     2387    if not CheckElementType(PPathElementHeader(FData+APos)^.ElementType) or
     2388      not CheckElementType(PPathElementHeader(FData+APos)^.PreviousElementType) then
     2389        raise exception.Create('Internal structure error');
     2390  end;
     2391end;
     2392
     2393function TBGRAPath.PeekNextElement(APos: PtrInt): TBGRAPathElementType;
     2394begin
     2395  if not GoToNextElement(APos) then
     2396    result := peNone
     2397  else
     2398    result := PPathElementHeader(FData+APos)^.ElementType;
     2399end;
     2400
     2401function TBGRAPath.GetElementStartCoord(APos: PtrInt): TPointF;
     2402var
     2403  elemType: TBGRAPathElementType;
     2404  elem: pointer;
     2405begin
     2406  GetElementAt(APos, elemType, elem);
     2407  case elemType of
     2408  peNone: raise exception.Create('No element');
     2409  peMoveTo: result := PPointF(elem)^;
     2410  else
     2411    begin
     2412      if not GoToPreviousElement(APos) then
     2413        raise exception.Create('No previous element')
     2414      else
     2415      begin
     2416        result := GetElementEndCoord(APos);
     2417      end;
     2418    end;
     2419  end;
     2420end;
     2421
     2422function TBGRAPath.GetElementEndCoord(APos: PtrInt): TPointF;
     2423var elemType: TBGRAPathElementType;
     2424  elem: pointer;
     2425begin
     2426  GetElementAt(APos, elemType, elem);
     2427  case elemType of
     2428  peMoveTo,peLineTo,peCloseSubPath: result := PPointF(elem)^;
     2429  peQuadraticBezierTo: result := PQuadraticBezierToElement(elem)^.Destination;
     2430  peCubicBezierTo: result := PCubicBezierToElement(elem)^.Destination;
     2431  peArc: result := ArcEndPoint(PArcElement(elem)^);
     2432  peClosedSpline: result := PPointF(PSplineElement(elem)+1)^;
     2433  peOpenedSpline: result := (PPointF(PSplineElement(elem)+1)+(PSplineElement(elem)^.NbControlPoints-1))^;
     2434  else
     2435    result := EmptyPointF;
     2436  end;
     2437end;
     2438
     2439function TBGRAPath.GetElementLength(APos: PtrInt; AAcceptedDeviation: single): Single;
     2440var elemType: TBGRAPathElementType;
     2441  elem: pointer;
     2442  pts: array of TPointF;
     2443begin
     2444  GetElementAt(APos, elemType, elem);
     2445  case elemType of
     2446  peMoveTo: result := 0;
     2447  peLineTo,peCloseSubPath: result := VectLen(PPointF(elem)^ - GetElementStartCoord(APos))*FScale;
     2448  peQuadraticBezierTo: with PQuadraticBezierToElement(elem)^ do
     2449      result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint,Destination).ComputeLength;
     2450  peCubicBezierTo: with PCubicBezierToElement(elem)^ do
     2451      result := BGRABitmapTypes.BezierCurve(GetElementStartCoord(APos),ControlPoint1,ControlPoint2,Destination).ComputeLength(AAcceptedDeviation);
     2452  peArc: begin
     2453      result := VectLen(ArcStartPoint(PArcElement(elem)^) - GetElementStartCoord(APos));
     2454      result += PolylineLen(ComputeArc(PArcElement(elem)^, 0.1/AAcceptedDeviation));
     2455    end;
     2456  peClosedSpline,peOpenedSpline:
     2457    begin
     2458      pts := GetPolygonalApprox(APos, AAcceptedDeviation, true);
     2459      result := PolylineLen(pts) + VectLen(pts[0]-GetElementStartCoord(APos));
     2460    end
     2461  else
     2462    result := 0;
     2463  end;
     2464end;
     2465
     2466procedure TBGRAPath.GetElementAt(APos: PtrInt; out
     2467  AElementType: TBGRAPathElementType; out AElement: pointer);
     2468begin
     2469  if APos >= FDataPos then
     2470  begin
     2471    AElementType := peNone;
     2472    AElement := nil;
     2473  end else
     2474  begin
     2475    AElementType:= PPathElementHeader(FData+APos)^.ElementType;
     2476    AElement := FData+(APos+sizeof(TPathElementHeader));
     2477  end;
    9792478end;
    9802479
     
    9902489end;
    9912490
     2491constructor TBGRAPath.Create(const APoints: ArrayOfTPointF);
     2492begin
     2493  Init;
     2494  SetPoints(APoints);
     2495end;
     2496
     2497constructor TBGRAPath.Create(APath: IBGRAPath);
     2498begin
     2499  Init;
     2500  APath.copyTo(self);
     2501end;
     2502
    9922503destructor TBGRAPath.Destroy;
    993 begin
     2504var i: integer;
     2505begin
     2506  for I := 0 to high(FCursors) do
     2507    FCursors[i].OnPathFree;
    9942508  if Assigned(FData) then
    9952509  begin
     
    10022516procedure TBGRAPath.beginPath;
    10032517begin
     2518  DoClear;
     2519end;
     2520
     2521procedure TBGRAPath.beginSubPath;
     2522begin
     2523  OnModify;
     2524  FLastSubPathElementType := peNone;
     2525  ClearLastCoord;
     2526  FSubPathStartCoord := EmptyPointF;
     2527  FExpectedTransformedControlPoint := EmptyPointF;
     2528end;
     2529
     2530procedure TBGRAPath.DoClear;
     2531begin
     2532  OnModify;
    10042533  FDataPos := 0;
     2534  BeginSubPath;
     2535end;
     2536
     2537function TBGRAPath.CheckElementType(AElementType: TBGRAPathElementType): boolean;
     2538begin
     2539  result := AElementType <= high(TBGRAPathElementType);
    10052540end;
    10062541
    10072542procedure TBGRAPath.closePath;
    1008 begin
    1009   if (FLastElementType <> peNone) and (FLastElementType <> peCloseSubPath) then
    1010   begin
    1011     StoreElementType(peCloseSubPath);
    1012     FLastCoord := FStartCoord;
     2543var
     2544  moveToType: TBGRAPathElementType;
     2545  moveToElem: pointer;
     2546begin
     2547  if (FLastSubPathElementType <> peNone) and (FLastSubPathElementType <> peCloseSubPath) then
     2548  begin
     2549    with PClosePathElement(AllocateElement(peCloseSubPath))^ do
     2550    begin
     2551      StartCoordinate := FSubPathTransformedStartCoord;
     2552      LoopDataPos := FLastMoveToDataPos;
     2553    end;
     2554    if FLastMoveToDataPos <> -1 then
     2555    begin
     2556      GetElementAt(FLastMoveToDataPos,moveToType,moveToElem);
     2557      PMoveToElement(moveToElem)^.LoopDataPos := FDataPos;
     2558      FLastMoveToDataPos:= -1;
     2559    end;
     2560    FLastCoord := FSubPathStartCoord;
     2561    FLastTransformedCoord := FSubPathTransformedStartCoord;
    10132562  end;
    10142563end;
     
    10162565procedure TBGRAPath.translate(x, y: single);
    10172566begin
     2567  OnMatrixChange;
    10182568  FMatrix *= AffineMatrixTranslation(x,y);
    10192569end;
     
    10212571procedure TBGRAPath.resetTransform;
    10222572begin
     2573  OnMatrixChange;
    10232574  FMatrix := AffineMatrixIdentity;
    10242575  FAngleRadCW := 0;
     
    10282579procedure TBGRAPath.rotate(angleRadCW: single);
    10292580begin
     2581  OnMatrixChange;
    10302582  FMatrix *= AffineMatrixRotationRad(-angleRadCW);
    10312583  FAngleRadCW += angleRadCW;
     
    10542606procedure TBGRAPath.scale(factor: single);
    10552607begin
     2608  OnMatrixChange;
    10562609  FMatrix *= AffineMatrixScale(factor,factor);
    10572610  FScale *= factor;
     
    10702623procedure TBGRAPath.moveTo(const pt: TPointF);
    10712624begin
    1072   if FLastElementType <> peMoveTo then
    1073   begin
    1074     StoreElementType(peMoveTo);
    1075     StoreCoord(pt);
     2625  if FLastSubPathElementType <> peMoveTo then
     2626  begin
     2627    FLastMoveToDataPos:= FDataPos;
     2628    with PMoveToElement(AllocateElement(peMoveTo))^ do
     2629    begin
     2630      StartCoordinate := SetLastCoord(pt);
     2631      LoopDataPos := -1;
     2632    end
    10762633  end else
    1077   begin
    1078     RewindFloat;
    1079     RewindFloat;
    1080     StoreCoord(pt);
    1081   end;
    1082   FLastCoord := pt;
    1083   FStartCoord := FLastCoord;
     2634    PMoveToElement(FData+(FDataPos-Sizeof(TMoveToElement)))^.StartCoordinate := SetLastCoord(pt);
     2635  FSubPathStartCoord := FLastCoord;
     2636  FSubPathTransformedStartCoord := FLastTransformedCoord;
    10842637end;
    10852638
    10862639procedure TBGRAPath.lineTo(const pt: TPointF);
    1087 begin
    1088   if not isEmptyPointF(FLastCoord) then
    1089   begin
    1090     StoreElementType(peLineTo);
    1091     StoreCoord(pt);
    1092     FLastCoord := pt;
     2640var lastTransfCoord, newTransfCoord: TPointF;
     2641begin
     2642  if LastCoordDefined then
     2643  begin
     2644    lastTransfCoord := FLastTransformedCoord;
     2645    newTransfCoord := SetLastCoord(pt);
     2646    if newTransfCoord <> lastTransfCoord then
     2647      PPointF(AllocateElement(peLineTo))^ := newTransfCoord;
    10932648  end else
    10942649    moveTo(pt);
    10952650end;
    10962651
     2652procedure TBGRAPath.polyline(const pts: array of TPointF);
     2653var i: integer;
     2654begin
     2655  if length(pts) = 0 then exit;
     2656  NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts));
     2657  moveTo(pts[0]);
     2658  for i := 1 to high(pts) do lineTo(pts[i]);
     2659end;
     2660
    10972661procedure TBGRAPath.polylineTo(const pts: array of TPointF);
    10982662var i: integer;
    10992663begin
    1100   NeedSpace((sizeof(TBGRAPathElementType)+2*sizeof(single))*length(pts));
    1101   for i := 0 to high(pts) do with pts[i] do lineTo(x,y);
     2664  NeedSpace((sizeof(TPathElementHeader)+sizeof(TPointF))*length(pts));
     2665  for i := 0 to high(pts) do lineTo(pts[i]);
     2666end;
     2667
     2668procedure TBGRAPath.polygon(const pts: array of TPointF);
     2669var lastPt: integer;
     2670begin
     2671  if length(pts) = 0 then exit;
     2672  lastPt := high(pts);
     2673  while (lastPt > 1) and (pts[lastPt] = pts[0]) do dec(lastPt);
     2674  if lastPt <> high(pts) then
     2675    polyline(slice(pts,lastPt+1))
     2676  else
     2677    polyline(pts);
     2678  closePath;
    11022679end;
    11032680
     
    11092686procedure TBGRAPath.quadraticCurveTo(const cp, pt: TPointF);
    11102687begin
    1111   if not isEmptyPointF(FLastCoord) then
    1112   begin
    1113     StoreElementType(peQuadraticBezierTo);
    1114     StoreCoord(cp);
    1115     StoreCoord(pt);
    1116     FLastCoord := pt;
    1117   end else
     2688  if LastCoordDefined then
     2689    QuadraticCurveFromTransformed(FMatrix*cp, pt) else
     2690  begin
    11182691    lineTo(pt);
    1119   FExpectedControlPoint := pt+(pt-cp);
     2692    FExpectedTransformedControlPoint := FMatrix*(pt+(pt-cp));
     2693  end;
    11202694end;
    11212695
     
    11272701procedure TBGRAPath.bezierCurveTo(const cp1, cp2, pt: TPointF);
    11282702begin
    1129   if isEmptyPointF(FLastCoord) then moveTo(cp1);
    1130   StoreElementType(peCubicBezierTo);
    1131   StoreCoord(cp1);
    1132   StoreCoord(cp2);
    1133   StoreCoord(pt);
    1134   FLastCoord := pt;
    1135   FExpectedControlPoint := pt + (pt-cp2);
     2703  if not LastCoordDefined then moveTo(cp1);
     2704  BezierCurveFromTransformed(FMatrix*cp1, cp2, pt);
    11362705end;
    11372706
     
    11422711end;
    11432712
     2713procedure TBGRAPath.bezierCurve(p1, cp1, cp2, p2: TPointF);
     2714begin
     2715  moveTo(p1);
     2716  bezierCurveTo(cp1,cp2,p2);
     2717end;
     2718
    11442719procedure TBGRAPath.smoothBezierCurveTo(cp2x, cp2y, x, y: single);
    11452720begin
     
    11492724procedure TBGRAPath.smoothBezierCurveTo(const cp2, pt: TPointF);
    11502725begin
    1151   if (FLastElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedControlPoint) then
    1152     bezierCurveTo(FExpectedControlPoint,cp2,pt)
    1153   else if not isEmptyPointF(FLastCoord) then
    1154     bezierCurveTo(FLastCoord,cp2,pt)
     2726  if (FLastSubPathElementType = peCubicBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then
     2727    BezierCurveFromTransformed(FExpectedTransformedControlPoint,cp2,pt)
     2728  else if LastCoordDefined then
     2729    BezierCurveFromTransformed(FLastTransformedCoord,cp2,pt)
    11552730  else
    11562731    bezierCurveTo(cp2,cp2,pt);
     
    11632738end;
    11642739
     2740procedure TBGRAPath.quadraticCurve(p1, cp, p2: TPointF);
     2741begin
     2742  moveTo(p1);
     2743  quadraticCurveTo(cp,p2);
     2744end;
     2745
    11652746procedure TBGRAPath.smoothQuadraticCurveTo(x, y: single);
    11662747begin
     
    11702751procedure TBGRAPath.smoothQuadraticCurveTo(const pt: TPointF);
    11712752begin
    1172   if (FLastElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedControlPoint) then
    1173     quadraticCurveTo(FExpectedControlPoint,pt)
    1174   else if not isEmptyPointF(FLastCoord) then
    1175     quadraticCurveTo(FLastCoord,pt)
     2753  if (FLastSubPathElementType = peQuadraticBezierTo) and not isEmptyPointF(FExpectedTransformedControlPoint) then
     2754    QuadraticCurveFromTransformed(FExpectedTransformedControlPoint,pt)
     2755  else if LastCoordDefined then
     2756    QuadraticCurveFromTransformed(FLastTransformedCoord,pt)
    11762757  else
    11772758    quadraticCurveTo(pt,pt);
     
    12372818var p0 : TPointF;
    12382819begin
    1239   if isEmptyPointF(FLastCoord) then
     2820  if IsEmptyPointF(FLastCoord) then
    12402821    p0 := p1 else p0 := FLastCoord;
    12412822  arc(Html5ArcTo(p0,p1,p2,radius));
     
    12432824
    12442825procedure TBGRAPath.arc(const arcDef: TArcDef);
    1245 var transformedArc: TArcDef;
     2826var transformedArc: TArcElement;
    12462827begin
    12472828  if (arcDef.radius.x = 0) and (arcDef.radius.y = 0) then
     
    12492830  else
    12502831  begin
    1251     if isEmptyPointF(FLastCoord) then
     2832    if not LastCoordDefined then
    12522833      moveTo(ArcStartPoint(arcDef));
    1253     StoreElementType(peArc);
    1254     NeedSpace(sizeof(TArcDef));
    12552834    transformedArc.anticlockwise := arcDef.anticlockwise;
    12562835    transformedArc.startAngleRadCW := arcDef.startAngleRadCW;
     
    12592838    transformedArc.radius := arcDef.radius*FScale;
    12602839    transformedArc.xAngleRadCW := arcDef.xAngleRadCW+FAngleRadCW;
    1261     PArcDef(FData+FDataPos)^ := transformedArc;
    1262     inc(FDataPos, sizeof(TArcDef));
    1263     FLastCoord := ArcEndPoint(arcDef);
    1264   end;
    1265 end;
    1266 
    1267 procedure TBGRAPath.arc(cx, cy, rx, ry, xAngleRadCW, startAngleRadCW,
     2840    PArcElement(AllocateElement(peArc))^ := transformedArc;
     2841        {$PUSH}{$OPTIMIZATION OFF}
     2842    SetLastCoord(ArcEndPoint(arcDef));
     2843        {$POP}
     2844  end;
     2845end;
     2846
     2847procedure TBGRAPath.arc(cx, cy, rx, ry: single; xAngleRadCW, startAngleRadCW,
    12682848  endAngleRadCW: single);
    12692849begin
     
    12802860  anticlockwise: boolean; x, y: single);
    12812861begin
    1282   if isEmptyPointF(FLastCoord) then
     2862  if IsEmptyPointF(FLastCoord) then
    12832863    moveTo(x,y)
    12842864  else
     
    12872867
    12882868procedure TBGRAPath.copyTo(dest: IBGRAPath);
    1289 var savedPos: integer;
    1290     cp1,cp2,p1: TPointF;
    1291 begin
    1292   savedPos:= FDataPos;
    1293   FDataPos := 0;
    1294   while FDataPos < savedPos do
    1295   begin
    1296     case ReadElementType of
    1297     peMoveTo: dest.moveTo(ReadCoord);
    1298     peLineTo: dest.lineTo(ReadCoord);
    1299     peCloseSubPath: dest.closePath;
    1300     peQuadraticBezierTo:
    1301       begin
    1302         cp1 := ReadCoord;
    1303         p1 := ReadCoord;
    1304         dest.quadraticCurveTo(cp1,p1);
    1305       end;
    1306     peCubicBezierTo:
    1307       begin
    1308         cp1 := ReadCoord;
    1309         cp2 := ReadCoord;
    1310         p1 := ReadCoord;
    1311         dest.bezierCurveTo(cp1,cp2,p1);
    1312       end;
    1313     peArc: dest.arc(ReadArcDef);
    1314     end;
    1315   end;
    1316   FDataPos := savedPos;
     2869var pos: IntPtr;
     2870    elemType: TBGRAPathElementType;
     2871    elem: Pointer;
     2872    pts: array of TPointF;
     2873begin
     2874  pos := 0;
     2875  repeat
     2876    GetElementAt(pos, elemType, elem);
     2877    if elem = nil then break;
     2878    case elemType of
     2879      peMoveTo: dest.moveTo(PPointF(elem)^);
     2880      peLineTo: dest.lineTo(PPointF(elem)^);
     2881      peCloseSubPath: dest.closePath;
     2882      peQuadraticBezierTo:
     2883        with PQuadraticBezierToElement(elem)^ do
     2884          dest.quadraticCurveTo(ControlPoint,Destination);
     2885      peCubicBezierTo:
     2886        with PCubicBezierToElement(elem)^ do
     2887          dest.bezierCurveTo(ControlPoint1,ControlPoint2,Destination);
     2888      peArc: dest.arc(PArcElement(elem)^);
     2889      peOpenedSpline, peClosedSpline:
     2890        begin
     2891          with PSplineElement(elem)^ do
     2892          begin
     2893            setlength(pts, NbControlPoints);
     2894            move(Pointer(PSplineElement(elem)+1)^, pts[0], NbControlPoints*sizeof(TPointF));
     2895            if elemType = peOpenedSpline then
     2896              dest.openedSpline(pts, SplineStyle)
     2897            else
     2898              dest.closedSpline(pts, SplineStyle);
     2899            pts := nil;
     2900          end;
     2901        end;
     2902    end;
     2903  until not GoToNextElement(pos);
    13172904end;
    13182905
  • GraphicTest/Packages/bgrabitmap/bgrapen.pas

    r472 r494  
    1212
    1313uses
    14   SysUtils, Graphics, BGRABitmapTypes;
     14  SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform;
    1515
    1616var   //predefined pen styles
     
    1818
    1919type
     20
     21  { TBGRAPenStroker }
     22
     23  TBGRAPenStroker = class(TBGRACustomPenStroker)
     24    protected
     25      { Pen style can be defined by PenStyle property of by CustomPenStyle property.
     26      When PenStyle property is assigned, CustomPenStyle property is assigned the actual
     27      pen pattern. }
     28      FCustomPenStyle: TBGRAPenStyle;
     29      FPenStyle: TPenStyle;
     30      FArrow: TBGRACustomArrow;
     31      FArrowOwned: boolean;
     32      FOriginalStrokeMatrix,FStrokeMatrix,FStrokeMatrixInverse: TAffineMatrix;
     33      FStrokeZoom: single;
     34      FStrokeMatrixIdentity: boolean;
     35      FLineCap: TPenEndCap;
     36      FJoinStyle: TPenJoinStyle;
     37      FMiterLimit: single;
     38
     39      function GetArrow: TBGRACustomArrow; override;
     40      function GetArrowOwned: boolean; override;
     41      function GetCustomPenStyle: TBGRAPenStyle; override;
     42      function GetJoinStyle: TPenJoinStyle; override;
     43      function GetLineCap: TPenEndCap; override;
     44      function GetMiterLimit: single; override;
     45      function GetPenStyle: TPenStyle; override;
     46      function GetStrokeMatrix: TAffineMatrix; override;
     47      procedure SetArrow(AValue: TBGRACustomArrow); override;
     48      procedure SetArrowOwned(AValue: boolean); override;
     49      procedure SetCustomPenStyle(AValue: TBGRAPenStyle); override;
     50      procedure SetJoinStyle(AValue: TPenJoinStyle); override;
     51      procedure SetLineCap(AValue: TPenEndCap); override;
     52      procedure SetMiterLimit(AValue: single); override;
     53      procedure SetPenStyle(AValue: TPenStyle); override;
     54      procedure SetStrokeMatrix(const AValue: TAffineMatrix); override;
     55    public
     56      constructor Create;
     57      destructor Destroy; override;
     58      function ComputePolyline(const APoints: array of TPointF; AWidth: single; AClosedCap: boolean = true): ArrayOfTPointF; override;
     59      function ComputePolyline(const APoints: array of TPointF; AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean = true): ArrayOfTPointF; override;
     60      function ComputePolylineAutocycle(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override;
     61      function ComputePolygon(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override;
     62
     63  end;
     64
    2065  TBGRAPolyLineOption = (plRoundCapOpen, //specifies that the line ending is opened
    2166                         plCycle,        //specifies that it is a polygon
     
    2671  TComputeArrowHeadProc = function(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF of object;
    2772
    28 { Draw a polyline with specified parameters. If a scanner is specified, it is used as a texture.
    29   Else the pencolor parameter is used as a solid color. }
    30 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF;
    31      width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    32      options: TBGRAPolyLineOptions; scan: IBGRAScanner = nil; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0);
    33 
    3473{ Compute the path for a polyline }
    3574function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    3675          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    37           options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; wantedStartArrowPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; WantedEndArrowPos: single = 0): ArrayOfTPointF;
     76          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF;
    3877
    3978{ Compute the path for a poly-polyline }
    4079function ComputeWidePolyPolylinePoints(const linepts: array of TPointF; width: single;
    4180          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    42           options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0): ArrayOfTPointF;
     81          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF;
    4382
    4483{--------------------- Pixel line procedures --------------------------}
     
    106145    end;
    107146    dest.VertLine(X1,Y1,Y2,c, ADrawMode);
     147        Exit;
    108148  end;
    109149
     
    690730  styleLength := 0;
    691731  styleIndex := -1;
     732  remainingDash := 0;
     733  betweenDash   := false;
    692734  for i := 0 to high(penstyle) do
    693735    if penstyle[i] <= 0 then
     
    745787end;
    746788
    747 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF; width: single;
    748           pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    749           options: TBGRAPolyLineOptions; scan: IBGRAScanner; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single);
    750 var
    751   widePolylinePoints: ArrayOfTPointF;
    752 begin
    753   widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
    754   if scan <> nil then
    755     bmp.FillPolyAntialias(widePolylinePoints,scan)
    756   else
    757     bmp.FillPolyAntialias(widePolylinePoints,pencolor);
    758 end;
    759 
    760789function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    761790          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    762           options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; wantedStartArrowPos: single; arrowEnd: TComputeArrowHeadProc; wantedEndArrowPos: single): ArrayOfTPointF;
     791          options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF;
     792const oneOver512 = 1/512;
    763793var
    764794  startArrowPos, startArrowDir, endArrowPos, endArrowDir: TPointF;
     
    9861016    hasStart,hasEnd: boolean;
    9871017  begin
    988     if assigned(arrowStart) and not isEmptyPointF(startArrowPos) then
    989       arrowStartData := arrowStart(startArrowPos, startArrowDir, width, startArrowLinePos)
     1018    if assigned(arrow) and not isEmptyPointF(startArrowPos) then
     1019      arrowStartData := arrow.ComputeStartAt(startArrowPos, startArrowDir, width, startArrowLinePos)
    9901020    else
    9911021      arrowStartData := nil;
    992     if assigned(arrowEnd) and not isEmptyPointF(endArrowPos) then
    993       arrowEndData := arrowEnd(endArrowPos, endArrowDir, width, endArrowLinePos)
     1022    if assigned(arrow) and not isEmptyPointF(endArrowPos) then
     1023      arrowEndData := arrow.ComputeEndAt(endArrowPos, endArrowDir, width, endArrowLinePos)
    9941024    else
    9951025      arrowEndData := nil;
     
    10331063  linePos: single;
    10341064  startArrowDone,endArrowDone: boolean;
     1065  wantedStartArrowPos,wantedEndArrowPos: single;
    10351066
    10361067begin
     
    10421073    if isEmptyPointF(linepts[i]) then
    10431074    begin
    1044       result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit);
     1075      result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow);
    10451076      exit;
    10461077    end;
     
    10551086  pjsMiter: if miterLimit < 1.001 then maxMiter := hw*1.001 else
    10561087               maxMiter := hw*miterLimit;
     1088  else
     1089    raise Exception.Create('Unknown join style');
    10571090  end;
    10581091
     
    10621095  setlength(pts, length(linepts)+2);
    10631096  for i := 0 to high(linepts) do
    1064     if (nbPts = 0) or (linepts[i] <> pts[nbPts-1]) then
     1097    if (nbPts = 0) or (abs(linepts[i].x-pts[nbPts-1].x)>oneOver512) or (abs(linepts[i].y-pts[nbPts-1].y)>oneOver512) then
    10651098    begin
    10661099      pts[nbPts]:= linePts[i];
    10671100      inc(nbPts);
    10681101    end;
    1069   if (nbPts > 1) and
    1070       (pts[nbPts-1] = pts[0]) then dec(nbPts);
     1102  if (nbPts > 1) and (plCycle in options) and
     1103      (abs(pts[0].x-pts[nbPts-1].x)<=oneOver512) and
     1104      (abs(pts[0].y-pts[nbPts-1].y)<=oneOver512) then dec(nbPts);
    10711105  if (plCycle in options) and (nbPts > 2) then
    10721106  begin
     
    10781112    pts[nbPts] := pts[1];
    10791113    inc(nbPts);
    1080     linecap := pecRound;
    10811114  end else
    10821115    options -= [plCycle];
     
    10951128  endArrowDir := EmptyPointF;
    10961129  endArrowPos := EmptyPointF;
    1097   startArrowDone := @arrowStart = nil;
    1098   endArrowDone := @arrowEnd = nil;
     1130  if Assigned(arrow) then
     1131  begin
     1132    wantedStartArrowPos:= arrow.StartOffsetX;
     1133    wantedEndArrowPos:= arrow.EndOffsetX;
     1134    startArrowDone := not arrow.IsStartDefined;
     1135    endArrowDone := not arrow.IsEndDefined;
     1136  end
     1137  else
     1138  begin
     1139    wantedStartArrowPos:= 0;
     1140    wantedEndArrowPos:= 0;
     1141    startArrowDone := true;
     1142    endArrowDone := true;
     1143  end;
    10991144
    11001145  //init computed points arrays
     
    14111456  width: single; pencolor: TBGRAPixel; linecap: TPenEndCap;
    14121457  joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    1413   options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single): ArrayOfTPointF;
     1458  options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF;
    14141459
    14151460var
     
    14281473      for j := startIndex to endIndexP1-1 do
    14291474        subPts[j-startIndex] := linepts[j];
    1430       tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
     1475      tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow);
    14311476      if length(results) = nbresults then
    14321477        setlength(results,(nbresults+1)*2);
     
    14701515end;
    14711516
     1517{ TBGRAPenStroker }
     1518
     1519function TBGRAPenStroker.GetArrow: TBGRACustomArrow;
     1520begin
     1521  result := FArrow;
     1522end;
     1523
     1524function TBGRAPenStroker.GetArrowOwned: boolean;
     1525begin
     1526  result := FArrowOwned;
     1527end;
     1528
     1529function TBGRAPenStroker.GetCustomPenStyle: TBGRAPenStyle;
     1530begin
     1531  result := FCustomPenStyle;
     1532end;
     1533
     1534function TBGRAPenStroker.GetJoinStyle: TPenJoinStyle;
     1535begin
     1536  result := FJoinStyle;
     1537end;
     1538
     1539function TBGRAPenStroker.GetLineCap: TPenEndCap;
     1540begin
     1541  result := FLineCap;
     1542end;
     1543
     1544function TBGRAPenStroker.GetMiterLimit: single;
     1545begin
     1546  result := FMiterLimit;
     1547end;
     1548
     1549function TBGRAPenStroker.GetPenStyle: TPenStyle;
     1550begin
     1551  result := FPenStyle;
     1552end;
     1553
     1554function TBGRAPenStroker.GetStrokeMatrix: TAffineMatrix;
     1555begin
     1556  result := FOriginalStrokeMatrix;
     1557end;
     1558
     1559procedure TBGRAPenStroker.SetArrow(AValue: TBGRACustomArrow);
     1560begin
     1561  FArrow := AValue;
     1562end;
     1563
     1564procedure TBGRAPenStroker.SetArrowOwned(AValue: boolean);
     1565begin
     1566  FArrowOwned := AValue;
     1567end;
     1568
     1569procedure TBGRAPenStroker.SetCustomPenStyle(AValue: TBGRAPenStyle);
     1570begin
     1571  if FCustomPenStyle=AValue then Exit;
     1572  FCustomPenStyle:=AValue;
     1573  if AValue = SolidPenStyle then FPenStyle := psSolid
     1574  else if AValue = ClearPenStyle then FPenStyle:= psClear
     1575  else if AValue = DashPenStyle then FPenStyle:= psDash
     1576  else if AValue = DotPenStyle then FPenStyle := psDot
     1577  else if AValue = DashDotPenStyle then FPenStyle:= psDashDot
     1578  else if AValue = DashDotDotPenStyle then FPenStyle:= psDashDotDot
     1579  else
     1580  begin
     1581    FPenStyle := psPattern;
     1582    FCustomPenStyle:= DuplicatePenStyle(AValue);
     1583  end;
     1584end;
     1585
     1586procedure TBGRAPenStroker.SetJoinStyle(AValue: TPenJoinStyle);
     1587begin
     1588  FJoinStyle:= AValue;
     1589end;
     1590
     1591procedure TBGRAPenStroker.SetLineCap(AValue: TPenEndCap);
     1592begin
     1593  FLineCap:= AValue;
     1594end;
     1595
     1596procedure TBGRAPenStroker.SetMiterLimit(AValue: single);
     1597begin
     1598  FMiterLimit := AValue;
     1599end;
     1600
     1601procedure TBGRAPenStroker.SetStrokeMatrix(const AValue: TAffineMatrix);
     1602begin
     1603  if FOriginalStrokeMatrix=AValue then Exit;
     1604  FOriginalStrokeMatrix:=AValue;
     1605  FStrokeMatrix := AValue;
     1606  FStrokeMatrix[1,3] := 0;
     1607  FStrokeMatrix[2,3] := 0;
     1608  FStrokeZoom := max(VectLen(PointF(FStrokeMatrix[1,1],FStrokeMatrix[2,1])),
     1609          VectLen(PointF(FStrokeMatrix[1,2],FStrokeMatrix[2,2])));
     1610  if FStrokeZoom > 0 then
     1611    FStrokeMatrix *= AffineMatrixScale(1/FStrokeZoom,1/FStrokeZoom);
     1612  FStrokeMatrixIdentity := IsAffineMatrixIdentity(FStrokeMatrix);
     1613  FStrokeMatrixInverse := AffineMatrixInverse(FStrokeMatrix);
     1614end;
     1615
     1616procedure TBGRAPenStroker.SetPenStyle(AValue: TPenStyle);
     1617begin
     1618  if FPenStyle=AValue then Exit;
     1619  Case AValue of
     1620  psSolid: FCustomPenStyle := SolidPenStyle;
     1621  psDash: FCustomPenStyle := DashPenStyle;
     1622  psDot: FCustomPenStyle := DotPenStyle;
     1623  psDashDot: FCustomPenStyle := DashDotPenStyle;
     1624  psDashDotDot: FCustomPenStyle := DashDotDotPenStyle;
     1625  else FCustomPenStyle := ClearPenStyle;
     1626  end;
     1627  FPenStyle := AValue;
     1628end;
     1629
     1630constructor TBGRAPenStroker.Create;
     1631begin
     1632  Style := psSolid;
     1633  LineCap := pecRound;
     1634  JoinStyle := pjsBevel;
     1635  MiterLimit := 2;
     1636  fillchar(FOriginalStrokeMatrix,sizeof(FOriginalStrokeMatrix),0);
     1637  StrokeMatrix := AffineMatrixIdentity;
     1638end;
     1639
     1640destructor TBGRAPenStroker.Destroy;
     1641begin
     1642  if ArrowOwned then FreeAndNil(FArrow);
     1643  inherited Destroy;
     1644end;
     1645
     1646function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF;
     1647  AWidth: single; AClosedCap: boolean): ArrayOfTPointF;
     1648var
     1649  c: TBGRAPixel;
     1650begin
     1651  if not AClosedCap then
     1652    c := BGRAWhite //needed for alpha junction
     1653  else
     1654    c := BGRAPixelTransparent;
     1655
     1656  if FStrokeMatrixIdentity then
     1657    result := ComputePolyline(APoints,AWidth*FStrokeZoom,c,AClosedCap)
     1658  else
     1659    result := FStrokeMatrix*ComputePolyline(FStrokeMatrixInverse*APoints,AWidth*FStrokeZoom,c,AClosedCap);
     1660end;
     1661
     1662function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF;
     1663  AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean): ArrayOfTPointF;
     1664var options: TBGRAPolyLineOptions;
     1665begin
     1666  options := [];
     1667  if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap];
     1668  if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap];
     1669  if not AClosedCap then options += [plRoundCapOpen];
     1670  if FStrokeMatrixIdentity then
     1671    result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow)
     1672  else
     1673    result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow);
     1674end;
     1675
     1676function TBGRAPenStroker.ComputePolylineAutocycle(
     1677  const APoints: array of TPointF; AWidth: single): ArrayOfTPointF;
     1678var options: TBGRAPolyLineOptions;
     1679begin
     1680  options := [plAutoCycle];
     1681  if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap];
     1682  if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap];
     1683  if FStrokeMatrixIdentity then
     1684    result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow)
     1685  else
     1686    result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow)
     1687end;
     1688
     1689function TBGRAPenStroker.ComputePolygon(const APoints: array of TPointF;
     1690  AWidth: single): ArrayOfTPointF;
     1691begin
     1692  if FStrokeMatrixIdentity then
     1693    result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit)
     1694  else
     1695    result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit);
     1696end;
     1697
    14721698initialization
    14731699
  • GraphicTest/Packages/bgrabitmap/bgrapolygon.pas

    r472 r494  
    3333
    3434uses
    35   Classes, SysUtils, Graphics, BGRABitmapTypes, BGRAFillInfo;
     35  Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRAFillInfo, BGRAPath;
    3636
    3737procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
     
    5959    procedure AddShape(AInfo: TBGRACustomFillInfo; AInternalInfo: boolean; ATexture: IBGRAScanner; AInternalTexture: TObject; AColor: TBGRAPixel);
    6060    function CheckRectangleBorderBounds(var x1, y1, x2, y2: single; w: single): boolean;
     61    procedure InternalAddStroke(const APoints: array of TPointF; AClosed: boolean; AData: Pointer);
    6162  public
    6263    FillMode : TFillMode;
     
    7071    procedure AddPolygon(const points: array of TPointF; AColor: TBGRAPixel);
    7172    procedure AddPolygon(const points: array of TPointF; ATexture: IBGRAScanner);
     73    procedure AddPathStroke(APath: TBGRAPath; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker);
     74    procedure AddPathStroke(APath: TBGRAPath; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker);
     75    procedure AddPathStroke(APath: TBGRAPath; AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker);
     76    procedure AddPathStroke(APath: TBGRAPath; AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker);
     77    procedure AddPathFill(APath: TBGRAPath; AColor: TBGRAPixel);
     78    procedure AddPathFill(APath: TBGRAPath; ATexture: IBGRAScanner);
     79    procedure AddPathFill(APath: TBGRAPath; AMatrix: TAffineMatrix; AColor: TBGRAPixel);
     80    procedure AddPathFill(APath: TBGRAPath; AMatrix: TAffineMatrix; ATexture: IBGRAScanner);
     81    procedure AddPolylineStroke(const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker);
     82    procedure AddPolylineStroke(const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker);
     83    procedure AddPolygonStroke(const points: array of TPointF; AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker);
     84    procedure AddPolygonStroke(const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker);
    7285    procedure AddTriangleLinearColor(pt1, pt2, pt3: TPointF; c1, c2, c3: TBGRAPixel);
    7386    procedure AddTriangleLinearMapping(pt1, pt2, pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF);
    7487    procedure AddQuadLinearColor(pt1, pt2, pt3, pt4: TPointF; c1, c2, c3, c4: TBGRAPixel);
    75     procedure AddQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
     88    procedure AddQuadLinearMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, {%H-}tex3, tex4: TPointF;
     89       ACulling: TFaceCulling = fcNone);
    7690    procedure AddQuadPerspectiveMapping(pt1, pt2, pt3, pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
    7791    procedure AddEllipse(x, y, rx, ry: single; AColor: TBGRAPixel);
     
    126140uses Math, BGRABlend, BGRAGradientScanner, BGRATransform;
    127141
     142type
     143  TPathStrokeData = record
     144    Stroker: TBGRACustomPenStroker;
     145    Texture: IBGRAScanner;
     146    Color: TBGRAPixel;
     147    Width: Single;
     148  end;
     149
    128150procedure FillShapeAntialias(bmp: TBGRACustomBitmap; shapeInfo: TBGRACustomFillInfo;
    129151  c: TBGRAPixel; EraseMode: boolean; scan: IBGRAScanner; NonZeroWinding: boolean; LinearBlend: boolean);
     152const oneOver512 = 1/512;
    130153var
    131154  inter:   array of TIntersectionInfo;
     
    135158    inter:   array of TIntersectionInfo;
    136159    nbInter: integer;
     160    sliceIndex: integer;
    137161  end;
    138162
     
    230254begin
    231255  if (scan=nil) and (c.alpha=0) then exit;
    232   If not shapeInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     256  If not BGRAShapeComputeMinMax(shapeInfo,minx,miny,maxx,maxy,bmp) then exit;
    233257
    234258  inter := shapeInfo.CreateIntersectionArray;
     
    265289    begin
    266290      with firstScan do
     291      begin
    267292        shapeInfo.ComputeAndSort(yb+1/256,inter,nbInter,NonZeroWinding);
     293        sliceIndex:= shapeInfo.GetSliceIndex;
     294      end;
    268295      with lastScan do
     296      begin
    269297        shapeInfo.ComputeAndSort(yb+255/256,inter,nbInter,NonZeroWinding);
    270       if (firstScan.nbInter = lastScan.nbInter) and (firstScan.nbInter >= 2) then
     298        sliceIndex:= shapeInfo.GetSliceIndex;
     299      end;
     300      if (firstScan.sliceIndex = lastScan.sliceIndex) and (firstScan.nbInter = lastScan.nbInter) then
    271301      begin
    272302        optimised := true;
     
    286316          x1 := firstScan.inter[i+i].interX;
    287317          x1b := lastScan.inter[i+i].interX;
    288           if (x1 > x1b) then
    289           begin
    290             temp := x1;
    291             x1 := x1b;
    292             x1b := temp;
    293           end;
    294318          x2 := firstScan.inter[i+i+1].interX;
    295319          x2b := lastScan.inter[i+i+1].interX;
    296           if (x2 < x2b) then
     320          if (abs(x1-x1b)<oneOver512) and (abs(x2-x2b)<oneOver512) and
     321             ((i+i+2 >= firstScan.nbInter) or
     322              ((firstScan.inter[i+i+2].interX >= x2+1) and
     323               (lastScan.inter[i+i+2].interX >= x2b+1))) then
    297324          begin
    298             temp := x2;
    299             x2 := x2b;
    300             x2b := temp;
     325            x1 := (x1+x1b)*0.5;
     326            x2 := (x2+x2b)*0.5;
     327            ix1 := floor(x1);
     328            ix2 := floor(x2);
     329            if ix1 < minx then ix1 := minx;
     330            if ix2 > maxx then ix2 := maxx;
     331            if ix1>ix2 then continue;
     332            if ix1=ix2 then
     333            begin
     334              tempDensity:= round((x2-x1)*256);
     335              if scan <> nil then //with texture scan
     336              begin
     337                scan.ScanMoveTo(ix1,yb);
     338                c := scan.ScanNextPixel;
     339                c.alpha := c.alpha*tempDensity shr 8;
     340                if linearBlend then
     341                  bmp.DrawPixel(ix1, yb, c, dmLinearBlend)
     342                else
     343                  bmp.DrawPixel(ix1, yb, c, dmDrawWithTransparency);
     344              end else
     345              if EraseMode then //erase with alpha
     346                bmp.ErasePixel(ix1,yb,c.alpha*tempDensity shr 8)
     347              else
     348              begin  //solid color
     349                c2.alpha := c.alpha*tempDensity shr 8;
     350                if linearBlend then
     351                  bmp.DrawPixel(ix1, yb, c2, dmLinearBlend)
     352                else
     353                  bmp.DrawPixel(ix1, yb, c2, dmDrawWithTransparency);
     354              end;
     355            end else
     356            begin
     357              tempDensity:= round((ix1+1-x1)*256);
     358              if scan <> nil then scan.ScanMoveTo(ix1,yb);
     359              if tempDensity < 256 then
     360              begin
     361                if scan <> nil then //with texture scan
     362                begin
     363                  c := scan.ScanNextPixel;
     364                  c.alpha := c.alpha*tempDensity shr 8;
     365                  if linearBlend then
     366                    bmp.DrawPixel(ix1, yb, c, dmLinearBlend)
     367                  else
     368                    bmp.DrawPixel(ix1, yb, c, dmDrawWithTransparency);
     369                end else
     370                if EraseMode then //erase with alpha
     371                  bmp.ErasePixel(ix1,yb, c.alpha*tempDensity shr 8)
     372                else
     373                begin  //solid color
     374                  c2.alpha := c.alpha*tempDensity shr 8;
     375                  if linearBlend then
     376                    bmp.DrawPixel(ix1, yb, c2, dmLinearBlend)
     377                  else
     378                    bmp.DrawPixel(ix1, yb, c2, dmDrawWithTransparency);
     379                end;
     380                inc(ix1);
     381              end;
     382              tempDensity:= round((x2-ix2)*256);
     383              if tempDensity < 256 then dec(ix2);
     384              if ix2 >= ix1 then
     385              begin
     386                if scan <> nil then //with texture scan
     387                begin
     388                  if linearBlend then
     389                    ScannerPutPixels(scan, bmp.ScanLine[yb] + ix1, ix2-ix1+1, dmLinearBlend)
     390                  else
     391                    ScannerPutPixels(scan, bmp.ScanLine[yb] + ix1, ix2-ix1+1, dmDrawWithTransparency);
     392                end else
     393                if EraseMode then //erase with alpha
     394                  bmp.EraseLine(ix1,yb,ix2,yb,c.alpha,True)
     395                else
     396                begin  //solid color
     397                  if LinearBlend then
     398                    bmp.HorizLine(ix1,yb,ix2,c,dmLinearBlend)
     399                  else
     400                    bmp.HorizLine(ix1,yb,ix2,c,dmDrawWithTransparency);
     401                end;
     402              end;
     403              if tempDensity < 256 then
     404              begin
     405                inc(ix2);
     406                if scan <> nil then //with texture scan
     407                begin
     408                  c := scan.ScanNextPixel;
     409                  c.alpha := c.alpha*tempDensity shr 8;
     410                  if linearBlend then
     411                    bmp.DrawPixel(ix2, yb, c, dmLinearBlend)
     412                  else
     413                    bmp.DrawPixel(ix2, yb, c, dmDrawWithTransparency);
     414                end else
     415                if EraseMode then //erase with alpha
     416                  bmp.ErasePixel(ix2,yb,c.alpha*tempDensity shr 8)
     417                else
     418                begin  //solid color
     419                  c2.alpha := c.alpha*tempDensity shr 8;
     420                  if linearBlend then
     421                    bmp.DrawPixel(ix2, yb, c2, dmLinearBlend)
     422                  else
     423                    bmp.DrawPixel(ix2, yb, c2, dmDrawWithTransparency);
     424                end;
     425              end;
     426            end;
     427            continue;
     428          end else
     429          begin
     430            if (x1 > x1b) then
     431            begin
     432              temp := x1;
     433              x1 := x1b;
     434              x1b := temp;
     435            end;
     436            if (x2 < x2b) then
     437            begin
     438              temp := x2;
     439              x2 := x2b;
     440              x2b := temp;
     441            end;
     442
     443            {$DEFINE INCLUDE_FILLDENSITY}
     444            {$DEFINE PARAM_SINGLESEGMENT}
     445            {$i density256.inc}
     446            SubTriangleDensity(x1,256,x1b,0);
     447            SubTriangleDensity(x2b,0,x2,256);
    301448          end;
    302           {$i filldensitysegment256.inc}
    303           SubTriangleDensity(x1,256,x1b,0);
    304           SubTriangleDensity(x2b,0,x2,256);
    305449        end;
    306450      end else
     
    311455          shapeInfo.ComputeAndSort(GetYScan(yc),inter,nbInter,NonZeroWinding);
    312456
    313           {$i filldensity256.inc}
     457          {$DEFINE INCLUDE_FILLDENSITY}
     458          {$i density256.inc}
    314459        end;
    315460      end;
     
    323468        shapeInfo.ComputeAndSort(GetYScan(yc),inter,nbInter,NonZeroWinding);
    324469
    325         {$i filldensity256.inc}
     470        {$DEFINE INCLUDE_FILLDENSITY}
     471        {$i density256.inc}
    326472      end;
    327473    end;
     
    330476    begin
    331477      if optimised then
     478        {$DEFINE INCLUDE_RENDERDENSITY}
    332479        {$define PARAM_LINEARANTIALIASING}
    333         {$i renderdensity256.inc}
     480        {$i density256.inc}
    334481      else
     482        {$DEFINE INCLUDE_RENDERDENSITY}
    335483        {$define PARAM_LINEARANTIALIASING}
    336484        {$define PARAM_ANTIALIASINGFACTOR}
    337         {$i renderdensity256.inc}
     485        {$i density256.inc}
    338486    end else
    339487    begin
    340488      if optimised then
    341         {$i renderdensity256.inc}
     489        {$DEFINE INCLUDE_RENDERDENSITY}
     490        {$i density256.inc}
    342491      else
     492        {$DEFINE INCLUDE_RENDERDENSITY}
    343493        {$define PARAM_ANTIALIASINGFACTOR}
    344         {$i renderdensity256.inc}
     494        {$i density256.inc}
    345495    end;
    346496  end;
     
    383533begin
    384534  if (scan=nil) and (c.alpha=0) then exit;
    385   If not shapeInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     535  If not BGRAShapeComputeMinMax(shapeInfo,minx,miny,maxx,maxy,bmp) then exit;
    386536  inter := shapeInfo.CreateIntersectionArray;
    387537
     
    532682  info: TFillBorderEllipseInfo;
    533683begin
    534   if (rx = 0) or (ry = 0) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then
     684  if ((rx = 0) and (ry = 0)) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then
    535685    exit;
    536686  info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w);
     
    544694  info: TFillBorderEllipseInfo;
    545695begin
    546   if (rx = 0) or (ry = 0) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then
     696  if ((rx = 0) and (ry = 0)) or (w=0) or (x = EmptySingle) or (y = EmptySingle) then
    547697    exit;
    548698  info := TFillBorderEllipseInfo.Create(x, y, rx, ry, w);
     
    587737end;
    588738
     739procedure TBGRAMultishapeFiller.InternalAddStroke(
     740  const APoints: array of TPointF; AClosed: boolean; AData: Pointer);
     741var pts: ArrayOfTPointF;
     742begin
     743  with TPathStrokeData(AData^) do
     744  begin
     745    if AClosed then
     746      pts := Stroker.ComputePolygon(APoints, Width)
     747    else
     748      pts := Stroker.ComputePolylineAutoCycle(APoints, Width);
     749    if Texture <> nil then
     750      AddPolygon(pts, Texture)
     751    else
     752      AddPolygon(pts, Color);
     753  end;
     754end;
     755
    589756constructor TBGRAMultishapeFiller.Create;
    590757begin
     
    635802end;
    636803
     804procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath;
     805  AColor: TBGRAPixel; AWidth: single; AStroker: TBGRACustomPenStroker);
     806begin
     807  AddPathStroke(APath,AffineMatrixIdentity,AColor,AWidth,AStroker);
     808end;
     809
     810procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath;
     811  ATexture: IBGRAScanner; AWidth: single; AStroker: TBGRACustomPenStroker);
     812begin
     813  AddPathStroke(APath,AffineMatrixIdentity,ATexture,AWidth,AStroker);
     814end;
     815
     816procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath;
     817  AMatrix: TAffineMatrix; AColor: TBGRAPixel; AWidth: single;
     818  AStroker: TBGRACustomPenStroker);
     819var data: TPathStrokeData;
     820begin
     821  data.Stroker := AStroker;
     822  data.Color := AColor;
     823  data.Texture := nil;
     824  data.Width := AWidth;
     825  APath.stroke(@InternalAddStroke, AMatrix, 0.1, @data);
     826end;
     827
     828procedure TBGRAMultishapeFiller.AddPathStroke(APath: TBGRAPath;
     829  AMatrix: TAffineMatrix; ATexture: IBGRAScanner; AWidth: single;
     830  AStroker: TBGRACustomPenStroker);
     831var data: TPathStrokeData;
     832begin
     833  data.Stroker := AStroker;
     834  data.Color := BGRAPixelTransparent;
     835  data.Texture := ATexture;
     836  data.Width := AWidth;
     837  APath.stroke(@InternalAddStroke, AMatrix, 0.1, @data);
     838end;
     839
     840procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath; AColor: TBGRAPixel);
     841begin
     842  AddPolygon(APath.ToPoints, AColor);
     843end;
     844
     845procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath;
     846  ATexture: IBGRAScanner);
     847begin
     848  AddPolygon(APath.ToPoints, ATexture);
     849end;
     850
     851procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath;
     852  AMatrix: TAffineMatrix; AColor: TBGRAPixel);
     853begin
     854  AddPolygon(APath.ToPoints(AMatrix), AColor);
     855end;
     856
     857procedure TBGRAMultishapeFiller.AddPathFill(APath: TBGRAPath;
     858  AMatrix: TAffineMatrix; ATexture: IBGRAScanner);
     859begin
     860  AddPolygon(APath.ToPoints(AMatrix), ATexture);
     861end;
     862
     863procedure TBGRAMultishapeFiller.AddPolylineStroke(
     864  const points: array of TPointF; AColor: TBGRAPixel; AWidth: single;
     865  AStroker: TBGRACustomPenStroker);
     866begin
     867  AddPolygon(AStroker.ComputePolyline(points,AWidth,AColor), AColor);
     868end;
     869
     870procedure TBGRAMultishapeFiller.AddPolylineStroke(
     871  const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single;
     872  AStroker: TBGRACustomPenStroker);
     873begin
     874  AddPolygon(AStroker.ComputePolyline(points,AWidth), ATexture);
     875end;
     876
     877procedure TBGRAMultishapeFiller.AddPolygonStroke(
     878  const points: array of TPointF; AColor: TBGRAPixel; AWidth: single;
     879  AStroker: TBGRACustomPenStroker);
     880begin
     881  AddPolygon(AStroker.ComputePolygon(points,AWidth), AColor);
     882end;
     883
     884procedure TBGRAMultishapeFiller.AddPolygonStroke(
     885  const points: array of TPointF; ATexture: IBGRAScanner; AWidth: single;
     886  AStroker: TBGRACustomPenStroker);
     887begin
     888  AddPolygon(AStroker.ComputePolygon(points,AWidth), ATexture);
     889end;
     890
    637891procedure TBGRAMultishapeFiller.AddTriangleLinearColor(pt1, pt2, pt3: TPointF; c1, c2,
    638892  c3: TBGRAPixel);
    639 var
    640   grad: TBGRAGradientTriangleScanner;
    641 begin
    642   grad := TBGRAGradientTriangleScanner.Create(pt1,pt2,pt3, c1,c2,c3);
    643   AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent);
     893var grad: TBGRAGradientTriangleScanner;
     894begin
     895  if (c1 = c2) and (c2 = c3) then
     896    AddPolygon([pt1,pt2,pt3],c1)
     897  else
     898  begin
     899    grad := TBGRAGradientTriangleScanner.Create(pt1,pt2,pt3, c1,c2,c3);
     900    AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3]),True,grad,grad,BGRAPixelTransparent);
     901  end;
    644902end;
    645903
     
    659917  centerColor: TBGRAPixel;
    660918begin
    661   center := (pt1+pt2+pt3+pt4)*(1/4);
    662   centerColor := GammaCompression( MergeBGRA(MergeBGRA(GammaExpansion(c1),GammaExpansion(c2)),
    663                     MergeBGRA(GammaExpansion(c3),GammaExpansion(c4))) );
    664   AddTriangleLinearColor(pt1,pt2,center, c1,c2,centerColor);
    665   AddTriangleLinearColor(pt2,pt3,center, c2,c3,centerColor);
    666   AddTriangleLinearColor(pt3,pt4,center, c3,c4,centerColor);
    667   AddTriangleLinearColor(pt4,pt1,center, c4,c1,centerColor);
     919  if (c1 = c2) and (c2 = c3) and (c3 = c4) then
     920    AddPolygon([pt1,pt2,pt3,pt4],c1)
     921  else
     922  begin
     923    center := (pt1+pt2+pt3+pt4)*(1/4);
     924    centerColor := GammaCompression( MergeBGRA(MergeBGRA(GammaExpansion(c1),GammaExpansion(c2)),
     925                      MergeBGRA(GammaExpansion(c3),GammaExpansion(c4))) );
     926    AddTriangleLinearColor(pt1,pt2,center, c1,c2,centerColor);
     927    AddTriangleLinearColor(pt2,pt3,center, c2,c3,centerColor);
     928    AddTriangleLinearColor(pt3,pt4,center, c3,c4,centerColor);
     929    AddTriangleLinearColor(pt4,pt1,center, c4,c1,centerColor);
     930  end;
    668931end;
    669932
    670933procedure TBGRAMultishapeFiller.AddQuadLinearMapping(pt1, pt2, pt3,
    671   pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF);
    672 var
    673   center: TPointF;
    674   centerTex: TPointF;
    675 begin
    676   center := (pt1+pt2+pt3+pt4)*(1/4);
    677   centerTex := (tex1+tex2+tex3+tex4)*(1/4);
    678   AddTriangleLinearMapping(pt1,pt2,center, texture,tex1,tex2,centerTex);
    679   AddTriangleLinearMapping(pt2,pt3,center, texture,tex2,tex3,centerTex);
    680   AddTriangleLinearMapping(pt3,pt4,center, texture,tex3,tex4,centerTex);
    681   AddTriangleLinearMapping(pt4,pt1,center, texture,tex4,tex1,centerTex);
     934  pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
     935  ACulling: TFaceCulling);
     936var
     937  mapping: TBGRAQuadLinearScanner;
     938begin
     939  mapping := TBGRAQuadLinearScanner.Create(texture,
     940    [tex1,tex2,tex3,tex4],
     941    [pt1,pt2,pt3,pt4]);
     942  mapping.Culling := ACulling;
     943  AddShape(TOnePassFillPolyInfo.Create([pt1,pt2,pt3,pt4]),True,mapping,mapping,BGRAPixelTransparent);
    682944end;
    683945
     
    8911153          end;
    8921154        end else
    893           {$I filldensity256.inc}
     1155                  {$DEFINE INCLUDE_FILLDENSITY}
     1156          {$i density256.inc}
    8941157      end;
    8951158
     
    9381201  for k := 0 to nbShapes-1 do
    9391202  begin
    940     If shapes[k].info.ComputeMinMax(minx,miny,maxx,maxy,dest) then
     1203    If BGRAShapeComputeMinMax(shapes[k].info,minx,miny,maxx,maxy,dest) then
    9411204    begin
    9421205      shapes[k].bounds := rect(minx,miny,maxx+1,maxy+1);
     
    12181481  EraseMode: boolean; LinearBlend: boolean);
    12191482var
    1220   info: TFillBorderRoundRectInfo;
    1221 begin
    1222   if (rx = 0) or (ry = 0) or (w=0) then exit;
     1483  info: TFillShapeInfo;
     1484  oldLinear: boolean;
     1485begin
     1486  if w=0 then exit;
     1487  if ((rx=0) or (ry=0)) and not EraseMode then
     1488  begin
     1489    oldLinear := bmp.LinearAntialiasing;
     1490    bmp.LinearAntialiasing := LinearBlend;
     1491    bmp.RectangleAntialias(x1,y1,x2,y2,c,w);
     1492    bmp.LinearAntialiasing := oldLinear;
     1493    exit;
     1494  end;
    12231495  info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options);
    12241496  FillShapeAntialias(bmp, info, c, EraseMode, nil, False, LinearBlend);
     
    12311503var
    12321504  info: TFillBorderRoundRectInfo;
    1233 begin
    1234   if (rx = 0) or (ry = 0) or (w=0) then exit;
     1505  oldLinear: Boolean;
     1506begin
     1507  if w=0 then exit;
     1508  if (rx=0) or (ry=0) then
     1509  begin
     1510    oldLinear := bmp.LinearAntialiasing;
     1511    bmp.LinearAntialiasing := LinearBlend;
     1512    bmp.RectangleAntialias(x1,y1,x2,y2,scan,w);
     1513    bmp.LinearAntialiasing := oldLinear;
     1514    exit;
     1515  end;
    12351516  info := TFillBorderRoundRectInfo.Create(x1, y1, x2,y2, rx, ry, w, options);
    12361517  FillShapeAntialiasWithTexture(bmp, info, scan, False, LinearBlend);
  • GraphicTest/Packages/bgrabitmap/bgrapolygonaliased.pas

    r472 r494  
    431431
    432432begin
    433   If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     433  If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit;
    434434  inter := polyInfo.CreateIntersectionArray;
    435435
     
    668668
    669669begin
    670   If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     670  If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit;
    671671  inter := polyInfo.CreateIntersectionArray;
    672672
     
    735735
    736736begin
    737   If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     737  If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit;
    738738
    739739  scanAtFunc := @texture.ScanAt;
  • GraphicTest/Packages/bgrabitmap/bgraqtbitmap.pas

    r452 r494  
    2828
    2929uses
    30   Classes, SysUtils, BGRADefaultBitmap, Graphics,
    31   GraphType;
     30  Classes, SysUtils, BGRALCLBitmap, Graphics,
     31  GraphType, BGRABitmapTypes;
    3232
    3333type
    3434  { TBGRAQtBitmap }
    3535
    36   TBGRAQtBitmap = class(TBGRADefaultBitmap)
     36  TBGRAQtBitmap = class(TBGRALCLBitmap)
    3737  private
    38     procedure SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;
     38    procedure SlowDrawTransparent(ABitmap: TBGRACustomBitmap;
    3939      ACanvas: TCanvas; ARect: TRect);
    4040  public
     
    4444    procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); override;
    4545    procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override;
    46     procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
    47       ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
    48       override;
    4946    procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); override;
    5047  end;
     
    5249implementation
    5350
    54 uses BGRABitmapTypes, LCLType,
     51uses LCLType,
    5552  LCLIntf, IntfGraphics,
    5653  qtobjects, qt4,
    5754  FPImage;
    5855
    59 procedure TBGRAQtBitmap.SlowDrawTransparent(ABitmap: TBGRADefaultBitmap;
     56procedure TBGRAQtBitmap.SlowDrawTransparent(ABitmap: TBGRACustomBitmap;
    6057  ACanvas: TCanvas; ARect: TRect);
    6158begin
     
    6663  AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
    6764var
    68   Temp: TBGRAPtrBitmap;
     65  Temp: TBGRALCLPtrBitmap;
    6966begin
    70   Temp := TBGRAPtrBitmap.Create(AWidth, AHeight, AData);
     67  Temp := TBGRALCLPtrBitmap.Create(AWidth, AHeight, AData);
    7168  Temp.LineOrder := ALineOrder;
    7269  SlowDrawTransparent(Temp, ACanvas, Rect);
     
    9592end;
    9693
    97 procedure TBGRAQtBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect;
    98   AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
    99 var
    100   Temp:     TBitmap;
    101   RawImage: TRawImage;
    102   BitmapHandle, MaskHandle: HBitmap;
    103   CreateSuccess: boolean;
    104 begin
    105   if (AHeight = 0) or (AWidth = 0) then
    106     exit;
    107 
    108   RawImage.Init;
    109   RawImage.Description.Init_BPP32_B8G8R8_BIO_TTB(AWidth, AHeight);
    110   RawImage.Description.LineOrder := ALineOrder;
    111   RawImage.Description.LineEnd := rileDWordBoundary;
    112   RawImage.Data     := PByte(AData);
    113   RawImage.DataSize := AWidth * AHeight * Sizeof(TBGRAPixel);
    114   CreateSuccess     := RawImage_CreateBitmaps(RawImage, BitmapHandle, MaskHandle, False);
    115 
    116   if not CreateSuccess then
    117     raise FPImageException.Create('Failed to create bitmap handle');
    118   Temp := TBitmap.Create;
    119   Temp.Handle := BitmapHandle;
    120   Temp.MaskHandle := MaskHandle;
    121   ACanvas.StretchDraw(Rect, Temp);
    122   Temp.Free;
    123 end;
    124 
    12594procedure TBGRAQtBitmap.GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer);
    12695var
     
    144113  SrcY     := y + Ofs.Y;
    145114
    146   {$warning QT: recheck this}
    147115  if (dcSource.vImage <> nil) and (dcSource.vImage.Handle <> nil) then
    148116  begin
     
    157125  end;
    158126
    159   (*
    160   gdk_window_copy_area(dcDest.Drawable, dcDest.GC, 0, 0, dcSource.Drawable,
    161     SrcX, SrcY, Width, Height);
    162   *)
    163127  LoadFromRawImage(bmp.RawImage, 255, True);
    164128  bmp.Free;
  • GraphicTest/Packages/bgrabitmap/bgrareadbmp.pas

    r472 r494  
    3636
    3737type
     38  TBMPTransparencyOption = (toAuto, toTransparent, toOpaque);
    3839
    3940  { TBGRAReaderBMP }
     
    5657      FOutputHeight: integer;
    5758      FOriginalHeight: Integer;
     59      FTransparencyOption: TBMPTransparencyOption;
    5860      FBuffer: packed array of byte;
    5961      FBufferPos, FBufferSize: integer;
    6062      FBufferStream: TStream;
     63      FHasAlphaValues: boolean;
    6164      // SetupRead will allocate the needed buffers, and read the colormap if needed.
    6265      procedure SetupRead(nPalette, nRowBits: Integer; Stream : TStream); virtual;
     
    7780      procedure CloseReadBuffer;
    7881      function GetNextBufferByte: byte;
     82      procedure MakeOpaque(Img: TFPCustomImage);
    7983    public
    8084      MinifyHeight,WantedHeight: integer;
     
    8387      property OriginalHeight: integer read FOriginalHeight;
    8488      property OutputHeight: integer read FOutputHeight;
     89      property TransparencyOption: TBMPTransparencyOption read FTransparencyOption write FTransparencyOption;
    8590  end;
    8691
    8792implementation
    88 
    89 uses dialogs;
    9093
    9194type
     
    117120end;
    118121
    119 Constructor TBGRAReaderBMP.create;
     122constructor TBGRAReaderBMP.Create;
    120123
    121124begin
    122125  inherited create;
    123 end;
    124 
    125 Destructor TBGRAReaderBMP.Destroy;
     126  FTransparencyOption := toTransparent;
     127end;
     128
     129destructor TBGRAReaderBMP.Destroy;
    126130
    127131begin
     
    130134end;
    131135
    132 Procedure TBGRAReaderBMP.FreeBufs;
     136procedure TBGRAReaderBMP.FreeBufs;
    133137
    134138begin
     
    405409    PrevSourceRow := SourceRow-SourceRowDelta;
    406410    if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then InitReadBuffer(Stream,2048);
     411    FHasAlphaValues:= false;
    407412    while SourceRow <> SourceLastRow+SourceRowDelta do
    408413    begin
     
    434439      if percent<>prevPercent then Progress(psRunning,percent,false,Rect,'',continue);
    435440    end;
     441    if not FHasAlphaValues and (TransparencyOption = toAuto) and (BFI.BitCount = 32) then
     442      MakeOpaque(Img);
    436443    Progress(psEnding,100,false,Rect,'',continue);
    437   except
    438     on ex:exception do
    439       ShowMessage(ex.Message);
     444  finally
     445    if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then CloseReadBuffer;
     446    FreeBufs;
    440447  end;
    441   if (BFI.Compression=BI_RLE8) or(BFI.Compression=BI_RLE4) then CloseReadBuffer;
    442   FreeBufs;
    443448end;
    444449
     
    602607Var
    603608  Column : Integer;
    604 
     609  c: TFPColor;
    605610begin
    606611  Case BFI.BitCount of
     
    628633          img.colors[Column,Row]:=ExpandColor(PLongWord(LineBuf)[Column])
    629634        else
    630           img.colors[Column,Row]:=RGBAToFPColor(PColorRGBA(LineBuf)[Column]);
     635        begin
     636          if FTransparencyOption = toOpaque then
     637            img.colors[Column,Row]:=RGBToFPColor(PColorRGB(PColorRGBA(LineBuf)+Column)^)
     638          else
     639          begin
     640            c := RGBAToFPColor(PColorRGBA(LineBuf)[Column]);
     641            if c.alpha <> 0 then FHasAlphaValues:= true;
     642            img.colors[Column,Row]:= c;
     643          end;
     644        end;
    631645    end;
    632646end;
     
    672686      for Column:=0 to img.Width-1 do
    673687      begin
    674         {$IFDEF ENDIAN_BIG}
    675         PDWord(PDest)^ := (PWord(PSrc)^ shl 16) or ((Psrc+2)^ shl 8) or $000000ff;
    676         {$ELSE}
    677         PDWord(PDest)^ := PWord(PSrc)^ or ((Psrc+2)^ shl 16) or $ff000000;
    678         {$ENDIF}
     688        PDest^ := BGRA((Psrc+2)^,(Psrc+1)^,(Psrc)^);
    679689        inc(PDest);
    680690        inc(PSrc,3);
     
    689699        inc(PDest);
    690700      end;
    691      end else Move(LineBuf^, PDest^, img.Width*SizeOf(TBGRAPixel));
     701     end else
     702     if FTransparencyOption = toOpaque then
     703     begin
     704       if TBGRAPixel_RGBAOrder then
     705       begin
     706        PSrc := LineBuf;
     707        for Column:=0 to img.Width-1 do
     708        begin
     709          PDest^:= BGRA((PSrc)^,(PSrc+1)^,(PSrc+2)^);
     710          inc(PDest);
     711          Inc(PSrc,4);
     712        end;
     713       end
     714       else
     715       begin
     716        PSrc := LineBuf;
     717        for Column:=0 to img.Width-1 do
     718        begin
     719          PDest^:= BGRA((PSrc+2)^,(PSrc+1)^,(PSrc+1)^);
     720          inc(PDest);
     721          Inc(PSrc,4);
     722        end;
     723       end;
     724     end else
     725     begin
     726       if TBGRAPixel_RGBAOrder then
     727       begin
     728        PSrc := LineBuf;
     729        for Column:=0 to img.Width-1 do
     730        begin
     731          PDest^:= BGRA((PSrc)^,(PSrc+1)^,(PSrc+2)^,(PSrc+3)^);
     732          if PDest^.alpha <> 0 then FHasAlphaValues:= true;
     733          inc(PDest);
     734          Inc(PSrc,4);
     735        end;
     736       end
     737       else
     738       begin
     739         PSrc := LineBuf;
     740         for Column:=0 to img.Width-1 do
     741         begin
     742           PDest^ := PBGRAPixel(PSrc)^;
     743           if PDest^.alpha <> 0 then FHasAlphaValues:= true;
     744           inc(PDest);
     745           Inc(PSrc,4);
     746         end;
     747       end;
     748     end;
    692749    end;
    693750end;
     
    741798end;
    742799
     800procedure TBGRAReaderBMP.MakeOpaque(Img: TFPCustomImage);
     801var c: TFPColor;
     802  x,y: NativeInt;
     803begin
     804  if Img is TBGRACustomBitmap then
     805    TBGRACustomBitmap(Img).AlphaFill(255)
     806  else
     807    for y := 0 to Img.Height-1 do
     808      for x := 0 to Img.Width-1 do
     809      begin
     810        c := Img.Colors[x,y];
     811        c.alpha := alphaOpaque;
     812        Img.Colors[x,y] := c;
     813      end;
     814end;
     815
    743816
    744817initialization
  • GraphicTest/Packages/bgrabitmap/bgrareadbmpmiomap.pas

    r472 r494  
    77uses
    88  Classes, SysUtils, FPimage, BGRABitmapTypes;
     9
     10const
     11  MioMapMagicValue = 'RL';
     12  MioMapTransparentColor = $F81F;
    913
    1014type
     
    2933  end;
    3034
     35function MioMapToBGRA(AColor: Word): TBGRAPixel;
     36function BGRAToMioMap(const AColor: TBGRAPixel): Word;
     37function MioMapToAlpha(AValue: Byte): Byte;
     38function AlphaToMioMap(AValue: Byte): Byte;
     39
    3140implementation
    3241
    3342uses bufstream;
     43
     44function MioMapToBGRA(AColor: Word): TBGRAPixel;
     45begin
     46  if AColor = MioMapTransparentColor then
     47    result := BGRAPixelTransparent
     48  else
     49    result := Color16BitToBGRA(AColor);
     50end;
     51
     52function BGRAToMioMap(const AColor: TBGRAPixel): Word;
     53begin
     54  if AColor.alpha < 7 then
     55    result := MioMapTransparentColor
     56  else
     57  begin
     58    result := BGRAToColor16Bit(AColor);
     59    if result = MioMapTransparentColor then dec(result);
     60  end;
     61end;
     62
     63function MioMapToAlpha(AValue: Byte): Byte;
     64begin
     65  result := AValue*255 div 32;
     66end;
     67
     68function AlphaToMioMap(AValue: Byte): Byte;
     69begin
     70  result := (AValue*32 + 64) div 255;
     71end;
    3472
    3573{ TBGRAReaderBmpMioMap }
     
    4179  fillchar({%H-}header,sizeof(header),0);
    4280  if stream.Read(header, sizeof(header))<> sizeof(header) then exit;
    43   if header.magic <> 'RL' then exit;
     81  if header.magic <> MioMapMagicValue then exit;
    4482  header.format:= LEtoN(header.format);
    4583  header.width:= LEtoN(header.width);
     
    64102  begin
    65103    colorValue := LEtoN(mioPalette[i]);
    66     if colorValue = $F81F then
    67       result[i] := BGRAPixelTransparent
    68     else
    69       result[i] := BGRA(   ((colorValue and $F800) shr 11)*255 div 31,
    70                            ((colorValue and $07e0) shr 5)*255 div 63,
    71                            (colorValue and $001f)*255 div 31);
     104    result[i] := MioMapToBGRA(colorValue);
    72105  end;
    73106  for i := nbColorsRead to nbColors-1 do
     
    78111    Stream.Read(alphaPalette[0],nbColors);
    79112    for i := 0 to nbColors-1 do
    80       if mioPalette[i] <> $F81F then
    81         result[i].alpha := alphaPalette[i]*255 div 32;
     113      if mioPalette[i] <> MioMapTransparentColor then
     114        result[i].alpha := MioMapToAlpha(alphaPalette[i]);
    82115  end;
    83116end;
  • GraphicTest/Packages/bgrabitmap/bgrareadgif.pas

    r472 r494  
    338338               Every := 4;
    339339             end;
    340          4 : begin
     340         else{4}
     341             begin
    341342               Row := 1;
    342343               Every := 2;
  • GraphicTest/Packages/bgrabitmap/bgrareadico.pas

    r472 r494  
    22
    33{$mode objfpc}{$H+}
     4{$i bgrabitmap.inc}
    45
    56interface
     
    1415  TBGRAReaderIco = class(TFPCustomImageReader)
    1516  protected
    16     procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
     17    procedure InternalRead({%H-}Str: TStream; {%H-}Img: TFPCustomImage); override;
    1718    function InternalCheck(Str: TStream): boolean; override;
    1819  public
     
    2223implementation
    2324
    24 uses BGRABitmapTypes, Graphics;
     25uses BGRABitmapTypes{$IFDEF BGRABITMAP_USE_LCL}, Graphics{$ENDIF};
    2526
    2627{ TBGRAReaderIco }
    2728
    2829procedure TBGRAReaderIco.InternalRead(Str: TStream; Img: TFPCustomImage);
     30{$IFDEF BGRABITMAP_USE_LCL}
    2931var ico: TIcon; i,bestIdx: integer;
    3032    height,width: word; format:TPixelFormat;
     
    5759    begin
    5860      ico.Current := bestIdx;
    59       with Img as TBGRACustomBitmap do
    60       begin
    61         SetSize(bestWidth,bestHeight);
    62         GetImageFromCanvas(ico.Canvas,0,0);
    63       end;
     61      (Img as TBGRACustomBitmap).Assign(ico);
    6462    end;
    6563  finally
     
    6765  end;
    6866end;
     67{$ELSE}
     68begin
     69  raise exception.create('Not implemented');
     70end;
     71{$ENDIF}
    6972
    7073function TBGRAReaderIco.InternalCheck(Str: TStream): boolean;
  • GraphicTest/Packages/bgrabitmap/bgrareadjpeg.pas

    r472 r494  
    1 unit bgrareadjpeg;
     1unit BGRAReadJpeg;
    22
    33{$mode objfpc}{$H+}
     
    99
    1010type
     11  TJPEGScale = FPReadJPEG.TJPEGScale;
     12  TJPEGReadPerformance = FPReadJPEG.TJPEGReadPerformance;
    1113
     14const
     15  jsFullSize = FPReadJPEG.jsFullSize;
     16  jsHalf = FPReadJPEG.jsHalf;
     17  jsQuarter = FPReadJPEG.jsQuarter;
     18  jsEighth = FPReadJPEG.jsEighth;
     19
     20  jpBestQuality = FPReadJPEG.jpBestQuality;
     21  jpBestSpeed = FPReadJPEG.jpBestSpeed;
     22
     23type
    1224  { TBGRAReaderJpeg }
    1325
  • GraphicTest/Packages/bgrabitmap/bgrareadlzp.pas

    r472 r494  
    296296                for x := w-1 downto 0 do
    297297                begin
    298                   {$IFDEF ENDIAN_LITTLE}
    299                   PDWord(PDest)^ := PCurBlue^ or (PCurGreen^ shl 8) or (PCurRed^ shl 16) or $ff000000;
    300                   {$ELSE}
    301                   PDWord(PDest)^ := (PCurBlue^ shl 24) or (PCurGreen^ shl 16) or (PCurRed^ shl 8) or $ff;
    302                   {$ENDIF}
     298                  PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^);
    303299                  inc(PCurBlue);
    304300                  inc(PCurGreen);
     
    317313              else
    318314              begin
    319                 {$IFDEF ENDIAN_LITTLE}
    320                 PDWord(PDest)^ := PCurBlue^ or (PCurGreen^ shl 8) or (PCurRed^ shl 16) or (PCurAlpha^ shl 24);
    321                 {$ELSE}
    322                 PDWord(PDest)^ := (PCurBlue^ shl 24) or (PCurGreen^ shl 16) or (PCurRed^ shl 8) or PCurAlpha^;
    323                 {$ENDIF}
     315                PDest^ := BGRA(PCurRed^,PCurGreen^,PCurBlue^,PCurAlpha^);
    324316                inc(PCurBlue);
    325317                inc(PCurGreen);
  • GraphicTest/Packages/bgrabitmap/bgrareadpng.pas

    r472 r494  
    5656      //CFmt : TColorFormat; // format of the colors to convert from
    5757      StartX,StartY, DeltaX,DeltaY, StartPass,EndPass : integer;  // number and format of passes
    58       FSwitchLine, FCurrentLine, FPreviousLine : pByteArray;
    5958      FPalette : TFPPalette;
    6059      FSetPixel : TSetPixelProc;
     
    10099      DataIndex : longword;
    101100      DataBytes : TColorData;
    102       function CurrentLine(x:longword) : byte;
    103       function PrevSample (x:longword): byte;
    104       function PreviousLine (x:longword) : byte;
    105       function PrevLinePrevSample (x:longword): byte;
    106101      procedure HandleChunk; virtual;
    107102      procedure HandlePalette; virtual;
     
    109104      function CalcX (relX:integer) : integer;
    110105      function CalcY (relY:integer) : integer;
    111       function CalcColor: TColorData;
     106      function CalcColor(const ScanLine : PByteArray): TColorData;
    112107      procedure HandleScanLine (const y : integer; const ScanLine : PByteArray); virtual;
    113108      procedure BGRAHandleScanLine(const y: integer; const ScanLine: PByteArray);
    114109      procedure BGRAHandleScanLineTr(const y: integer; const ScanLine: PByteArray);
    115110      procedure DoDecompress; virtual;
    116       function  DoFilter(LineFilter:byte;index:longword; b:byte) : byte; virtual;
    117111      procedure SetPalettePixel (x,y:integer; const CD : TColordata);
    118112      procedure SetPalColPixel (x,y:integer; const CD : TColordata);
     
    381375end;
    382376
    383 function TBGRAReaderPNG.CurrentLine(x:longword):byte;
    384 begin
    385   result := FCurrentLine^[x];
    386 end;
    387 
    388 function TBGRAReaderPNG.PrevSample (x:longword): byte;
    389 begin
    390   if x < byteWidth then
    391     result := 0
    392   else
    393     result := FCurrentLine^[x - bytewidth];
    394 end;
    395 
    396 function TBGRAReaderPNG.PreviousLine (x:longword) : byte;
    397 begin
    398   result := FPreviousline^[x];
    399 end;
    400 
    401 function TBGRAReaderPNG.PrevLinePrevSample (x:longword): byte;
    402 begin
    403   if x < byteWidth then
    404     result := 0
    405   else
    406     result := FPreviousLine^[x - bytewidth];
    407 end;
    408 
    409 function TBGRAReaderPNG.DoFilter(LineFilter:byte;index:longword; b:byte) : byte;
    410 var diff : byte;
    411   procedure FilterSub;
    412   begin
    413     diff := PrevSample(index);
    414   end;
    415   procedure FilterUp;
    416   begin
    417     diff := PreviousLine(index);
    418   end;
    419   procedure FilterAverage;
    420   var l, p : word;
    421   begin
    422     l := PrevSample(index);
    423     p := PreviousLine(index);
    424     diff := (l + p) div 2;
    425   end;
    426   procedure FilterPaeth;
    427   var dl, dp, dlp : word; // index for previous and distances for:
    428       l, p, lp : byte;  // r:predictor, Left, Previous, LeftPrevious
    429       r : integer;
    430   begin
    431     l := PrevSample(index);
    432     lp := PrevLinePrevSample(index);
    433     p := PreviousLine(index);
    434     r := integer(l) + integer(p) - integer(lp);
    435     dl := abs (r - l);
    436     dlp := abs (r - lp);
    437     dp := abs (r - p);
    438     if (dl <= dp) and (dl <= dlp) then
    439       diff := l
    440     else if dp <= dlp then
    441       diff := p
    442     else
    443       diff := lp;
    444   end;
    445 begin
    446   case LineFilter of
    447     0 : diff := 0;
    448     1 : FilterSub;
    449     2 : FilterUp;
    450     3 : FilterAverage;
    451     4 : FilterPaeth;
    452   end;
    453   result := (b + diff) mod $100;
    454 end;
    455 
    456377function TBGRAReaderPNG.DecideSetPixel : TSetPixelProc;
    457378begin
     
    488409end;
    489410
    490 function TBGRAReaderPNG.CalcColor: TColorData;
     411function TBGRAReaderPNG.CalcColor(const ScanLine : PByteArray): TColorData;
    491412var cd : longword;
    492413    r : word;
     
    499420      begin
    500421       p := @Databytes;
    501        p^ := 0;
    502        for r:=0 to bytewidth-2 do
     422       for r:=0 to bytewidth shr 1 - 1 do
    503423       begin
    504         inc(p);
    505         p^:=FCurrentLine^[Dataindex+r];
     424        p^ := ScanLine^[Dataindex+(r shl 1)+1];
     425        (p+1)^ := ScanLine^[Dataindex+(r shl 1)];
     426        inc(p,2);
    506427       end;
    507428      end
    508     else move (FCurrentLine^[DataIndex], Databytes, bytewidth);
     429    else move (ScanLine^[DataIndex], Databytes, bytewidth);
    509430    {$IFDEF ENDIAN_BIG}
    510431    Databytes:=swap(Databytes);
     
    586507  for rx := 0 to ScanlineLength[CurrentPass]-1 do
    587508    begin
    588     c := CalcColor;
     509    c := CalcColor(ScanLine);
    589510    FSetPixel (x,y,c);
    590511    Inc(X, deltaX);
     
    666587  for rx := 0 to ScanlineLength[CurrentPass]-1 do
    667588    begin
    668     c := CalcColor;
     589    c := CalcColor(ScanLine);
    669590    FSetPixel (x,y,c);
    670591    Inc(X, deltaX);
     
    767688  for rx := 0 to ScanlineLength[CurrentPass]-1 do
    768689    begin
    769     c := CalcColor;
     690    c := CalcColor(ScanLine);
    770691    FSetPixel (x,y,c);
    771692    Inc(X, deltaX);
     
    934855  c := c + (c shl 2);
    935856  c := c + (c shl 4);
    936   with result do
    937     begin
    938     red := c;
    939     green := c;
    940     blue := c;
    941     alpha := 255;
    942     end;
     857  result := BGRA(c,c,c);
    943858end;
    944859
     
    948863  c := CD and $F;
    949864  c := c + (c shl 4);
    950   with result do
    951     begin
    952     red := c;
    953     green := c;
    954     blue := c;
    955     alpha := 255;
    956     end;
     865  result := BGRA(c,c,c);
    957866end;
    958867
     
    961870begin
    962871  c := CD and $FF;
    963   with result do
    964     begin
    965     red := c;
    966     green := c;
    967     blue := c;
    968     alpha := 255;
    969     end;
     872  result := BGRA(c,c,c);
    970873end;
    971874
     
    974877begin
    975878  c := (CD shr 8) and $FF;
    976   with result do
    977     begin
    978     red := c;
    979     green := c;
    980     blue := c;
    981     alpha := 255;
    982     end;
     879  result := BGRA(c,c,c);
    983880end;
    984881
     
    987884begin
    988885  c := CD and $00FF;
    989   with result do
    990     begin
    991     red := c;
    992     green := c;
    993     blue := c;
    994     alpha := (CD shr 8) and $FF;
    995     end;
     886  result := BGRA(c,c,c,(CD shr 8) and $FF);
    996887end;
    997888
     
    1000891begin
    1001892  c := (CD shr 8) and $FF;
    1002   with result do
    1003     begin
    1004     red := c;
    1005     green := c;
    1006     blue := c;
    1007     alpha := (CD shr 24) and $FF;
    1008     end;
     893  result := BGRA(c,c,c,(CD shr 24) and $FF);
    1009894end;
    1010895
     
    1013898begin
    1014899  temp := CD;
    1015   temp := ((temp and $ff) shl 16) or
    1016     (temp and $ff00) or ((temp shr 16) and $ff) or
    1017     $ff000000;
    1018   {$IFDEF ENDIAN_BIG}
    1019   DWord(result) := swap(temp);
    1020   {$ELSE}
    1021   DWord(result) := temp;
    1022   {$ENDIF}
     900  result := BGRA(temp and $ff, (temp shr 8) and $ff, (temp shr 16) and $ff);
    1023901end;
    1024902
    1025903function TBGRAReaderPNG.BGRAColorColor16(const CD: TColorData): TBGRAPixel;
    1026904begin
    1027   with result do
    1028     begin
    1029     red := CD shr 8 and $FF;
    1030     green := (CD shr 24) and $FF;
    1031     blue := (CD shr 40) and $FF;
    1032     alpha := 255;
    1033     end;
     905  result := BGRA(CD shr 8 and $FF,(CD shr 24) and $FF,(CD shr 40) and $FF);
    1034906end;
    1035907
     
    1038910begin
    1039911  temp := CD;
    1040   temp := ((temp and $ff) shl 16) or
    1041     (temp and $ff00) or ((temp shr 16) and $ff) or
    1042     (temp and $ff000000);
    1043   {$IFDEF ENDIAN_BIG}
    1044   DWord(result) := swap(temp);
    1045   {$ELSE}
    1046   DWord(result) := temp;
    1047   {$ENDIF}
     912  result := BGRA(temp and $ff, (temp shr 8) and $ff, (temp shr 16) and $ff, temp shr 24);
    1048913end;
    1049914
    1050915function TBGRAReaderPNG.BGRAColorColorAlpha16(const CD: TColorData): TBGRAPixel;
    1051916begin
    1052   with result do
    1053     begin
    1054     red := (CD shr 8) and $FF;
    1055     green := (CD shr 24) and $FF;
    1056     blue := (CD shr 40) and $FF;
    1057     alpha := (CD shr 56) and $FF;
    1058     end;
     917  result := BGRA(CD shr 8 and $FF,(CD shr 24) and $FF,(CD shr 40) and $FF, CD shr 56);
    1059918end;
    1060919
     
    11851044  end;
    11861045
     1046  procedure FilterSub(p: PByte; Count: NativeInt; bw: NativeInt);
     1047  begin
     1048    inc(p,bw);
     1049    dec(Count,bw);
     1050    while Count > 0 do
     1051    begin
     1052      {$push}{$r-}
     1053      p^ += (p-bw)^;
     1054      {$pop}
     1055      inc(p);
     1056      dec(Count);
     1057    end;
     1058  end;
     1059
     1060  procedure FilterUp(p,pPrev: PByte; Count: NativeUInt);
     1061  var Count4: NativeInt;
     1062  begin
     1063    Count4 := Count shr 2;
     1064    dec(Count, Count4 shl 2);
     1065    while Count4 > 0 do
     1066    begin
     1067      {$push}{$r-}
     1068      PDWord(p)^ := (((PDWord(pPrev)^ and $00FF00FF) + (PDWord(p)^ and $00FF00FF)) and $00FF00FF)
     1069        or (((PDWord(pPrev)^ and $FF00FF00) + (PDWord(p)^ and $FF00FF00)) and $FF00FF00);
     1070      {$pop}
     1071      inc(p,4);
     1072      inc(pPrev,4);
     1073      dec(Count4);
     1074    end;
     1075    while Count > 0 do
     1076    begin
     1077      {$push}{$r-}
     1078      p^ += pPrev^;
     1079      {$pop}
     1080
     1081      inc(p);
     1082      inc(pPrev);
     1083      dec(Count);
     1084    end;
     1085  end;
     1086
     1087  procedure FilterAverage(p,pPrev: PByte; Count: NativeUInt; bw: NativeInt);
     1088  var CountBW: NativeInt;
     1089  begin
     1090    CountBW := bw;
     1091    dec(Count,CountBW);
     1092    while CountBW > 0 do
     1093    begin
     1094      {$push}{$r-}
     1095      p^ += pPrev^ shr 1;
     1096      {$pop}
     1097      inc(p);
     1098      inc(pPrev);
     1099      dec(CountBW);
     1100    end;
     1101
     1102    while Count > 0 do
     1103    begin
     1104      {$push}{$r-}
     1105      p^ += (pPrev^+(p-bw)^) shr 1;
     1106      {$pop}
     1107      inc(p);
     1108      inc(pPrev);
     1109      dec(Count);
     1110    end;
     1111  end;
     1112
     1113  procedure FilterPaeth(p,pPrev: PByte; Count: NativeUInt; bw: NativeInt);
     1114  var
     1115    rx, dl, dp, dlp : NativeInt;
     1116    diag,left: NativeUInt;
     1117  begin
     1118    for rx := 0 to bw-1 do
     1119    begin
     1120      {$push}{$r-}
     1121      p^ += pPrev^;
     1122      {$pop}
     1123      inc(p);
     1124      inc(pPrev);
     1125    end;
     1126    dec(Count,bw);
     1127    while Count > 0 do
     1128    begin
     1129      diag := (pPrev-bw)^;
     1130      left := (p - bw)^;
     1131      dl := pPrev^ - NativeInt(diag);
     1132      dp := NativeInt(left) - NativeInt(diag);
     1133      dlp := abs(dl+dp);
     1134      if dl < 0 then dl := -dl;
     1135      if dp < 0 then dp := -dp;
     1136      {$push}{$r-}
     1137      if dp <= dlp then
     1138      begin
     1139        if dl <= dp then
     1140          p^ += left
     1141        else
     1142          p^ += pPrev^
     1143      end
     1144      else
     1145      if dl <= dlp then
     1146        p^ += left
     1147      else
     1148        p^ += diag;
     1149      {$pop}
     1150      inc(p);
     1151      inc(pPrev);
     1152      dec(Count);
     1153     end;
     1154  end;
     1155
    11871156  procedure Decode;
    1188   var y, rp, ry, rx, l : integer;
     1157  var y, rp, ry, l : NativeInt;
    11891158      lf : byte;
     1159      switchLine, currentLine, previousLine : pByteArray;
    11901160  begin
    11911161    FSetPixel := DecideSetPixel;
     
    12151185      if (l>0) then
    12161186        begin
    1217         GetMem (FPreviousLine, l);
    1218         GetMem (FCurrentLine, l);
    1219         fillchar (FCurrentLine^,l,0);
     1187        GetMem (previousLine, l);
     1188        GetMem (currentLine, l);
     1189        fillchar (currentLine^,l,0);
    12201190        try
    12211191          for ry := 0 to CountScanlines[rp]-1 do
    12221192            begin
    1223             FSwitchLine := FCurrentLine;
    1224             FCurrentLine := FPreviousLine;
    1225             FPreviousLine := FSwitchLine;
    1226             Y := CalcY(ry);
     1193            switchLine := currentLine;
     1194            currentLine := previousLine;
     1195            previousLine := switchLine;
     1196            Y := StartY + (ry * deltaY);
    12271197            lf := 0;
    12281198            Decompress.Read (lf, sizeof(lf));
    1229             Decompress.Read (FCurrentLine^, l);
    1230             if lf <> 0 then  // Do nothing when there is no filter used
    1231               for rx := 0 to l-1 do
    1232                 FCurrentLine^[rx] := DoFilter (lf, rx, FCurrentLine^[rx]);
     1199            Decompress.Read (currentLine^, l);
     1200
     1201            case lf of
     1202              1: FilterSub(PByte(currentLine), l, ByteWidth);
     1203              2: FilterUp(PByte(currentLine), PByte(previousLine), l);
     1204              3: FilterAverage(PByte(currentLine), PByte(previousLine), l, ByteWidth);
     1205              4: FilterPaeth(PByte(currentLine), PByte(previousLine), l, ByteWidth);
     1206            end;
     1207
    12331208            if FVerticalShrinkShr <> 0 then
    12341209              begin
    12351210                if (y and FVerticalShrinkMask) = 0 then
    1236                   FHandleScanLine (y shr FVerticalShrinkShr, FCurrentLine);
     1211                  FHandleScanLine (y shr FVerticalShrinkShr, currentLine);
    12371212              end else
    1238                 FHandleScanLine (y, FCurrentLine);
     1213                FHandleScanLine (y, currentLine);
    12391214            end;
    12401215        finally
    1241           freemem (FPreviousLine);
    1242           freemem (FCurrentLine);
     1216          freemem (previousLine);
     1217          freemem (currentLine);
    12431218        end;
    12441219        end;
  • GraphicTest/Packages/bgrabitmap/bgrareadtga.pas

    r472 r494  
    116116          for Col:=Img.Width-1 downto 0 do
    117117          begin
    118             PWord(PDest)^ := PWord(P)^;
    119             (PByte(PDest)+2)^ := (PByte(P)+2)^;
    120             (PByte(PDest)+3)^ := 255;
     118            PDest^ := BGRA((P+2)^,(P+1)^,P^);
    121119            inc(Pdest);
    122120            Inc(p,3);
     
    129127            inc(P);
    130128            Value:=value or (P[0] shl 8);
    131             With PDest^ do
    132                begin
    133                Red:=((value)shr 10) shl 3;
    134                Green:=((value)shr 5) shl 3;
    135                Blue:=((value)) shl 3;
    136                end;
     129            PDest^ := BGRA(((value)shr 10) shl 3,((value)shr 5) shl 3,((value)) shl 3);
    137130            Inc(PDest);
    138131            Inc(P);
  • GraphicTest/Packages/bgrabitmap/bgrareadxpm.pas

    r472 r494  
    2222implementation
    2323
    24 uses BGRABitmapTypes, Dialogs;
     24uses BGRABitmapTypes;
    2525
    2626{ TBGRAReaderXPM }
  • GraphicTest/Packages/bgrabitmap/bgraresample.pas

    r472 r494  
    9797implementation
    9898
    99 uses GraphType, Math, BGRABlend;
     99uses Math, BGRABlend;
    100100
    101101function SimpleStretch(bmp: TBGRACustomBitmap;
  • GraphicTest/Packages/bgrabitmap/bgrascene3d.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, BGRABitmapTypes, BGRAColorInt, BGRASSE, BGRAMatrix3D;
     8  Classes, SysUtils, BGRABitmapTypes, BGRAColorInt,
     9  BGRASSE, BGRAMatrix3D,
     10  BGRASceneTypes, BGRARenderer3D;
    911
    1012type
    1113  TProjection3D = BGRAMatrix3D.TProjection3D;
    12   TBox3D = record
    13     min,max: TPoint3D;
    14   end;
    15 
    16   TLightingNormal3D = (lnNone, lnFace, lnVertex, lnFaceVertexMix);
    17   TLightingInterpolation3D = (liLowQuality, liSpecularHighQuality, liAlwaysHighQuality);
    18   TAntialiasingMode3D = (am3dNone, am3dMultishape, am3dResample);
    19   TPerspectiveMode3D = (pmLinearMapping, pmPerspectiveMapping, pmZBuffer);
    20 
    21   TRenderingOptions = record
    22     LightingInterpolation: TLightingInterpolation3D;
    23     AntialiasingMode: TAntialiasingMode3D;
    24     AntialiasingResampleLevel: integer;
    25     PerspectiveMode: TPerspectiveMode3D;
    26     TextureInterpolation: boolean;
    27     MinZ: single;
    28   end;
    29 
    30   PSceneLightingContext = ^TSceneLightingContext;
    31   TSceneLightingContext = packed record
    32     basic: TBasicLightingContext;
    33     {128} diffuseColor, {144} specularColor: TColorInt65536;
    34     {160} vL, {176} dummy: TPoint3D_128;
    35     {192} vH: TPoint3D_128;
    36     {208} lightness: integer;
    37     {212} material : TObject;
    38     LightThroughFactor: single;
    39     LightThrough: LongBool;
    40     SaturationLow: integer;
    41     SaturationLowF: single;
    42     SaturationHigh: integer;
    43     SaturationHighF: single;
    44   end;
    45 
    46   TBGRAScene3D = class;
    47 
    48 {$i bgrascene3Dinterface.inc}
     14  TLightingNormal3D = BGRASceneTypes.TLightingNormal3D;
     15  TLightingInterpolation3D = BGRASceneTypes.TLightingInterpolation3D;
     16  TAntialiasingMode3D = BGRASceneTypes.TAntialiasingMode3D;
     17  TPerspectiveMode3D = BGRASceneTypes.TPerspectiveMode3D;
     18  TRenderingOptions = BGRASceneTypes.TRenderingOptions;
     19
     20  IBGRAVertex3D = BGRASceneTypes.IBGRAVertex3D;
     21  IBGRANormal3D = BGRASceneTypes.IBGRANormal3D;
     22  IBGRALight3D = BGRASceneTypes.IBGRALight3D;
     23  IBGRADirectionalLight3D = BGRASceneTypes.IBGRADirectionalLight3D;
     24  IBGRAPointLight3D = BGRASceneTypes.IBGRAPointLight3D;
     25  IBGRAMaterial3D = BGRASceneTypes.IBGRAMaterial3D;
     26  IBGRAFace3D = BGRASceneTypes.IBGRAFace3D;
     27  IBGRAPart3D = BGRASceneTypes.IBGRAPart3D;
     28  IBGRAObject3D = BGRASceneTypes.IBGRAObject3D;
     29
     30  arrayOfIBGRAVertex3D = BGRASceneTypes.arrayOfIBGRAVertex3D;
     31
     32const
     33  lnNone = BGRASceneTypes.lnNone;
     34  lnFace = BGRASceneTypes.lnFace;
     35  lnVertex = BGRASceneTypes.lnVertex;
     36  lnFaceVertexMix = BGRASceneTypes.lnFaceVertexMix;
     37
     38  liLowQuality = BGRASceneTypes.liLowQuality;
     39  liSpecularHighQuality = BGRASceneTypes.liSpecularHighQuality;
     40  liAlwaysHighQuality = BGRASceneTypes.liAlwaysHighQuality;
     41
     42  am3dNone = BGRASceneTypes.am3dNone;
     43  am3dMultishape = BGRASceneTypes.am3dMultishape;
     44  am3dResample = BGRASceneTypes.am3dResample;
     45
     46  pmLinearMapping = BGRASceneTypes.pmLinearMapping;
     47  pmPerspectiveMapping = BGRASceneTypes.pmPerspectiveMapping;
     48  pmZBuffer = BGRASceneTypes.pmZBuffer;
    4949
    5050type
     51
     52  { TCamera3D }
     53
     54  TCamera3D = class
     55  private
     56    procedure ComputeMatrix;
     57    function GetLookWhere: TPoint3D;
     58    function GetMatrix: TMatrix3D;
     59    function GetViewPoint: TPoint3D;
     60    procedure SetMatrix(AValue: TMatrix3D);
     61    procedure SetViewPoint(AValue: TPoint3D);
     62  protected
     63    FMatrix: TMatrix3D;
     64    FMatrixComputed: boolean;
     65    FViewPoint: TPoint3D_128;
     66    FLookWhere, FTopDir: TPoint3D_128;
     67  public
     68    procedure LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
     69    procedure LookDown(angleDeg: single);
     70    procedure LookLeft(angleDeg: single);
     71    procedure LookRight(angleDeg: single);
     72    procedure LookUp(angleDeg: single);
     73    property ViewPoint: TPoint3D read GetViewPoint write SetViewPoint;
     74    property LookWhere: TPoint3D read GetLookWhere;
     75    property Matrix: TMatrix3D read GetMatrix write SetMatrix;
     76  end;
     77
    5178  { TBGRAScene3D }
    5279
    5380  TBGRAScene3D = class
    5481  private
    55     FSurface: TBGRACustomBitmap;
    56     FViewCenter: TPointF;
    57     FAutoViewCenter: boolean;
     82    FSurface: TBGRACustomBitmap; //destination of software renderer
     83    FViewCenter: TPointF;        //where origin is drawn
     84    FAutoViewCenter: boolean;    //use middle of the screen
     85    FZoom: TPointF;              //how much the drawing is zoomed
     86    FAutoZoom: Boolean;          //display 1 as 80% of surface size
     87    FProjection: TProjection3D;  //current projection
     88    FRenderedFaceCount: integer; //current counter of rendered faces
     89
     90    FCamera: TCamera3D;
     91
    5892    FObjects: array of IBGRAObject3D;
    5993    FObjectCount: integer;
    6094    FMaterials: array of IBGRAMaterial3D;
    6195    FMaterialCount: integer;
    62     FMatrix: TMatrix3D;
    63     FViewPoint: TPoint3D_128;
    64     FLookWhere, FTopDir: TPoint3D_128;
    65     FZoom: TPointF;
    66     FAutoZoom: Boolean;
    67     FLights: TList;
    68     FAmbiantLightness: integer;
    69     FAmbiantLightColor: TColorInt65536;
    70     FRenderedFaceCount: integer;
    71     FProjection: TProjection3D;
     96    FDefaultMaterial : IBGRAMaterial3D;
     97
     98    FAmbiantLightColorF: TColorF;        //lightness without light sources
     99    FLights: TList;                      //individual light sources
     100
    72101    function GetAmbiantLightColorF: TColorF;
    73102    function GetAmbiantLightness: single;
     
    91120    procedure SetViewPoint(const AValue: TPoint3D);
    92121    procedure ComputeView(ScaleX,ScaleY: single);
    93     function ComputeCoordinate(ASceneCoord: TPoint3D_128; APart: IBGRAPart3D): TPointF; overload;
    94     function ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF; overload;
    95     procedure ComputeLight;
    96     procedure ComputeMatrix;
     122    function ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF;
    97123    procedure AddObject(AObj: IBGRAObject3D);
    98124    procedure AddLight(ALight: TObject);
    99125    procedure AddMaterial(AMaterial: IBGRAMaterial3D);
    100126    procedure Init;
    101     procedure InternalRender(ASurface: TBGRACustomBitmap; AAntialiasingMode: TAntialiasingMode3D; GlobalScale: single); virtual;
    102127
    103128  protected
    104     function ApplyLightingWithLightness(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
    105     function ApplyLightingWithDiffuseColor(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
    106     function ApplyLightingWithDiffuseAndSpecularColor(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
    107     function ApplyLightingWithAmbiantLightnessOnly(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
    108     function ApplyNoLighting(Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel; virtual;
     129    FRenderer: TCustomRenderer3D;
     130    FMaterialLibrariesFetched: array of string;
     131    FTexturesFetched: array of record
     132        Name: string;
     133        Bitmap: TBGRACustomBitmap;
     134      end;
    109135    procedure UseMaterial(AMaterialName: string; AFace: IBGRAFace3D); virtual;
    110     function FetchTexture({%H-}AName: string; out texSize: TPointF): IBGRAScanner; virtual;
     136    function LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap; virtual;
     137    function FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; virtual;
     138    procedure HandleFetchException(AException: Exception); virtual;
     139    procedure DoRender; virtual;
     140    procedure DoClear; virtual;
     141    function GetRenderWidth: integer;
     142    function GetRenderHeight: integer;
     143    procedure OnMaterialTextureChanged({%H-}ASender: TObject); virtual;
     144    procedure SetDefaultMaterial(AValue: IBGRAMaterial3D);
     145    procedure InvalidateMaterial;
    111146
    112147  public
    113148    DefaultLightingNormal: TLightingNormal3D;
    114     DefaultMaterial : IBGRAMaterial3D;
    115149    RenderingOptions: TRenderingOptions;
    116150    UnknownColor: TBGRAPixel;
     151    FetchDirectory: string;
     152    FetchThrowsException: boolean;
    117153
    118154    constructor Create;
     
    120156    destructor Destroy; override;
    121157    procedure Clear; virtual;
     158    function FetchObject(AName: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
     159    procedure FetchMaterials(ALibraryName: string); virtual;
    122160    function LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
    123161    function LoadObjectFromFileUTF8(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
     
    132170    procedure LookDown(angleDeg: single);
    133171    procedure Render; virtual;
     172    procedure Render(ARenderer: TCustomRenderer3D);
    134173    function CreateObject: IBGRAObject3D; overload;
    135174    function CreateObject(ATexture: IBGRAScanner): IBGRAObject3D; overload;
     
    154193    procedure ForEachVertex(ACallback: TVertex3DCallback);
    155194    procedure ForEachFace(ACallback: TFace3DCallback);
     195    function MakeLightList: TList;
     196
    156197    property ViewCenter: TPointF read GetViewCenter write SetViewCenter;
    157198    property AutoViewCenter: boolean read FAutoViewCenter write SetAutoViewCenter;
     
    173214    property Material[AIndex: integer] : IBGRAMaterial3D read GetMaterial;
    174215    property MaterialCount: integer read FMaterialCount;
     216    property Camera: TCamera3D read FCamera;
     217    property DefaultMaterial: IBGRAMaterial3D read FDefaultMaterial write SetDefaultMaterial;
    175218  end;
    176219
    177220implementation
    178221
    179 uses BGRAPolygon, BGRAPolygonAliased, BGRACoordPool3D, BGRAResample,
    180   lazutf8classes;
     222uses BGRACoordPool3D, BGRAUTF8;
    181223
    182224{$i lightingclasses3d.inc}
    183225{$i vertex3d.inc}
    184226{$i face3d.inc}
    185 
    186 type
    187   { TBGRAObject3D }
    188 
    189   TBGRAObject3D = class(TInterfacedObject,IBGRAObject3D)
    190   private
    191     FColor: TBGRAPixel;
    192     FLight: Single;
    193     FTexture: IBGRAScanner;
    194     FMainPart: IBGRAPart3D;
    195     FFaces: array of IBGRAFace3D;
    196     FFaceCount: integer;
    197     FLightingNormal : TLightingNormal3D;
    198     FParentLighting: boolean;
    199     FMaterial: IBGRAMaterial3D;
    200     FScene: TBGRAScene3D;
    201     procedure AddFace(AFace: IBGRAFace3D);
    202   public
    203     constructor Create(AScene: TBGRAScene3D);
    204     destructor Destroy; override;
    205     procedure Clear;
    206 
    207     function AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
    208     function AddFace(const AVertices: array of IBGRAVertex3D; ABiface: boolean): IBGRAFace3D;
    209     function AddFace(const AVertices: array of IBGRAVertex3D; ATexture: IBGRAScanner): IBGRAFace3D;
    210     function AddFace(const AVertices: array of IBGRAVertex3D; AColor: TBGRAPixel): IBGRAFace3D;
    211     function AddFace(const AVertices: array of IBGRAVertex3D; AColors: array of TBGRAPixel): IBGRAFace3D;
    212     function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
    213     procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D);
    214     function GetColor: TBGRAPixel;
    215     function GetLight: Single;
    216     function GetTexture: IBGRAScanner;
    217     function GetMainPart: IBGRAPart3D;
    218     function GetLightingNormal: TLightingNormal3D;
    219     function GetParentLighting: boolean;
    220     function GetFace(AIndex: integer): IBGRAFace3D;
    221     function GetFaceCount: integer;
    222     function GetTotalVertexCount: integer;
    223     function GetTotalNormalCount: integer;
    224     function GetMaterial: IBGRAMaterial3D;
    225     procedure SetLightingNormal(const AValue: TLightingNormal3D);
    226     procedure SetParentLighting(const AValue: boolean);
    227     procedure SetColor(const AValue: TBGRAPixel);
    228     procedure SetLight(const AValue: Single);
    229     procedure SetTexture(const AValue: IBGRAScanner);
    230     procedure SetMaterial(const AValue: IBGRAMaterial3D);
    231     procedure RemoveUnusedVertices;
    232     procedure SeparatePart(APart: IBGRAPart3D);
    233     function GetScene: TBGRAScene3D;
    234     function GetRefCount: integer;
    235     procedure SetBiface(AValue : boolean);
    236     procedure ForEachVertex(ACallback: TVertex3DCallback);
    237     procedure ForEachFace(ACallback: TFace3DCallback);
    238   end;
    239 
    240227{$i part3d.inc}
    241228{$i object3d.inc}
    242229{$i shapes3d.inc}
    243230
     231{ TCamera3D }
     232
     233function TCamera3D.GetLookWhere: TPoint3D;
     234begin
     235  result := Point3D(FLookWhere);
     236end;
     237
     238function TCamera3D.GetMatrix: TMatrix3D;
     239begin
     240  if not FMatrixComputed then
     241  begin
     242    ComputeMatrix;
     243    FMatrixComputed := true;
     244  end;
     245  result := FMatrix;
     246end;
     247
     248function TCamera3D.GetViewPoint: TPoint3D;
     249begin
     250  result := Point3D(FViewPoint);
     251end;
     252
     253procedure TCamera3D.SetMatrix(AValue: TMatrix3D);
     254begin
     255  FMatrix := AValue;
     256  FMatrixComputed:= true;
     257  FViewPoint := Point3D_128(FMatrix[1,4],FMatrix[2,4],FMatrix[3,4]);
     258end;
     259
     260procedure TCamera3D.SetViewPoint(AValue: TPoint3D);
     261begin
     262  FViewPoint := Point3D_128(AValue);
     263  FMatrix[1,4] := FViewPoint.x;
     264  FMatrix[2,4] := FViewPoint.y;
     265  FMatrix[3,4] := FViewPoint.z;
     266  FMatrixComputed := false;
     267end;
     268
     269procedure TCamera3D.ComputeMatrix;
     270var ZDir, XDir, YDir: TPoint3D_128;
     271begin
     272  if IsPoint3D_128_Zero(FTopDir) then exit;
     273  YDir := -FTopDir;
     274  Normalize3D_128(YDir);
     275
     276  ZDir := FLookWhere-FViewPoint;
     277  if IsPoint3D_128_Zero(ZDir) then exit;
     278  Normalize3D_128(ZDir);
     279
     280  VectProduct3D_128(YDir,ZDir,XDir);
     281  VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir
     282  Normalize3D_128(XDir);
     283  Normalize3D_128(YDir);
     284
     285  FMatrix := Matrix3D(XDir,YDir,ZDir,FViewPoint);
     286  FMatrix := MatrixInverse3D(FMatrix);
     287end;
     288
     289procedure TCamera3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
     290begin
     291  FLookWhere := Point3D_128(AWhere);
     292  FTopDir := Point3D_128(ATopDir);
     293  FMatrixComputed := false;
     294end;
     295
     296procedure TCamera3D.LookLeft(angleDeg: single);
     297var m,inv: TMatrix3D;
     298begin
     299  inv := MatrixInverse3D(Matrix);
     300  m := MatrixRotateY(angleDeg*Pi/180);
     301  FLookWhere := inv*m*Matrix*FLookWhere;
     302  FMatrixComputed := false;
     303end;
     304
     305procedure TCamera3D.LookRight(angleDeg: single);
     306begin
     307  LookLeft(-angleDeg);
     308end;
     309
     310procedure TCamera3D.LookUp(angleDeg: single);
     311var m,inv: TMatrix3D;
     312begin
     313  inv := MatrixInverse3D(Matrix);
     314  m := MatrixRotateX(-angleDeg*Pi/180);
     315  FLookWhere := inv*m*Matrix*FLookWhere;
     316  FMatrixComputed := false;
     317end;
     318
     319procedure TCamera3D.LookDown(angleDeg: single);
     320begin
     321  LookUp(-angleDeg);
     322end;
     323
     324
    244325{ TBGRAScene3D }
    245326
     
    248329  if FAutoViewCenter then
    249330  begin
    250     if Surface = nil then
    251       result := PointF(0,0)
    252     else
    253       result := PointF((Surface.Width-1)/2,(Surface.Height-1)/2)
     331    result := PointF((GetRenderWidth-1)/2,(GetRenderHeight-1)/2)
    254332  end
    255333  else
     
    259337function TBGRAScene3D.GetViewPoint: TPoint3D;
    260338begin
    261   result := Point3D(FViewPoint);
     339  result := Camera.ViewPoint;
    262340end;
    263341
     
    267345  if FAutoZoom then
    268346  begin
    269     if FSurface = nil then
     347    Size := sqrt(GetRenderWidth*GetRenderHeight)*0.8;
     348    if Size = 0 then
    270349      result := PointF(1,1)
    271350    else
    272     begin
    273       Size := sqrt(FSurface.Width*FSurface.Height)*0.8;
    274351      result := PointF(size,size);
    275     end;
    276352  end else
    277353    result := FZoom;
     
    280356procedure TBGRAScene3D.SetAmbiantLightColorF(const AValue: TColorF);
    281357begin
    282   FAmbiantLightColor := ColorFToColorInt65536(AValue);
    283   FAmbiantLightness := (FAmbiantLightColor.r + FAmbiantLightColor.g + FAmbiantLightColor.b) div 6;
     358  FAmbiantLightColorF := AValue;
    284359end;
    285360
    286361procedure TBGRAScene3D.SetAmbiantLightness(const AValue: single);
    287362begin
    288   FAmbiantLightness:= round(AValue*32768);
    289   FAmbiantLightColor := ColorInt65536(FAmbiantLightness*2, FAmbiantLightness*2, FAmbiantLightness*2);
     363  FAmbiantLightColorF := ColorF(AValue, AValue, AValue, 1);
    290364end;
    291365
    292366procedure TBGRAScene3D.SetAmbiantLightColor(const AValue: TBGRAPixel);
    293367begin
    294   FAmbiantLightColor := BGRAToColorInt(AValue);
    295   FAmbiantLightness := (FAmbiantLightColor.r + FAmbiantLightColor.g + FAmbiantLightColor.b) div 6;
     368  FAmbiantLightColorF := ColorInt65536ToColorF(BGRAToColorInt(AValue,True));
    296369end;
    297370
     
    313386function TBGRAScene3D.GetAmbiantLightColor: TBGRAPixel;
    314387begin
    315   result := ColorIntToBGRA(FAmbiantLightColor);
     388  result := ColorIntToBGRA(ColorFToColorInt65536(FAmbiantLightColorF),True);
    316389end;
    317390
     
    354427function TBGRAScene3D.GetAmbiantLightness: single;
    355428begin
    356   result := FAmbiantLightness/32768;
     429  result := (FAmbiantLightColorF[1]+FAmbiantLightColorF[2]+FAmbiantLightColorF[3])/3;
    357430end;
    358431
    359432function TBGRAScene3D.GetAmbiantLightColorF: TColorF;
    360433begin
    361   result := ColorInt65536ToColorF(FAmbiantLightColor);
     434  result := FAmbiantLightColorF;
    362435end;
    363436
     
    378451end;
    379452
     453procedure TBGRAScene3D.SetDefaultMaterial(AValue: IBGRAMaterial3D);
     454begin
     455  if FDefaultMaterial=AValue then Exit;
     456  FDefaultMaterial:=AValue;
     457  InvalidateMaterial;
     458end;
     459
    380460procedure TBGRAScene3D.SetViewCenter(const AValue: TPointF);
    381461begin
     
    384464end;
    385465
    386 procedure TBGRAScene3D.ComputeMatrix;
    387 var ZDir, XDir, YDir: TPoint3D_128;
    388 begin
    389   if IsPoint3D_128_Zero(FTopDir) then exit;
    390   YDir := -FTopDir;
    391   Normalize3D_128(YDir);
    392 
    393   ZDir := FLookWhere-FViewPoint;
    394   if IsPoint3D_128_Zero(ZDir) then exit;
    395   Normalize3D_128(ZDir);
    396 
    397   VectProduct3D_128(YDir,ZDir,XDir);
    398   VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir
    399   Normalize3D_128(XDir);
    400   Normalize3D_128(YDir);
    401 
    402   FMatrix := Matrix3D(XDir,YDir,ZDir,FViewPoint);
    403   FMatrix := MatrixInverse3D(FMatrix);
     466procedure TBGRAScene3D.SetViewPoint(const AValue: TPoint3D);
     467begin
     468  Camera.ViewPoint := AValue;
    404469end;
    405470
     
    431496  FAutoZoom := True;
    432497  FAutoViewCenter := True;
    433   ViewPoint := Point3D(0,0,-100);
    434   LookAt(Point3D(0,0,0), Point3D(0,-1,0));
     498
     499  FCamera := TCamera3D.Create;
     500  Camera.ViewPoint := Point3D(0,0,-100);
     501  Camera.LookAt(Point3D(0,0,0), Point3D(0,-1,0));
    435502  with RenderingOptions do
    436503  begin
     
    464531
    465532destructor TBGRAScene3D.Destroy;
    466 begin
    467   Clear;
    468   FLights.Free;
     533var
     534  i: Integer;
     535begin
     536  DoClear;
     537  FreeAndNil(FLights);
     538  FreeAndNil(FCamera);
     539  for i := 0 to high(FTexturesFetched) do
     540    FTexturesFetched[i].Bitmap.Free;
    469541  inherited Destroy;
    470542end;
    471543
    472544procedure TBGRAScene3D.Clear;
    473 var i: integer;
    474 begin
    475   for i := 0 to FLights.Count-1 do
    476     TBGRALight3D(FLights[i])._Release;
    477   FLights.Clear;
    478 
    479   for i := 0 to FObjectCount-1 do
    480     FObjects[i].Clear;
    481   FObjects := nil;
    482   FObjectCount := 0;
    483 
    484   FMaterials := nil;
    485   FMaterialCount := 0;
     545begin
     546  DoClear;
    486547  DefaultMaterial := CreateMaterial;
     548end;
     549
     550function TBGRAScene3D.FetchObject(AName: string; SwapFacesOrientation: boolean
     551  ): IBGRAObject3D;
     552begin
     553  if FetchDirectory = '' then raise exception.Create('Please define first the FetchDirectory');
     554  try
     555    result := LoadObjectFromFileUTF8(ConcatPaths([FetchDirectory,AName]), SwapFacesOrientation);
     556  except
     557    on ex:Exception do
     558      HandleFetchException(ex);
     559  end;
    487560end;
    488561
     
    529602end;
    530603
     604function TBGRAScene3D.LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap;
     605begin
     606  result := BGRABitmapFactory.Create(AfileNameUTF8,True);
     607end;
     608
    531609function TBGRAScene3D.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner;
    532 begin
    533   result := nil;
    534   texSize := PointF(1,1);
     610var
     611  i: Integer;
     612  bmp: TBGRACustomBitmap;
     613begin
     614  bmp := nil;
     615  for i := 0 to high(FTexturesFetched) do
     616    if FTexturesFetched[i].Name = AName then
     617    begin
     618      bmp := FTexturesFetched[i].Bitmap;
     619      result := bmp;
     620      texSize := PointF(bmp.Width,bmp.Height);
     621      exit;
     622    end;
     623  if FetchDirectory <> '' then
     624  begin
     625    try
     626      bmp := LoadBitmapFromFileUTF8(ConcatPaths([FetchDirectory,AName]));
     627    except
     628      on ex:Exception do
     629        HandleFetchException(ex);
     630    end;
     631  end;
     632  if bmp = nil then
     633  begin
     634    result := nil;
     635    texSize := PointF(1,1);
     636  end else
     637  begin
     638    setlength(FTexturesFetched, length(FTexturesFetched)+1);
     639    FTexturesFetched[high(FTexturesFetched)].Name := AName;
     640    FTexturesFetched[high(FTexturesFetched)].Bitmap := bmp;
     641    result := bmp;
     642    texSize := PointF(bmp.Width,bmp.Height);
     643  end;
     644end;
     645
     646procedure TBGRAScene3D.FetchMaterials(ALibraryName: string);
     647var
     648  i: Integer;
     649begin
     650  if FetchDirectory <> '' then
     651  begin
     652    for i := 0 to high(FMaterialLibrariesFetched) do
     653      if FMaterialLibrariesFetched[i]=ALibraryName then exit;
     654    setlength(FMaterialLibrariesFetched,length(FMaterialLibrariesFetched)+1);
     655    FMaterialLibrariesFetched[high(FMaterialLibrariesFetched)] := ALibraryName;
     656    try
     657      LoadMaterialsFromFile(ConcatPaths([FetchDirectory,ALibraryName]));
     658    except
     659      on ex:Exception do
     660        HandleFetchException(ex);
     661    end;
     662  end;
     663end;
     664
     665procedure TBGRAScene3D.HandleFetchException(AException: Exception);
     666begin
     667  if FetchThrowsException then
     668    raise AException;
     669end;
     670
     671procedure TBGRAScene3D.DoClear;
     672var i: integer;
     673begin
     674  for i := 0 to FLights.Count-1 do
     675    TBGRALight3D(FLights[i]).ReleaseInterface;
     676  FLights.Clear;
     677
     678  for i := 0 to FObjectCount-1 do
     679  begin
     680    FObjects[i].Clear;
     681    FObjects[i] := nil;
     682  end;
     683  FObjects := nil;
     684  FObjectCount := 0;
     685
     686  FMaterials := nil;
     687  FMaterialCount := 0;
     688  DefaultMaterial := nil;
     689end;
     690
     691function TBGRAScene3D.GetRenderWidth: integer;
     692begin
     693  if Assigned(FRenderer) then
     694    result := FRenderer.SurfaceWidth
     695  else
     696  if Assigned(FSurface) then
     697    result := FSurface.Width
     698  else
     699    result := 0;
     700end;
     701
     702function TBGRAScene3D.GetRenderHeight: integer;
     703begin
     704  if Assigned(FRenderer) then
     705    result := FRenderer.SurfaceHeight
     706  else
     707  if Assigned(FSurface) then
     708    result := FSurface.Height
     709  else
     710    result := 0;
     711end;
     712
     713procedure TBGRAScene3D.OnMaterialTextureChanged(ASender: TObject);
     714begin
     715  InvalidateMaterial;
     716end;
     717
     718procedure TBGRAScene3D.InvalidateMaterial;
     719var
     720  i: Integer;
     721begin
     722  for i := 0 to FObjectCount-1 do
     723    FObjects[i].InvalidateMaterial;
    535724end;
    536725
    537726function TBGRAScene3D.LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean): IBGRAObject3D;
    538 var source: TFileStream;
    539 begin
    540   source := TFileStream.Create(AFilename,fmOpenRead,fmShareDenyWrite);
    541   try
    542     result := LoadObjectFromStream(source,SwapFacesOrientation);
    543   finally
    544     source.free;
    545   end;
     727begin
     728  result := LoadObjectFromFileUTF8(SysToUTF8(AFilename), SwapFacesOrientation);
    546729end;
    547730
     
    659842      result.LightingNormal := lnVertex;
    660843    end else
     844    if lineType = 'mtllib' then
     845      FetchMaterials(trim(s))
     846    else
    661847    if lineType = 'usemtl' then
    662848      materialname := trim(s)
     
    8371023procedure TBGRAScene3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
    8381024begin
    839   FLookWhere := Point3D_128(AWhere);
    840   FTopDir := Point3D_128(ATopDir);
     1025  Camera.LookAt(AWhere,ATopDir);
    8411026end;
    8421027
    8431028procedure TBGRAScene3D.LookLeft(angleDeg: single);
    844 var m,inv: TMatrix3D;
    845 begin
    846   inv := MatrixInverse3D(FMatrix);
    847   m := MatrixRotateY(angleDeg*Pi/180);
    848   FLookWhere := inv*m*FMatrix*FLookWhere;
     1029begin
     1030  Camera.LookLeft(angleDeg);
    8491031end;
    8501032
    8511033procedure TBGRAScene3D.LookRight(angleDeg: single);
    8521034begin
    853   LookLeft(-angleDeg);
     1035  Camera.LookRight(angleDeg);
    8541036end;
    8551037
    8561038procedure TBGRAScene3D.LookUp(angleDeg: single);
    857 var m,inv: TMatrix3D;
    858 begin
    859   inv := MatrixInverse3D(FMatrix);
    860   m := MatrixRotateX(-angleDeg*Pi/180);
    861   FLookWhere := inv*m*FMatrix*FLookWhere;
     1039begin
     1040  Camera.LookUp(angleDeg);
    8621041end;
    8631042
    8641043procedure TBGRAScene3D.LookDown(angleDeg: single);
    8651044begin
    866   LookUp(-angleDeg);
     1045  Camera.LookDown(angleDeg);
    8671046end;
    8681047
    8691048procedure TBGRAScene3D.Render;
    8701049begin
    871   InternalRender(FSurface, RenderingOptions.AntialiasingMode, 1);
     1050  FRenderer := TBGRARenderer3D.Create(FSurface, RenderingOptions,
     1051    FAmbiantLightColorF,
     1052    FLights);
     1053  DoRender;
     1054  FRenderer.Free;
     1055end;
     1056
     1057procedure TBGRAScene3D.Render(ARenderer: TCustomRenderer3D);
     1058begin
     1059  FRenderer := ARenderer;
     1060  DoRender;
     1061  FRenderer := nil;
    8721062end;
    8731063
     
    8761066  i: Integer;
    8771067begin
    878   ComputeMatrix;
    879 
    8801068  FProjection.Zoom := Zoom;
    8811069  FProjection.Zoom.X *= ScaleX;
     
    8851073  FProjection.Center.Y *= ScaleY;
    8861074  for i := 0 to FObjectCount-1 do
    887     FObjects[i].ComputeWithMatrix(FMatrix, FProjection);
    888 end;
    889 
    890 function TBGRAScene3D.ComputeCoordinate(ASceneCoord: TPoint3D_128; APart: IBGRAPart3D): TPointF;
    891 begin
    892   result := APart.ComputeCoordinate(ASceneCoord, FProjection);
     1075    FObjects[i].ComputeWithMatrix(Camera.Matrix, FProjection);
    8931076end;
    8941077
     
    9031086  end else
    9041087    result := PointF(0,0);
    905 end;
    906 
    907 procedure TBGRAScene3D.ComputeLight;
    908 begin
    909 
    9101088end;
    9111089
     
    9981176end;
    9991177
    1000 procedure TBGRAScene3D.InternalRender(ASurface: TBGRACustomBitmap; AAntialiasingMode: TAntialiasingMode3D; GlobalScale: single);
     1178procedure TBGRAScene3D.DoRender;
    10011179var
    10021180  LFaces: array of TBGRAFace3D;
     
    10141192      obj := FObjects[i];
    10151193      inc(LFaceCount, obj.GetFaceCount);
    1016       if obj.GetParentLighting then
    1017       begin
    1018         obj.SetLightingNormal(Self.DefaultLightingNormal);
    1019         obj.SetParentLighting(True);
    1020       end;
     1194      obj.Update;
    10211195    end;
    10221196    setlength(LFaces, LFaceCount);
     
    10341208
    10351209var
    1036   multi: TBGRAMultishapeFiller;
    1037   ColorGradientTempBmp: TBGRACustomBitmap;
    1038   zbuffer: psingle;
    1039 
     1210  faceDesc: TFaceRenderingDescription;
    10401211  LVertices: array of TBGRAVertex3D;
    1041   LColors: array of TBGRAPixel;
    1042   LTexCoord: array of TPointF;
    1043   LZ: array of single;
    1044   LProj: array of TPointF;
    1045   LPos3D, LNormal3D: array of TPoint3D_128;
    1046   LLighting: array of word;
    1047   shaderContext: TMemoryBlockAlign128;
    1048   lightingProc: TShaderFunction3D;
    1049   UseAmbiantColor: boolean;
    10501212
    10511213  procedure DrawFace(numFace: integer);
    1052 
    1053     procedure DrawAliasedColoredFace(shader: TShaderFunction3D; VCount: integer; context: PBasicLightingContext);
    1054     var j,k: integer;
    1055         SameColor: boolean;
    1056         center: record
    1057           proj: TPointF;
    1058           pos3D,normal3D: TPoint3D_128;
    1059           color: TBGRAPixel;
    1060         end;
    1061 
    1062     begin
    1063       SameColor := True;
    1064       for j := 1 to VCount-1 do
    1065         if (LColors[j]<>LColors[j-1]) then SameColor := False;
    1066 
    1067       if shader <> nil then
    1068       begin
    1069         if SameColor then
    1070         begin
    1071           BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,
    1072             slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),nil,
    1073               slice(LTexCoord,VCount),False,shader,True,LColors[0],zbuffer,context);
    1074         end else
    1075         if VCount = 3 then
    1076         begin
    1077           ColorGradientTempBmp.SetPixel(0,0,LColors[0]);
    1078           ColorGradientTempBmp.SetPixel(1,0,LColors[1]);
    1079           ColorGradientTempBmp.SetPixel(0,1,LColors[2]);
    1080           ColorGradientTempBmp.SetPixel(1,1,MergeBGRA(LColors[1],LColors[2]));
    1081           BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,
    1082             slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),ColorGradientTempBmp,
    1083               [PointF(0,0),PointF(1,0),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);
    1084         end else
    1085         if VCount = 4 then
    1086         begin
    1087           ColorGradientTempBmp.SetPixel(0,0,LColors[0]);
    1088           ColorGradientTempBmp.SetPixel(1,0,LColors[1]);
    1089           ColorGradientTempBmp.SetPixel(1,1,LColors[2]);
    1090           ColorGradientTempBmp.SetPixel(0,1,LColors[3]);
    1091           BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,
    1092             slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),ColorGradientTempBmp,
    1093               [PointF(0,0),PointF(1,0),PointF(1,1),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);
    1094         end else
    1095         if VCount >= 3 then
    1096         begin //split into triangles
    1097           with center do
    1098           begin
    1099             ClearPoint3D_128(pos3D);
    1100             ClearPoint3D_128(normal3D);
    1101             color := MergeBGRA(slice(LColors,VCount));
    1102           end;
    1103           for j := 0 to VCount-1 do
    1104           begin
    1105             center.pos3D += LPos3D[j];
    1106             center.normal3D += LNormal3D[j];
    1107           end;
    1108           with center do
    1109           begin
    1110             pos3D *= (1/VCount);
    1111             Normalize3D_128(normal3D);
    1112           end;
    1113           center.proj := ComputeCoordinate(center.pos3D);
    1114           k := VCount-1;
    1115           for j := 0 to VCount-1 do
    1116           begin
    1117             ColorGradientTempBmp.SetPixel(0,0,LColors[k]);
    1118             ColorGradientTempBmp.SetPixel(1,0,LColors[j]);
    1119             ColorGradientTempBmp.SetPixel(0,1,center.color);
    1120             ColorGradientTempBmp.SetPixel(1,1,MergeBGRA(LColors[j],center.color));
    1121             BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,
    1122               [LProj[k],LProj[j],center.proj], [LPos3D[k],LPos3D[j],center.pos3D],
    1123               [LNormal3D[k],LNormal3D[j],center.normal3D], ColorGradientTempBmp,
    1124                 [PointF(0,0),PointF(1,0),PointF(0,1)],True,shader,True, BGRAPixelTransparent,zbuffer,context);
    1125             k := j;
    1126           end;
    1127         end;
    1128       end else
    1129       begin
    1130         if SameColor then
    1131         begin
    1132           if RenderingOptions.PerspectiveMode = pmZBuffer then
    1133             BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, slice(LProj,VCount),
    1134             slice(LZ,VCount), slice(LColors,VCount),True,zbuffer)
    1135           else
    1136             ASurface.FillPoly(slice(LProj,VCount),LColors[0],dmDrawWithTransparency);
    1137         end
    1138         else
    1139         begin
    1140           if VCount > 4 then
    1141           begin //split into triangles
    1142             with center do
    1143             begin
    1144               ClearPoint3D_128(pos3D);
    1145               color := MergeBGRA(slice(LColors,VCount));
    1146             end;
    1147             for j := 0 to VCount-1 do
    1148               center.pos3D += LPos3D[j];
    1149             with center do
    1150               pos3D *= (1/VCount);
    1151             center.proj := ComputeCoordinate(center.pos3D);
    1152             k := VCount-1;
    1153             if RenderingOptions.PerspectiveMode = pmLinearMapping then
    1154             begin
    1155               for j := 0 to VCount-1 do
    1156               begin
    1157                 ASurface.FillPolyLinearColor([LProj[k],LProj[j],center.proj],[LColors[k],LColors[j],center.color]);
    1158                 k := j;
    1159               end;
    1160             end else
    1161             begin
    1162               for j := 0 to VCount-1 do
    1163               begin
    1164                 BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, [LProj[k],LProj[j],center.proj],
    1165                  [LZ[k],LZ[j],center.pos3D.z], [LColors[k],LColors[j],center.color],True,zbuffer);
    1166                 k := j;
    1167               end;
    1168             end;
    1169           end else
    1170           begin
    1171             if RenderingOptions.PerspectiveMode = pmLinearMapping then
    1172               ASurface.FillPolyLinearColor(slice(LProj,VCount),slice(LColors,VCount))
    1173             else
    1174               BGRAPolygonAliased.PolygonPerspectiveColorGradientAliased(ASurface, slice(LProj,VCount),
    1175                slice(LZ,VCount), slice(LColors,VCount),True,zbuffer);
    1176           end;
    1177         end;
    1178       end;
    1179     end;
    1180 
    11811214  var
    11821215    j,k: Integer;
    1183     LTexture: IBGRAScanner;
    1184     LMaterial: TBGRAMaterial3D;
    1185     SameColor: boolean;
    1186     LLightNormal : TLightingNormal3D;
    1187     LNoLighting: boolean;
    1188     PtCenter: TPointF;
    1189     PtCenter3D: TPoint3D_128;
    1190     ColorCenter: TBGRAPixel;
    11911216    VCount,NewVCount: integer;
    1192     ctx: PSceneLightingContext;
    1193     NegNormals, UseDiffuseColor,
    1194     UseDiffuseLightness{, OnlyDirectionalLight}: boolean;
     1217    NegNormals: boolean;
    11951218    LastVisibleVertex: integer;
    11961219
     
    12041227       LVertices[NewVCount] := nil; //computed
    12051228
    1206        LColors[NewVCount] := MergeBGRA(LColors[n1],round((1-t)*65536),LColors[n2],round(t*65536));
    1207        LTexCoord[NewVCount] := LTexCoord[n1]*(1-t) + LTexCoord[n2]*t;
    1208        LPos3D[NewVCount] := LPos3D[n1]*(1-t) + LPos3D[n2]*t;
    1209        LNormal3D[NewVCount] := LNormal3D[n1]*(1-t) + LNormal3D[n2]*t;
    1210        LZ[NewVCount] := LZ[n1]*(1-t) + LZ[n2]*t;
    1211        LProj[NewVCount] := ComputeCoordinate(LPos3D[NewVCount]);
     1229       faceDesc.Colors[NewVCount] := MergeBGRA(faceDesc.Colors[n1],round((1-t)*65536),faceDesc.Colors[n2],round(t*65536));
     1230       faceDesc.TexCoords[NewVCount] := faceDesc.TexCoords[n1]*(1-t) + faceDesc.TexCoords[n2]*t;
     1231       faceDesc.Positions3D[NewVCount] := faceDesc.Positions3D[n1]*(1-t) + faceDesc.Positions3D[n2]*t;
     1232       faceDesc.Normals3D[NewVCount] := faceDesc.Normals3D[n1]*(1-t) + faceDesc.Normals3D[n2]*t;
     1233       faceDesc.Projections[NewVCount] := ComputeCoordinate(faceDesc.Positions3D[NewVCount]);
    12121234       NewVCount += 1;
    12131235    end;
    12141236
    12151237    procedure LoadVertex(idxL: integer; idxV: integer);
    1216     var desc: PBGRAFaceVertexDescription;
     1238    var vertexDesc: PBGRAFaceVertexDescription;
    12171239        tempV: TBGRAVertex3D;
    12181240    begin
    12191241      with LFaces[numFace] do
    12201242      begin
    1221         desc := VertexDescription[idxV];
    1222         with desc^ do
     1243        vertexDesc := VertexDescription[idxV];
     1244        with vertexDesc^ do
    12231245        begin
    12241246          tempV := TBGRAVertex3D(vertex.GetAsObject);
    12251247          LVertices[idxL] := tempV;
    12261248
    1227           if LTexture <> nil then
    1228             LColors[idxL] := BGRA(128,128,128)
    1229           else
    1230           begin
    1231             if ColorOverride then
    1232               LColors[idxL] := Color
    1233             else
    1234             begin
    1235               if tempV.ParentColor then
    1236                 LColors[idxL] := Object3D.Color
    1237               else
    1238                 LColors[idxL] := tempV.Color;
    1239             end;
    1240           end;
    1241 
    1242           if TexCoordOverride then
    1243             LTexCoord[idxL] := TexCoord
    1244           else
    1245             LTexCoord[idxL] := tempV.TexCoord;
    1246           with LMaterial.GetTextureZoom do
    1247           begin
    1248             LTexCoord[idxL].x *= x;
    1249             LTexCoord[idxL].y *= y;
    1250           end;
     1249          faceDesc.Colors[idxL] := ActualColor;
     1250          faceDesc.TexCoords[idxL] := ActualTexCoord;
    12511251
    12521252          with tempV.CoordData^ do
    12531253          begin
    1254             LPos3D[idxL] := viewCoord;
    1255             LNormal3D[idxL] := viewNormal;
    1256             LProj[idxL] := projectedCoord;
    1257             LZ[idxL] := viewCoord.Z;
     1254            faceDesc.Positions3D[idxL] := viewCoord;
     1255            facedesc.Normals3D[idxL] := viewNormal;
     1256            faceDesc.Projections[idxL] := projectedCoord;
    12581257          end;
    12591258          if Normal <> nil then
    1260             LNormal3D[idxL] := Normal.ViewNormal_128;
     1259            facedesc.Normals3D[idxL] := Normal.ViewNormal_128;
     1260          Normalize3D_128(facedesc.Normals3D[idxL]);
    12611261        end;
    12621262      end;
     
    12691269       if VCount < 3 then exit;
    12701270
    1271        if Material <> nil then
    1272          LMaterial := TBGRAMaterial3D(Material.GetAsObject)
    1273        else if Object3D.Material <> nil then
    1274          LMaterial := TBGRAMaterial3D(Object3D.Material.GetAsObject)
    1275        else if self.DefaultMaterial <> nil then
    1276          LMaterial := TBGRAMaterial3D(self.DefaultMaterial.GetAsObject)
    1277        else
    1278          exit;
    1279 
    1280        if ParentTexture then
    1281        begin
    1282          if LMaterial.GetTexture <> nil then
    1283            LTexture := LMaterial.GetTexture
    1284          else
    1285            LTexture := Object3D.Texture
    1286        end
    1287        else
    1288          LTexture := Texture;
    1289 
    1290        LLightNormal := Object3D.LightingNormal;
     1271       faceDesc.NormalsMode := Object3D.LightingNormal;
     1272
     1273       faceDesc.Material := ActualMaterial;
     1274       if faceDesc.Material = nil then exit;
     1275       faceDesc.Texture := ActualTexture;
    12911276
    12921277       if length(LVertices) < VCount+3 then  //keep margin for z-clip
    12931278       begin
    12941279         setlength(LVertices, (VCount+3)*2);
    1295          setlength(LColors, length(LVertices));
    1296          setlength(LTexCoord, length(LVertices));
    1297          setlength(LZ, length(LVertices));
    1298          setlength(LProj, length(LVertices));
    1299          setlength(LPos3D, length(LVertices));
    1300          setlength(LNormal3D, length(LVertices));
    1301          setlength(LLighting, length(LVertices));
     1280         setlength(faceDesc.Colors, length(LVertices));
     1281         setlength(faceDesc.TexCoords, length(LVertices));
     1282         setlength(faceDesc.Projections, length(LVertices));
     1283         setlength(faceDesc.Positions3D, length(LVertices));
     1284         setlength(faceDesc.Normals3D, length(LVertices));
    13021285       end;
    13031286
    1304        NewVCount := 0;
    1305        LastVisibleVertex := -1;
    1306        for k := VCount-1 downto 0 do
    1307          if Vertex[k].ViewCoordZ >= RenderingOptions.MinZ then
    1308          begin
    1309            LastVisibleVertex := k;
    1310            break;
    1311          end;
    1312        if LastVisibleVertex = -1 then exit;
    1313 
    1314        k := VCount-1;
    1315        for j := 0 to VCount-1 do
     1287       if FRenderer.HandlesNearClipping then
    13161288       begin
    1317          if Vertex[j].ViewCoordZ >= RenderingOptions.MinZ then
    1318          begin
    1319            if k <> LastVisibleVertex then   //one or more vertices is out
    1320            begin
    1321              LoadVertex(NewVCount+1, LastVisibleVertex);
    1322              LoadVertex(NewVCount+2, (LastVisibleVertex+1) mod VertexCount);
    1323              AddZIntermediate(NewVCount+1,NewVCount+2);
    1324 
    1325              LoadVertex(NewVCount+1, j);
    1326              LoadVertex(NewVCount+2, k);
    1327 
    1328              AddZIntermediate(NewVCount+1,NewVCount+2);
    1329              inc(NewVCount);
    1330            end else
    1331            begin
    1332              LoadVertex(NewVCount, j);
    1333              NewVCount += 1;
    1334            end;
    1335            LastVisibleVertex := j;
    1336          end;
    1337          k := j;
    1338        end;
    1339        VCount := NewVCount;
    1340        if VCount < 3 then exit; //after z-clipping
    1341 
    1342        if not IsPolyVisible(slice(LProj,VCount)) then
    1343        begin
    1344          if not Biface then exit;
    1345          NegNormals := True;
     1289         for j := 0 to VCount-1 do
     1290           LoadVertex(j,j);
    13461291       end else
    13471292       begin
    1348          NegNormals := False;
     1293         NewVCount := 0;
     1294         LastVisibleVertex := -1;
     1295         for k := VCount-1 downto 0 do
     1296           if Vertex[k].ViewCoordZ >= RenderingOptions.MinZ then
     1297           begin
     1298             LastVisibleVertex := k;
     1299             break;
     1300           end;
     1301         if LastVisibleVertex = -1 then exit;
     1302
     1303         k := VCount-1;
     1304         for j := 0 to VCount-1 do
     1305         begin
     1306           if Vertex[j].ViewCoordZ >= RenderingOptions.MinZ then
     1307           begin
     1308             if k <> LastVisibleVertex then   //one or more vertices is out
     1309             begin
     1310               LoadVertex(NewVCount+1, LastVisibleVertex);
     1311               LoadVertex(NewVCount+2, (LastVisibleVertex+1) mod VertexCount);
     1312               AddZIntermediate(NewVCount+1,NewVCount+2);
     1313
     1314               LoadVertex(NewVCount+1, j);
     1315               LoadVertex(NewVCount+2, k);
     1316
     1317               AddZIntermediate(NewVCount+1,NewVCount+2);
     1318               inc(NewVCount);
     1319             end else
     1320             begin
     1321               LoadVertex(NewVCount, j);
     1322               NewVCount += 1;
     1323             end;
     1324             LastVisibleVertex := j;
     1325           end;
     1326           k := j;
     1327         end;
     1328         VCount := NewVCount;
     1329         if VCount < 3 then exit; //after z-clipping
    13491330       end;
    13501331
    1351        //from here we assume the face will be drawn
    1352        inc(FRenderedFaceCount);
     1332       if not FRenderer.HandlesFaceCulling then
     1333       begin
     1334         if not IsPolyVisible(slice(faceDesc.Projections,VCount)) then
     1335         begin
     1336           if not Biface then exit;
     1337           NegNormals := True;
     1338         end else
     1339         begin
     1340           NegNormals := False;
     1341         end;
     1342       end else
     1343         NegNormals := false;
    13531344
    13541345       //compute normals
    1355        case LLightNormal of
     1346       case faceDesc.NormalsMode of
    13561347         lnFace: for j := 0 to VCount-1 do
    1357                    LNormal3D[j] := ViewNormal_128;
     1348                   faceDesc.Normals3D[j] := ViewNormal_128;
    13581349         lnFaceVertexMix:
    13591350             for j := 0 to VCount-1 do
    13601351             begin
    1361                LNormal3D[j] += ViewNormal_128;
    1362                Normalize3D_128(LNormal3D[j]);
     1352               faceDesc.Normals3D[j] += ViewNormal_128;
     1353               Normalize3D_128(faceDesc.Normals3D[j]);
    13631354             end;
    13641355       end;
    13651356       if NegNormals then
    13661357         for j := 0 to VCount-1 do
    1367            LNormal3D[j] := -LNormal3D[j];
    1368 
    1369        //prepare lighting
    1370        {OnlyDirectionalLight := true;
    1371        for j := 0 to LightCount-1 do
    1372          if not Light[j].IsDirectional then OnlyDirectionalLight := false; }
    1373 
    1374        if LMaterial.GetSpecularOn then
    1375         lightingProc:= TShaderFunction3D(@ApplyLightingWithDiffuseAndSpecularColor) else
    1376        begin
    1377          UseDiffuseColor := UseAmbiantColor;
    1378          if not UseDiffuseColor then
    1379          begin
    1380            with LMaterial.GetDiffuseColorInt do
    1381             UseDiffuseColor := (r <> g) or (g <> b);
    1382            if not UseDiffuseColor and LMaterial.GetAutoDiffuseColor then
    1383            begin
    1384              for j := 0 to LightCount-1 do
    1385                if Light[j].ColoredLight then
    1386                begin
    1387                  UseDiffuseColor := true;
    1388                  break;
    1389                end;
    1390            end;
    1391          end;
    1392          if UseDiffuseColor then
    1393            lightingProc := TShaderFunction3D(@ApplyLightingWithDiffuseColor) else
    1394          begin
    1395            UseDiffuseLightness := FAmbiantLightness <> 32768;
    1396            if not UseDiffuseLightness then
    1397            begin
    1398              if LightCount <> 0 then
    1399                UseDiffuseLightness := true;
    1400            end;
    1401 
    1402            if UseDiffuseLightness then
    1403              lightingProc := TShaderFunction3D(@ApplyLightingWithLightness) else
    1404            if FAmbiantLightness <> 32768 then
    1405             lightingProc := TShaderFunction3D(@ApplyLightingWithAmbiantLightnessOnly) else
    1406               lightingProc := TShaderFunction3D(@ApplyNoLighting);
    1407          end;
    1408        end;
    1409 
    1410        ctx := PSceneLightingContext( shaderContext.Data );
    1411        ctx^.material := LMaterial;
     1358           faceDesc.Normals3D[j] := -faceDesc.Normals3D[j];
     1359
    14121360       if LightThroughFactorOverride then
    1413          ctx^.LightThroughFactor := LightThroughFactor
     1361         faceDesc.LightThroughFactor := LightThroughFactor
    14141362       else
    1415          ctx^.LightThroughFactor := LMaterial.GetLightThroughFactor;
    1416        ctx^.LightThrough := ctx^.LightThroughFactor > 0;
    1417        ctx^.SaturationHighF := LMaterial.GetSaturationHigh;
    1418        ctx^.SaturationLowF := LMaterial.GetSaturationLow;
    1419        ctx^.SaturationHigh := round(LMaterial.GetSaturationHigh*32768);
    1420        ctx^.SaturationLow := round(LMaterial.GetSaturationLow*32768);
    1421 
    1422        //high-quality lighting interpolation, necessary for Phong and high-quality Gouraud
    1423        if (
    1424            (RenderingOptions.LightingInterpolation = liAlwaysHighQuality) or
    1425            ((RenderingOptions.LightingInterpolation = liSpecularHighQuality) and LMaterial.GetSpecularOn)
    1426        ) and (LLightNormal <> lnNone) {and (not (LLightNormal = lnFace) and OnlyDirectionalLight) }then
    1427        begin
    1428          if LTexture = nil then
    1429            DrawAliasedColoredFace(lightingProc,VCount,PBasicLightingContext(ctx)) //use shader
    1430          else
    1431            BGRAPolygonAliased.PolygonPerspectiveMappingShaderAliased(ASurface,
    1432                slice(LProj,VCount),slice(LPos3D,VCount),slice(LNormal3D,VCount),LTexture,
    1433                  slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation,lightingProc,True, BGRAPixelTransparent,zbuffer,PBasicLightingContext(ctx));
    1434 
    1435          exit;
    1436        end;
    1437 
    1438        //Vertex lighting interpolation (low-quality Gouraud, low-quality Phong)
    1439        LNoLighting := True;
    1440        for j := 0 to VCount-1 do
    1441        begin
    1442          with ctx^ do
    1443          begin
    1444            basic.Position := LPos3D[j];
    1445            basic.Normal := LNormal3D[j];
    1446          end;
    1447          LColors[j] := lightingProc(PBasicLightingContext(ctx),LColors[j]);
    1448          if LColors[j] <> BGRA(128,128,128) then
    1449            LNoLighting := false;
    1450        end;
    1451 
    1452        if (AAntialiasingMode = am3dMultishape) and not (RenderingOptions.PerspectiveMode = pmZBuffer) then //high-quality antialiasing
    1453        begin
    1454          if LTexture <> nil then
    1455          begin
    1456            if (RenderingOptions.PerspectiveMode <> pmLinearMapping) and (VCount=4) then
    1457              multi.AddQuadPerspectiveMapping(LProj[0],LProj[1],LProj[2],LProj[3],LTexture,LTexCoord[0],LTexCoord[1],LTexCoord[2],LTexCoord[3])
    1458            else
    1459            if VCount>=3 then
    1460            begin
    1461              for j := 0 to VCount-3 do
    1462                multi.AddTriangleLinearMapping(LProj[j],LProj[j+1],LProj[j+2],LTexture,LTexCoord[j],LTexCoord[j+1],LTexCoord[j+2]);
    1463            end;
    1464          end
    1465          else
    1466          begin
    1467            SameColor := True;
    1468            for j := 1 to VCount-1 do
    1469              if (LColors[j]<>LColors[j-1]) then SameColor := False;
    1470 
    1471            if SameColor then
    1472              multi.AddPolygon(slice(LProj,VCount),LColors[0])
    1473            else
    1474            if VCount=3 then
    1475              multi.AddTriangleLinearColor(LProj[0],LProj[1],LProj[2],LColors[0],LColors[1],LColors[2])
    1476            else
    1477            if VCount>=3 then
    1478            begin  //split into triangles
    1479              PtCenter3D := Point3D_128_Zero;
    1480              for j := 0 to VCount-1 do
    1481                PtCenter3D += LPos3D[j];
    1482              PtCenter3D *= (1/VCount);
    1483              PtCenter := ComputeCoordinate(PtCenter3D);
    1484              ColorCenter := MergeBGRA(slice(LColors,VCount));
    1485              k := VCount-1;
    1486              for j := 0 to VCount-1 do
    1487              begin
    1488                multi.AddTriangleLinearColor(LProj[k],LProj[j],PtCenter,LColors[k],LColors[j],ColorCenter);
    1489                k := j;
    1490              end;
    1491            end;
    1492          end;
    1493        end else
    1494        begin
    1495          if LTexture <> nil then
    1496          begin
    1497            if LNoLighting then
    1498            begin
    1499              if RenderingOptions.PerspectiveMode <> pmLinearMapping then
    1500                ASurface.FillPolyPerspectiveMapping(slice(LProj,VCount),slice(LZ,VCount),LTexture,slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation, zbuffer)
    1501              else
    1502                ASurface.FillPolyLinearMapping(slice(LProj,VCount),LTexture,slice(LTexCoord,VCount),RenderingOptions.TextureInterpolation);
    1503            end else
    1504            begin
    1505              for j := 0 to VCount-1 do
    1506                LLighting[j] := LColors[j].green shl 8;
    1507              if RenderingOptions.PerspectiveMode <> pmLinearMapping then
    1508                ASurface.FillPolyPerspectiveMappingLightness(slice(LProj,VCount),slice(LZ,VCount),LTexture,slice(LTexCoord,VCount),slice(LLighting,VCount),RenderingOptions.TextureInterpolation, zbuffer)
    1509              else
    1510                ASurface.FillPolyLinearMappingLightness(slice(LProj,VCount),LTexture,slice(LTexCoord,VCount),slice(LLighting,VCount),RenderingOptions.TextureInterpolation);
    1511            end;
    1512          end
    1513          else
    1514            DrawAliasedColoredFace(nil,VCount,PBasicLightingContext(ctx));  //already low-quality shaded
    1515        end;
     1363         faceDesc.LightThroughFactor := faceDesc.Material.GetLightThroughFactor;
     1364
     1365       faceDesc.NbVertices:= VCount;
     1366       faceDesc.Biface := Biface;
     1367
     1368       if FRenderer.RenderFace(faceDesc, @ComputeCoordinate) then
     1369         inc(FRenderedFaceCount);
    15161370     end;
    15171371  end;
    15181372
    1519   procedure DrawWithResample;
    1520   var
    1521     tempSurface: TBGRACustomBitmap;
    1522   begin
    1523     tempSurface := ASurface.NewBitmap(ASurface.Width*RenderingOptions.AntialiasingResampleLevel,ASurface.Height*RenderingOptions.AntialiasingResampleLevel);
    1524     InternalRender(tempSurface, am3dNone, RenderingOptions.AntialiasingResampleLevel);
    1525     BGRAResample.DownSamplePutImage(tempSurface,RenderingOptions.AntialiasingResampleLevel,RenderingOptions.AntialiasingResampleLevel,
    1526                  ASurface, 0,0, dmDrawWithTransparency);
    1527     tempSurface.Free;
    1528   end;
    1529 
    15301373var i,j: integer;
    15311374
     
    15331376  FRenderedFaceCount:= 0;
    15341377
    1535   if ASurface = nil then
    1536     raise exception.Create('No surface specified');
    1537 
    1538   if (AAntialiasingMode = am3dResample) and (RenderingOptions.AntialiasingResampleLevel > 1) then
    1539   begin
    1540     DrawWithResample;
    1541     exit;
    1542   end;
    1543 
    15441378  PrepareFaces;
    1545   ComputeView(GlobalScale,GlobalScale);
    1546   ComputeLight;
    1547   UseAmbiantColor := (FAmbiantLightColor.r <> FAmbiantLightColor.g) or (FAmbiantLightColor.g <> FAmbiantLightColor.b);
     1379  ComputeView(FRenderer.GlobalScale,FRenderer.GlobalScale);
     1380  FRenderer.Projection := FProjection;
    15481381
    15491382  SortFaces(LFaces);
    15501383  LVertices := nil;
    15511384
    1552   if AAntialiasingMode = am3dMultishape then
    1553   begin
    1554     multi := TBGRAMultishapeFiller.Create;
    1555     multi.PolygonOrder := poLastOnTop;
    1556   end
    1557   else
    1558     multi := nil;
    1559 
    1560   ColorGradientTempBmp := ASurface.NewBitmap(2,2);
    1561   ColorGradientTempBmp.ScanInterpolationFilter := rfLinear;
    1562 
    1563   if RenderingOptions.PerspectiveMode = pmZBuffer then
    1564   begin
    1565     getmem(zbuffer, ASurface.NbPixels*sizeof(single));
    1566     FillDWord(zbuffer^, ASurface.NbPixels, dword(single(0)));
    1567   end
    1568   else
    1569     zbuffer := nil;
    1570 
    1571   shaderContext := TMemoryBlockAlign128.Create(sizeof(TSceneLightingContext));
    1572 
    1573   if zbuffer <> nil then
     1385  //if there is a Z-Buffer, it is possible to avoid drawing things that
     1386  //are hidden by opaque faces by drawing first all opaque faces
     1387  if FRenderer.HasZBuffer then
    15741388  begin
    15751389    setlength(LFaceOpaque, length(LFaces));
     
    16021416      DrawFace(i);
    16031417  end;
    1604 
    1605   shaderContext.Free;
    1606   if zbuffer <> nil then freemem(zbuffer);
    1607   ColorGradientTempBmp.Free;
    1608 
    1609   if multi <> nil then
    1610   begin
    1611     multi.Draw(ASurface);
    1612     multi.Free;
    1613   end;
    1614 end;
    1615 
    1616 procedure TBGRAScene3D.SetViewPoint(const AValue: TPoint3D);
    1617 begin
    1618   FViewPoint := Point3D_128(AValue);
    1619 end;
    1620 
    1621 function TBGRAScene3D.ApplyLightingWithLightness(Context: PSceneLightingContext;
    1622   Color: TBGRAPixel): TBGRAPixel;
    1623 var i: Integer;
    1624   m: TBGRAMaterial3D;
    1625 begin
    1626   m := TBGRAMaterial3D(Context^.material);
    1627   if not m.GetAutoSimpleColor then Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetSimpleColorInt));
    1628 
    1629   Context^.lightness := FAmbiantLightness;
    1630 
    1631   i := FLights.Count-1;
    1632   while i >= 0 do
    1633   begin
    1634     TBGRALight3D(FLights[i]).ComputeDiffuseLightness(Context);
    1635     dec(i);
    1636   end;
    1637 
    1638   with Context^ do
    1639     if Lightness <= 0 then
    1640       result := BGRA(0,0,0,color.alpha)
    1641     else
    1642     begin
    1643       if Lightness <= SaturationLow then
    1644         result := ApplyIntensityFast(Color, Lightness)
    1645       else if Lightness >= SaturationHigh then
    1646         result := BGRA(255,255,255,color.alpha)
    1647       else
    1648         result := ApplyLightnessFast( ApplyIntensityFast(Color, SaturationLow),
    1649                               (Lightness - SaturationLow)*32767 div (SaturationHigh-SaturationLow)+32768 );
    1650     end;
    1651 end;
    1652 
    1653 function TBGRAScene3D.ApplyLightingWithDiffuseColor(Context: PSceneLightingContext;
    1654   Color: TBGRAPixel): TBGRAPixel;
    1655 var i: Integer;
    1656   m: TBGRAMaterial3D;
    1657 begin
    1658   m := TBGRAMaterial3D(Context^.material);
    1659 
    1660   if m.GetAutoAmbiantColor then
    1661     Context^.diffuseColor := FAmbiantLightColor
    1662   else
    1663     Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;
    1664 
    1665   i := FLights.Count-1;
    1666   while i >= 0 do
    1667   begin
    1668     TBGRALight3D(FLights[i]).ComputeDiffuseColor(Context);
    1669     dec(i);
    1670   end;
    1671 
    1672   result := ColorIntToBGRA(BGRAToColorIntMultiply(Color,Context^.diffuseColor));
    1673   result.alpha := Color.alpha;
    1674 end;
    1675 
    1676 function TBGRAScene3D.ApplyLightingWithDiffuseAndSpecularColor(Context: PSceneLightingContext;
    1677   Color: TBGRAPixel): TBGRAPixel;
    1678 var i: Integer;
    1679   m: TBGRAMaterial3D;
    1680 begin
    1681   m := TBGRAMaterial3D(Context^.material);
    1682 
    1683   if m.GetAutoAmbiantColor then
    1684     Context^.diffuseColor := FAmbiantLightColor
    1685   else
    1686     Context^.diffuseColor := FAmbiantLightColor*m.GetAmbiantColorInt;
    1687   Context^.specularColor := ColorInt65536(0,0,0,0);
    1688 
    1689   i := FLights.Count-1;
    1690   while i >= 0 do
    1691   begin
    1692     TBGRALight3D(FLights[i]).ComputeDiffuseAndSpecularColor(Context);
    1693     dec(i);
    1694   end;
    1695 
    1696   with Context^ do
    1697   begin
    1698     diffuseColor.a := 65536;
    1699     result := ColorIntToBGRA(BGRAToColorIntMultiply(Color,diffuseColor) + specularColor);
    1700   end;
    1701 end;
    1702 
    1703 function TBGRAScene3D.ApplyNoLighting(Context: PSceneLightingContext;
    1704   Color: TBGRAPixel): TBGRAPixel;
    1705 var
    1706   m: TBGRAMaterial3D;
    1707 begin
    1708   m := TBGRAMaterial3D(Context^.material);
    1709 
    1710   if not m.GetAutoAmbiantColor then
    1711     result := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt))
    1712   else
    1713     result := Color;
    1714 end;
    1715 
    1716 function TBGRAScene3D.ApplyLightingWithAmbiantLightnessOnly(
    1717   Context: PSceneLightingContext; Color: TBGRAPixel): TBGRAPixel;
    1718 var
    1719   m: TBGRAMaterial3D;
    1720 begin
    1721   m := TBGRAMaterial3D(Context^.material);
    1722 
    1723   if not m.GetAutoAmbiantColor then
    1724     Color := ColorIntToBGRA(BGRAToColorIntMultiply(Color, m.GetAmbiantColorInt));
    1725 
    1726   if FAmbiantLightness <= 0 then
    1727     result := BGRA(0,0,0,color.alpha)
    1728   else
    1729     result := ApplyIntensityFast(Color, FAmbiantLightness);
    17301418end;
    17311419
     
    18601548
    18611549function TBGRAScene3D.CreateMaterial: IBGRAMaterial3D;
    1862 begin
    1863   result := TBGRAMaterial3D.Create;
     1550var m: TBGRAMaterial3D;
     1551begin
     1552  m := TBGRAMaterial3D.Create;
     1553  m.OnTextureChanged := @OnMaterialTextureChanged;
     1554  result := m;
    18641555  AddMaterial(result);
    18651556end;
    18661557
    18671558function TBGRAScene3D.CreateMaterial(ASpecularIndex: integer): IBGRAMaterial3D;
    1868 begin
    1869   result := TBGRAMaterial3D.Create;
    1870   result.SpecularIndex := ASpecularIndex;
    1871   result.SpecularColor := BGRAWhite;
     1559var m: TBGRAMaterial3D;
     1560begin
     1561  m := TBGRAMaterial3D.Create;
     1562  m.SetSpecularIndex(ASpecularIndex);
     1563  m.SetSpecularColor(BGRAWhite);
     1564  m.OnTextureChanged := @OnMaterialTextureChanged;
     1565  result := m;
    18721566  AddMaterial(result);
    18731567end;
     
    19331627end;
    19341628
     1629function TBGRAScene3D.MakeLightList: TList;
     1630var i: integer;
     1631begin
     1632  result := TList.Create;
     1633  for i := 0 to FLights.Count-1 do
     1634    result.Add(FLights[i]);
     1635end;
     1636
    19351637initialization
    19361638
  • GraphicTest/Packages/bgrabitmap/bgraslicescaling.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, Graphics, BGRABitmap, BGRABitmapTypes, IniFiles, FileUtil;
     8  Classes, SysUtils, BGRAGraphics, BGRABitmap, BGRABitmapTypes, IniFiles;
    99
    1010type
     
    151151implementation
    152152
    153 uses types;
     153uses BGRAUTF8, Types;
    154154
    155155function Margins(ATop, ARight, ABottom, ALeft: integer): TMargins;
     
    759759        break;
    760760      end;
    761       Inc(p);
    762761    end;
    763762    if not isRepeating then
  • GraphicTest/Packages/bgrabitmap/bgrasse.inc

    r472 r494  
    1 {$IFDEF CPUI386}
    2   {$DEFINE BGRASSE_AVAILABLE}
     1{$IFDEF SSE_LOADV}
     2  {$UNDEF SSE_LOADV}
     3  {$ifdef cpux86_64}
     4  mov rax,v
     5  movups xmm1,[rax]
     6  {$else}
     7  mov eax,v
     8  movups xmm1,[eax]
     9  {$endif}
     10{$ELSE}
     11  {$IFDEF SSE_SAVEV}
     12    {$UNDEF SSE_SAVEV}
     13    {$ifdef cpux86_64}
     14    mov rax,v
     15    movups [rax],xmm1
     16    {$else}
     17    mov eax,v
     18    movups [eax],xmm1
     19    {$endif} 
     20  {$ELSE}
     21    {$IFDEF CPUI386}
     22      {$DEFINE BGRASSE_AVAILABLE}
     23    {$ENDIF}
     24    {$IFDEF cpux86_64}
     25      {$DEFINE BGRASSE_AVAILABLE}
     26    {$ENDIF}
     27  {$ENDIF}
    328{$ENDIF}
    4 {$IFDEF cpux86_64}
    5   {$DEFINE BGRASSE_AVAILABLE}
    6 {$ENDIF}
    7 
  • GraphicTest/Packages/bgrabitmap/bgrasse.pas

    r472 r494  
    2121var UseSSE, UseSSE2, UseSSE3 : boolean;
    2222
     23{$ifdef CPUI386}
     24  {$asmmode intel}
     25{$ENDIF}
     26{$ifdef cpux86_64}
     27  {$asmmode intel}
     28{$ENDIF}
     29
    2330{$ifdef BGRASSE_AVAILABLE}
    24   {$asmmode intel}
    2531  //SSE rotate singles
    2632  const Shift231 = 1 + 8;
     
    351357begin
    352358  asm
    353     {$i sseloadv.inc}
     359    {$DEFINE SSE_LOADV}{$i bgrasse.inc}
    354360    movaps xmm2, xmm1
    355361    mulps xmm2, xmm2
     
    377383    rsqrtps xmm2, xmm2
    378384    mulps xmm1, xmm2  //apply
    379     {$i ssesavev.inc}
     385    {$DEFINE SSE_SAVEV}{$i bgrasse.inc}
    380386  end;
    381387end;
     
    387393begin
    388394  asm
    389     {$i sseloadv.inc}
     395    {$DEFINE SSE_LOADV}{$i bgrasse.inc}
    390396    movaps xmm2, xmm1
    391397    mulps xmm2, xmm2
     
    407413    rsqrtps xmm2, xmm2
    408414    mulps xmm1, xmm2  //apply
    409     {$i ssesavev.inc}
     415    {$DEFINE SSE_SAVEV}{$i bgrasse.inc}
    410416  end;
    411417end;
     
    419425    begin
    420426      asm
    421         {$i sseloadv.inc}
     427        {$DEFINE SSE_LOADV}{$i bgrasse.inc}
    422428        movaps xmm2, xmm1
    423429        mulps xmm2, xmm2
     
    451457        rsqrtps xmm2, xmm2
    452458        mulps xmm1, xmm2  //apply
    453         {$i ssesavev.inc}
     459        {$DEFINE SSE_SAVEV}{$i bgrasse.inc}
    454460      end;
    455461    end
  • GraphicTest/Packages/bgrabitmap/bgrastreamlayers.pas

    r472 r494  
    1717implementation
    1818
    19 uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp;
     19uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp,
     20     BGRAUTF8;
    2021
    2122procedure SaveLayeredBitmapToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
     
    3738  StreamMaxLayerCount = 4096;
    3839  StreamMaxHeaderSize = 256;
    39 
    40 {$i winstream.inc}
    4140
    4241function CheckStreamForLayers(AStream: TStream): boolean;
     
    9190
    9291    //header size
    93     HeaderSize:= WinReadLongint(AStream);
     92    HeaderSize:= LEReadLongint(AStream);
    9493    if (HeaderSize < 12) or (HeaderSize > StreamMaxHeaderSize) then
    9594      raise exception.Create('Invalid header size');
    9695    LayerStackStartPosition := AStream.Position + HeaderSize;
    9796
    98     NbLayers:= WinReadLongint(AStream);
     97    NbLayers:= LEReadLongint(AStream);
    9998    if (NbLayers < 0) or (NbLayers > StreamMaxLayerCount) then
    10099      raise exception.Create('Invalid layer count');
    101100
    102     ASelectedLayerIndex:= WinReadLongint(AStream);
     101    ASelectedLayerIndex:= LEReadLongint(AStream);
    103102    if (ASelectedLayerIndex < -1) or (ASelectedLayerIndex >= NbLayers) then
    104103      raise exception.Create('Selected layer out of bounds');
    105104
    106     StackOption := WinReadLongint(AStream);
     105    StackOption := LEReadLongint(AStream);
    107106    result.LinearBlend := (StackOption and 1) = 1;
    108107    if (StackOption and 2) = 2 then Compression := lzpRLE else Compression:= lzpZStream;
     
    112111    for i := 0 to NbLayers-1 do
    113112    begin
    114       LayerHeaderSize:= WinReadLongint(AStream);
     113      LayerHeaderSize:= LEReadLongint(AStream);
    115114      LayerHeaderPosition := AStream.Position;
    116115      LayerBitmapPosition := LayerHeaderPosition + LayerHeaderSize;
     
    126125      if AStream.Position <= LayerBitmapPosition-4 then
    127126      begin
    128         LayerOption := WinReadLongint(AStream);
     127        LayerOption := LEReadLongint(AStream);
    129128        LayerVisible := (LayerOption and 1) = 1;
    130129      end;
    131130      if AStream.Position <= LayerBitmapPosition-4 then
    132         LayerBlendOp := TBlendOperation(WinReadLongint(AStream));
     131        LayerBlendOp := TBlendOperation(LEReadLongint(AStream));
    133132
    134133      if AStream.Position <= LayerBitmapPosition-8 then
    135134      begin
    136         LayerOffset := Point(WinReadLongint(AStream),WinReadLongint(AStream));
     135        LayerOffset := Point(LEReadLongint(AStream),LEReadLongint(AStream));
    137136        if AStream.Position <= LayerBitmapPosition-4 then
    138137        begin
    139           LayerId := WinReadLongint(AStream);
     138          LayerId := LEReadLongint(AStream);
    140139          LayerIdFound := true;
    141140        end;
    142141        if AStream.Position <= LayerBitmapPosition-4 then
    143           LayerOpacity := WinReadLongint(AStream) shr 8;
     142          LayerOpacity := LEReadLongint(AStream) shr 8;
    144143      end;
    145144      if AStream.Position <= LayerBitmapPosition-4 then
    146145      begin
    147         LayerBitmapSize := WinReadLongint(AStream);
     146        LayerBitmapSize := LEReadLongint(AStream);
    148147        LayerEndPosition:= LayerBitmapPosition+LayerBitmapSize;
    149148      end;
     
    186185    raise exception.Create('Selected layer out of bounds');
    187186  AStream.Write(StreamHeader[1], length(StreamHeader));
    188   WinWriteLongint(AStream, 12); //header size
    189   WinWriteLongint(AStream, ALayers.NbLayers);
    190   WinWriteLongint(AStream, ASelectedLayerIndex);
     187  LEWriteLongint(AStream, 12); //header size
     188  LEWriteLongint(AStream, ALayers.NbLayers);
     189  LEWriteLongint(AStream, ASelectedLayerIndex);
    191190  StackOption := 0;
    192191  if ALayers.LinearBlend then StackOption := StackOption or 1;
    193192  if ACompression = lzpRLE then StackOption:= StackOption or 2;
    194   WinWriteLongint(AStream, StackOption);
     193  LEWriteLongint(AStream, StackOption);
    195194  //end of header
    196195
     
    198197  begin
    199198    LayerHeaderSizePosition:= AStream.Position;
    200     WinWriteLongint(AStream, 0); //header size not computed yet
     199    LEWriteLongint(AStream, 0); //header size not computed yet
    201200    LayerHeaderPosition := AStream.Position;
    202201
    203202    LayerOption := 0;
    204203    if ALayers.LayerVisible[i] then LayerOption:= LayerOption or 1;
    205     WinWriteLongint(AStream, LayerOption);
    206     WinWriteLongint(AStream, Longint(ALayers.BlendOperation[i]));
    207     WinWriteLongint(AStream, ALayers.LayerOffset[i].x);
    208     WinWriteLongint(AStream, ALayers.LayerOffset[i].y);
    209     WinWriteLongint(AStream, ALayers.LayerUniqueId[i]);
    210     WinWriteLongint(AStream, integer(ALayers.LayerOpacity[i])*$101);
     204    LEWriteLongint(AStream, LayerOption);
     205    LEWriteLongint(AStream, Longint(ALayers.BlendOperation[i]));
     206    LEWriteLongint(AStream, ALayers.LayerOffset[i].x);
     207    LEWriteLongint(AStream, ALayers.LayerOffset[i].y);
     208    LEWriteLongint(AStream, ALayers.LayerUniqueId[i]);
     209    LEWriteLongint(AStream, integer(ALayers.LayerOpacity[i])*$101);
    211210    LayerBitmapSizePosition:=AStream.Position;
    212     WinWriteLongint(AStream, 0);
     211    LEWriteLongint(AStream, 0);
    213212    LayerBitmapPosition:=AStream.Position;
    214213    LayerHeaderSize := LayerBitmapPosition - LayerHeaderPosition;
    215214    AStream.Position:= LayerHeaderSizePosition;
    216     WinWriteLongint(AStream, LayerHeaderSize);
     215    LEWriteLongint(AStream, LayerHeaderSize);
    217216    //end of layer header
    218217
     
    231230      raise exception.Create('Image too big');
    232231    AStream.Position:= LayerBitmapSizePosition;
    233     WinWriteLongint(AStream, BitmapSize);
     232    LEWriteLongint(AStream, BitmapSize);
    234233    AStream.Position:= LayerBitmapPosition+BitmapSize;
    235234  end;
  • GraphicTest/Packages/bgrabitmap/bgrasvg.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, BGRABitmapTypes, laz2_DOM, BGRAUnits, BGRASVGShapes, BGRACanvas2D;
     8  Classes, SysUtils, BGRABitmapTypes, laz2_DOM, BGRAUnits, BGRASVGShapes,
     9  BGRACanvas2D;
    910
    1011type
     
    6465    function GetPreserveAspectRatio: string;
    6566    function GetViewBox: TSVGViewBox;
     67    function GetViewBox(AUnit: TCSSUnit): TSVGViewBox;
     68    procedure GetViewBoxIndirect(AUnit: TCSSUnit; out AViewBox: TSVGViewBox);
    6669    function GetWidth: TFloatWithCSSUnit;
    6770    function GetWidthAsCm: single;
     
    8790    FContent: TSVGContent;
    8891    procedure Init(ACreateEmpty: boolean);
    89     procedure InternalDraw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit; destDpi: TPointF); overload;
    90     procedure InternalDraw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit; destDpi: single); overload;
     92    function GetViewBoxAlignment(AHorizAlign: TAlignment; AVertAlign: TTextLayout): TPointF;
    9193  public
    9294    constructor Create; overload;
     
    100102    procedure SaveToFile(AFilenameUTF8: string);
    101103    procedure SaveToStream(AStream: TStream);
     104    procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; AUnit: TCSSUnit = cuPixel); overload;
     105    procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; destDpi: single); overload;
     106    procedure Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y: single; destDpi: TPointF); overload;
     107    procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit = cuPixel); overload;
    102108    procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; destDpi: single); overload;
    103109    procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; destDpi: TPointF); overload;
    104     procedure Draw(ACanvas2d: TBGRACanvas2D; x,y: single; AUnit: TCSSUnit); overload;
     110    procedure StretchDraw(ACanvas2d: TBGRACanvas2D; x,y,w,h: single); overload;
     111    procedure StretchDraw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment; AVertAlign: TTextLayout; x,y,w,h: single); overload;
    105112    property Units: TSVGUnits read FUnits;
    106113    property Width: TFloatWithCSSUnit read GetWidth write SetWidth;
     
    112119    property Zoomable: boolean read GetZoomable write SetZoomable;
    113120    property ViewBox: TSVGViewBox read GetViewBox write SetViewBox;
     121    property ViewBoxInUnit[AUnit: TCSSUnit]: TSVGViewBox read GetViewBox;
    114122    property Attribute[AName: string]: string read GetAttribute write SetAttribute;
    115123    property DefaultDpi: single read FDefaultDpi write SetDefaultDpi; //this is not saved in the SVG file
     
    121129implementation
    122130
    123 uses laz2_XMLRead, laz2_XMLWrite, lazutf8classes, BGRATransform;
     131uses laz2_XMLRead, laz2_XMLWrite, BGRAUTF8;
    124132
    125133const SvgNamespace = 'http://www.w3.org/2000/svg';
     
    168176  function parseNextFloat: single;
    169177  var
    170     idxSpace,errPos: integer;
     178    idxSpace,{%H-}errPos: integer;
    171179  begin
    172180    idxSpace:= pos(' ',viewBoxStr);
     
    335343end;
    336344
     345function TBGRASVG.GetViewBox(AUnit: TCSSUnit): TSVGViewBox;
     346begin
     347  GetViewBoxIndirect(AUnit,result);
     348end;
     349
     350procedure TBGRASVG.GetViewBoxIndirect(AUnit: TCSSUnit; out AViewBox: TSVGViewBox);
     351begin
     352  with FUnits.ViewBox do
     353  begin
     354    AViewBox.min := FUnits.ConvertCoord(min,cuCustom,AUnit);
     355    AViewBox.size := FUnits.ConvertCoord(size,cuCustom,AUnit);
     356  end;
     357end;
     358
    337359function TBGRASVG.GetWidth: TFloatWithCSSUnit;
    338360begin
     
    443465end;
    444466
     467function TBGRASVG.GetViewBoxAlignment(AHorizAlign: TAlignment;
     468  AVertAlign: TTextLayout): TPointF;
     469var vb: TSVGViewBox;
     470begin
     471  GetViewBoxIndirect(cuPixel, vb);
     472  with vb do
     473  begin
     474    case AHorizAlign of
     475      taCenter: result.x := -(min.x+size.x*0.5);
     476      taRightJustify: result.x := -(min.x+size.x);
     477    else
     478      {taLeftJustify:} result.x := -min.x;
     479    end;
     480    case AVertAlign of
     481      tlCenter: result.y := -(min.y+size.y*0.5);
     482      tlBottom: result.y := -(min.y+size.y);
     483    else
     484      {tlTop:} result.y := -min.y;
     485    end;
     486  end;
     487end;
     488
    445489constructor TBGRASVG.Create;
    446490begin
     
    547591end;
    548592
    549 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; AUnit: TCSSUnit);
     593procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment;
     594  AVertAlign: TTextLayout; x, y: single; AUnit: TCSSUnit);
    550595var prevMatrix: TAffineMatrix;
    551596begin
    552   if (x<>0) or (y<>0) then
    553   begin
    554     prevMatrix := ACanvas2d.matrix;
    555     ACanvas2d.translate(x,y);
    556     Content.Draw(ACanvas2d,AUnit);
    557     ACanvas2d.matrix := prevMatrix;
    558   end else
    559     Content.Draw(ACanvas2d,AUnit);
    560 end;
    561 
    562 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: TPointF);
    563 begin
    564   InternalDraw(ACanvas2d,x,y,cuPixel,destDpi);
    565 end;
    566 
    567 procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: single);
    568 begin
    569   InternalDraw(ACanvas2d,x,y,cuPixel,destDpi);
    570 end;
    571 
    572 procedure TBGRASVG.InternalDraw(ACanvas2d: TBGRACanvas2D; x, y: single;
    573   AUnit: TCSSUnit; destDpi: TPointF);
    574 var prevMatrix: TAffineMatrix;
    575 begin
    576   if (Units.DpiX = 0) or (Units.DpiY = 0) then exit;
    577597  prevMatrix := ACanvas2d.matrix;
    578598  ACanvas2d.translate(x,y);
    579   if AUnit = cuPixel then
    580     ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY);
     599  with GetViewBoxAlignment(AHorizAlign,AVertAlign) do ACanvas2d.translate(x,y);
     600  Draw(ACanvas2d, 0,0, AUnit);
     601  ACanvas2d.matrix := prevMatrix;
     602end;
     603
     604procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment;
     605  AVertAlign: TTextLayout; x, y: single; destDpi: single);
     606begin
     607  Draw(ACanvas2d, AHorizAlign,AVertAlign, x,y, PointF(destDpi,destDpi));
     608end;
     609
     610procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; AHorizAlign: TAlignment;
     611  AVertAlign: TTextLayout; x, y: single; destDpi: TPointF);
     612begin
     613  ACanvas2d.save;
     614  ACanvas2d.translate(x,y);
     615  ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY);
     616  ACanvas2d.strokeResetTransform;
     617  ACanvas2d.strokeScale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY);
     618  with GetViewBoxAlignment(AHorizAlign,AVertAlign) do ACanvas2d.translate(x,y);
     619  Draw(ACanvas2d, 0,0, cuPixel);
     620  ACanvas2d.restore;
     621end;
     622
     623procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; AUnit: TCSSUnit);
     624var prevLinearBlend: boolean;
     625begin
     626  prevLinearBlend:= ACanvas2d.linearBlend;
     627  acanvas2d.linearBlend := true;
     628  ACanvas2d.save;
     629  ACanvas2d.translate(x,y);
    581630  Content.Draw(ACanvas2d,AUnit);
    582   ACanvas2d.matrix := prevMatrix;
    583 end;
    584 
    585 procedure TBGRASVG.InternalDraw(ACanvas2d: TBGRACanvas2D; x, y: single;
    586   AUnit: TCSSUnit; destDpi: single);
    587 begin
    588   InternalDraw(ACanvas2d,x,y,AUnit,PointF(destDpi,destDpi));
     631  ACanvas2d.restore;
     632  ACanvas2d.linearBlend := prevLinearBlend;
     633end;
     634
     635procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: single);
     636begin
     637  Draw(ACanvas2d, x,y, PointF(destDpi,destDpi));
     638end;
     639
     640procedure TBGRASVG.Draw(ACanvas2d: TBGRACanvas2D; x, y: single; destDpi: TPointF);
     641begin
     642  ACanvas2d.save;
     643  ACanvas2d.translate(x,y);
     644  ACanvas2d.scale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY);
     645  ACanvas2d.strokeResetTransform;
     646  ACanvas2d.strokeScale(destDpi.x/Units.DpiX,destDpi.y/Units.DpiY);
     647  Draw(ACanvas2d, 0,0, cuPixel);
     648  ACanvas2d.restore;
     649end;
     650
     651procedure TBGRASVG.StretchDraw(ACanvas2d: TBGRACanvas2D; x, y, w, h: single);
     652var vb: TSVGViewBox;
     653begin
     654  ACanvas2d.save;
     655  ACanvas2d.translate(x,y);
     656  ACanvas2d.strokeResetTransform;
     657  GetViewBoxIndirect(cuPixel,vb);
     658  with vb do
     659  begin
     660    ACanvas2d.translate(-min.x,-min.y);
     661    if size.x <> 0 then
     662    begin
     663      ACanvas2d.scale(w/size.x,1);
     664      ACanvas2d.strokeScale(w/size.x,1);
     665    end;
     666    if size.y <> 0 then
     667    begin
     668      ACanvas2d.scale(1,h/size.y);
     669      ACanvas2d.strokeScale(1,h/size.y);
     670    end;
     671  end;
     672  Draw(ACanvas2d, 0,0);
     673  ACanvas2d.restore;
     674end;
     675
     676procedure TBGRASVG.StretchDraw(ACanvas2d: TBGRACanvas2D;
     677  AHorizAlign: TAlignment; AVertAlign: TTextLayout; x, y, w, h: single);
     678var ratio,stretchRatio,zoom: single;
     679  vb: TSVGViewBox;
     680  sx,sy,sw,sh: single;
     681begin
     682  GetViewBoxIndirect(cuPixel,vb);
     683  if (h = 0) or (w = 0) or (vb.size.x = 0) or (vb.size.y = 0) then exit;
     684  ratio := vb.size.x/vb.size.y;
     685  stretchRatio := w/h;
     686  if ratio > stretchRatio then
     687    zoom := w / vb.size.x
     688  else
     689    zoom := h / vb.size.y;
     690
     691  sx := x;
     692  sy := y;
     693  sw := vb.size.x*zoom;
     694  sh := vb.size.y*zoom;
     695
     696  case AHorizAlign of
     697    taCenter: sx += (w - sw)/2;
     698    taRightJustify: sx += w - sw;
     699  end;
     700  case AVertAlign of
     701    tlCenter: sy += (h - sh)/2;
     702    tlBottom: sy += h - sh;
     703  end;
     704  StretchDraw(ACanvas2d, sx,sy,sw,sh);
    589705end;
    590706
  • GraphicTest/Packages/bgrabitmap/bgrasvgshapes.pas

    r472 r494  
    243243implementation
    244244
    245 uses BGRATransform, Graphics;
     245uses BGRATransform, BGRAGraphics;
    246246
    247247function GetSVGFactory(ATagName: string): TSVGFactory;
     
    414414  if not isStrokeNone then
    415415  begin
    416     ACanvas2d.strokeStyle(strokeColor);
     416    ApplyStrokeStyle(ACanvas2D,AUnit);
    417417    ACanvas2d.stroke;
    418418  end;
     
    533533    if not isStrokeNone then
    534534    begin
    535       ACanvas2d.strokeStyle(strokeColor);
     535      ApplyStrokeStyle(ACanvas2D,AUnit);
    536536      ACanvas2d.stroke;
    537537    end;
     
    629629    if not isStrokeNone then
    630630    begin
    631       ACanvas2d.strokeStyle(strokeColor);
     631      ApplyStrokeStyle(ACanvas2D,AUnit);
    632632      ACanvas2d.stroke;
    633633    end;
     
    717717    if not isStrokeNone then
    718718    begin
    719       ACanvas2d.strokeStyle(strokeColor);
     719      ApplyStrokeStyle(ACanvas2D,AUnit);
    720720      ACanvas2d.stroke;
    721721    end;
     
    785785    if not isStrokeNone then
    786786    begin
    787       ACanvas2d.strokeStyle(strokeColor);
     787      ApplyStrokeStyle(ACanvas2D,AUnit);
    788788      ACanvas2d.stroke;
    789789    end;
     
    842842    if not isStrokeNone then
    843843    begin
    844       ACanvas2d.strokeStyle(strokeColor);
     844      ApplyStrokeStyle(ACanvas2D,AUnit);
    845845      ACanvas2d.stroke;
    846846    end;
     
    899899  if not isStrokeNone then
    900900  begin
    901     ACanvas2d.strokeStyle(strokeColor);
     901    ApplyStrokeStyle(ACanvas2D,AUnit);
    902902    ACanvas2d.beginPath;
    903903    ACanvas2d.moveTo(Units.ConvertWidth(x1,AUnit).value,Units.ConvertWidth(y1,AUnit).value);
  • GraphicTest/Packages/bgrabitmap/bgrasvgtype.pas

    r472 r494  
    2626      function GetIsStrokeNone: boolean;
    2727      function GetMatrix(AUnit: TCSSUnit): TAffineMatrix;
     28      function GetOpacity: single;
    2829      function GetOrthoAttributeOrStyleWithUnit(AName: string
    2930        ): TFloatWithCSSUnit;
    3031      function GetStroke: string;
    3132      function GetStrokeColor: TBGRAPixel;
     33      function GetStrokeLineCap: string;
     34      function GetStrokeLineJoin: string;
     35      function GetStrokeMiterLimit: single;
    3236      function GetStrokeOpacity: single;
    3337      function GetStrokeWidth: TFloatWithCSSUnit;
     
    5054      procedure SetHorizAttributeWithUnit(AName: string; AValue: TFloatWithCSSUnit);
    5155      procedure SetMatrix(AUnit: TCSSUnit; const AValue: TAffineMatrix);
     56      procedure SetOpacity(AValue: single);
    5257      procedure SetStroke(AValue: string);
    5358      procedure SetStrokeColor(AValue: TBGRAPixel);
     59      procedure SetStrokeLineCap(AValue: string);
     60      procedure SetStrokeLineJoin(AValue: string);
     61      procedure SetStrokeMiterLimit(AValue: single);
    5462      procedure SetStrokeOpacity(AValue: single);
    5563      procedure SetStrokeWidth(AValue: TFloatWithCSSUnit);
     
    6674      procedure InternalDraw({%H-}ACanvas2d: TBGRACanvas2D; {%H-}AUnit: TCSSUnit); virtual;
    6775      procedure LocateStyleDeclaration(AText: string; AProperty: string; out AStartPos,AColonPos,AValueLength: integer);
     76      procedure ApplyStrokeStyle(ACanvas2D: TBGRACanvas2D; AUnit: TCSSUnit);
    6877    public
    6978      constructor Create({%H-}ADocument: TXMLDocument; AElement: TDOMElement; AUnits: TCSSUnitConverter); virtual;
     
    93102      property strokeColor: TBGRAPixel read GetStrokeColor write SetStrokeColor;
    94103      property strokeOpacity: single read GetStrokeOpacity write SetStrokeOpacity;
     104      property strokeMiterLimit: single read GetStrokeMiterLimit write SetStrokeMiterLimit;
     105      property strokeLineJoin: string read GetStrokeLineJoin write SetStrokeLineJoin;
     106      property strokeLineCap: string read GetStrokeLineCap write SetStrokeLineCap;
    95107      property fill: string read GetFill write SetFill;
    96108      property fillColor: TBGRAPixel read GetFillColor write SetFillColor;
    97109      property fillOpacity: single read GetFillOpacity write SetFillOpacity;
     110      property opacity: single read GetOpacity write SetOpacity;
    98111  end;
    99112
     
    123136implementation
    124137
    125 uses Math;
    126 
    127138{ TSVGParser }
    128139
     
    264275begin
    265276  result := StrToBGRA(fill,BGRABlack);
    266   result.alpha := round(result.alpha*fillOpacity);
     277  result.alpha := round(result.alpha*fillOpacity*opacity);
    267278  if result.alpha = 0 then result := BGRAPixelTransparent;
    268279end;
     
    359370    begin
    360371      angle := parser.ParseFloat;
    361       result *= AffineMatrix(1,tan(angle*Pi/180),0,
    362                              0,        1,        0);
     372      result *= AffineMatrixSkewXDeg(angle);
    363373    end else
    364374    if compareText(kind,'skewy')=0 then
    365375    begin
    366376      angle := parser.ParseFloat;
    367       result *= AffineMatrix(1,         0        ,0,
    368                      tan(angle*Pi/180), 1,        0);
     377      result *= AffineMatrixSkewYDeg(angle);
    369378    end;
    370379    parser.SkipUpToSymbol(')');
     
    375384end;
    376385
     386function TSVGElement.GetOpacity: single;
     387var errPos: integer;
     388begin
     389  val(AttributeOrStyle['opacity'], result, errPos);
     390  if errPos <> 0 then result := 1 else
     391    if result < 0 then result := 0 else
     392      if result > 1 then result := 1;
     393end;
     394
    377395function TSVGElement.GetOrthoAttributeOrStyleWithUnit(AName: string
    378396  ): TFloatWithCSSUnit;
     
    390408begin
    391409  result := StrToBGRA(stroke);
    392   result.alpha := round(result.alpha*strokeOpacity);
     410  result.alpha := round(result.alpha*strokeOpacity*opacity);
    393411  if result.alpha = 0 then result := BGRAPixelTransparent;
     412end;
     413
     414function TSVGElement.GetStrokeLineCap: string;
     415begin
     416  result := AttributeOrStyle['stroke-linecap'];
     417  if result = '' then result := 'butt';
     418end;
     419
     420function TSVGElement.GetStrokeLineJoin: string;
     421begin
     422  result := AttributeOrStyle['stroke-linejoin'];
     423  if result = '' then result := 'miter';
     424end;
     425
     426function TSVGElement.GetStrokeMiterLimit: single;
     427var errPos: integer;
     428begin
     429  val(AttributeOrStyle['stroke-miterlimit'], result, errPos);
     430  if errPos <> 0 then result := 4 else
     431    if result < 1 then result := 1;
    394432end;
    395433
     
    405443function TSVGElement.GetStrokeWidth: TFloatWithCSSUnit;
    406444begin
    407   result := HorizAttributeOrStyleWithUnit['stroke-width'];
     445  result := OrthoAttributeOrStyleWithUnit['stroke-width'];
    408446end;
    409447
     
    525563end;
    526564
     565procedure TSVGElement.SetOpacity(AValue: single);
     566begin
     567  Attribute['opacity'] := Units.formatValue(AValue);
     568  RemoveStyle('opacity');
     569end;
     570
    527571procedure TSVGElement.SetStroke(AValue: string);
    528572begin
     
    536580  AValue.alpha:= 255;
    537581  stroke := BGRAToStr(AValue, CSSColors);
     582end;
     583
     584procedure TSVGElement.SetStrokeLineCap(AValue: string);
     585begin
     586  Attribute['stroke-linecap'] := AValue;
     587  RemoveStyle('stroke-linecap');
     588end;
     589
     590procedure TSVGElement.SetStrokeLineJoin(AValue: string);
     591begin
     592  Attribute['stroke-linejoin'] := AValue;
     593  RemoveStyle('stroke-linejoin');
     594end;
     595
     596procedure TSVGElement.SetStrokeMiterLimit(AValue: single);
     597begin
     598  if AValue < 1 then AValue := 1;
     599  Attribute['stroke-miterlimit'] := Units.formatValue(AValue);
     600  RemoveStyle('stroke-miterlimit');
    538601end;
    539602
     
    671734    if AText[i] = ';' then
    672735    begin
    673       curValueLength := i-curColon;
     736      curValueLength := i-(curColon+1);
    674737      if CheckShouldReturnResult then exit;
    675738      curStart := -1;
     
    680743  if curColon <> -1 then
    681744  begin
    682     curValueLength:= length(AText)-curColon;
     745    curValueLength:= length(AText)-(curColon+1)+1;
    683746    if CheckShouldReturnResult then exit;
    684747  end;
     748end;
     749
     750procedure TSVGElement.ApplyStrokeStyle(ACanvas2D: TBGRACanvas2D; AUnit: TCSSUnit);
     751begin
     752  ACanvas2d.strokeStyle(strokeColor);
     753  ACanvas2d.lineWidth := Units.ConvertWidth(strokeWidth,AUnit).value;
     754  ACanvas2d.lineCap := strokeLineCap;
     755  ACanvas2d.lineJoin := strokeLineJoin;
     756  ACanvas2d.miterLimit := strokeMiterLimit;
    685757end;
    686758
  • GraphicTest/Packages/bgrabitmap/bgratext.pas

    r472 r494  
    44
    55interface
     6
     7{$IFDEF LINUX}
     8  {$DEFINE LCL_RENDERER_IS_FINE}
     9  {$DEFINE LCL_CLEARTYPE_RENDERER_IS_FINE}
     10{$ENDIF}
     11{$IFDEF FREEBSD}
     12  {$DEFINE LCL_RENDERER_IS_FINE}
     13  {$DEFINE LCL_CLEARTYPE_RENDERER_IS_FINE}
     14{$ENDIF}
     15{$IFDEF DARWIN}
     16  {$DEFINE LCL_RENDERER_IS_FINE}
     17  {$DEFINE RENDER_TEXT_ON_TBITMAP}
     18{$ENDIF}
    619
    720{
    821  Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
    922
    10   This unit provides basic text rendering functions using LCL, and general
    11   text definitions.
     23  This unit provides basic text rendering functions using LCL.
    1224
    1325  Text functions use a temporary bitmap where the operating system text drawing is used.
     
    2032
    2133uses
    22   Classes, Types, SysUtils, Graphics, BGRABitmapTypes, InterfaceBase, BGRAPen, BGRAGrayscaleMask;
     34  Classes, Types, SysUtils, BGRAGraphics, BGRABitmapTypes, InterfaceBase, BGRAPen, BGRAGrayscaleMask;
    2335
    2436type
    25   TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;
     37  TWordBreakHandler = BGRABitmapTypes.TWordBreakHandler;
    2638
    2739  { TCustomLCLFontRenderer }
     
    7890function BGRAOriginalTextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: integer): TSize;
    7991function BGRAOriginalTextSizeEx(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer; out actualAntialiasingLevel: integer): TSize;
    80 procedure BGRADefaultWordBreakHandler(var ABefore,AAfter: string);
    8192
    8293function BGRATextUnderline(ATopLeft: TPointF; AWidth: Single; AMetrics: TFontPixelMetric): ArrayOfTPointF; overload;
     
    89100function FontFullHeightSign: integer;
    90101function LCLFontAvailable: boolean;
     102function GetFineClearTypeAuto: TBGRAFontQuality;
    91103
    92104procedure BGRAFillClearTypeGrayscaleMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true);
    93105procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; RGBOrder: boolean=true);
    94106procedure BGRAFillClearTypeRGBMask(dest: TBGRACustomBitmap; x,y: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner = nil; KeepRGBOrder: boolean=true);
    95 procedure BGRAInternalRenderText(dest: TBGRACustomBitmap; Quality: TBGRAFontQuality; grayscale: TGrayscaleMask; temp: TBGRACustomBitmap;
    96     x,y,xThird: integer; c: TBGRAPixel; tex: IBGRAScanner);
    97 
    98 const FontAntialiasingLevel = {$IFDEF LINUX}3{$ELSE}6{$ENDIF}; //linux rendering is already great
     107
     108const FontAntialiasingLevel = {$IFDEF LCL_RENDERER_IS_FINE}3{$ELSE}6{$ENDIF};
    99109const FontDefaultQuality = fqAntialiased;
    100110
    101 function GetFontPixelMetric(AFont: TFont): TFontPixelMetric;
     111function GetLCLFontPixelMetric(AFont: TFont): TFontPixelMetric;
     112
     113var
     114  BGRATextOutImproveReadabilityProc : procedure (bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
    102115
    103116implementation
    104117
    105 uses GraphType, Math, BGRABlend, LCLProc;
     118uses GraphType, Math, BGRABlend, BGRAUTF8;
    106119
    107120const MaxPixelMetricCount = 100;
     
    110123  LCLFontDisabledValue: boolean;
    111124  TempBmp: TBitmap;
     125  fqFineClearTypeComputed: boolean;
     126  fqFineClearTypeValue: TBGRAFontQuality;
    112127  FontHeightSignComputed: boolean;
    113128  FontHeightSignValue: integer;
     
    264279end;
    265280
    266 function GetFontPixelMetric(AFont: TFont): TFontPixelMetric;
     281function GetLCLFontPixelMetric(AFont: TFont): TFontPixelMetric;
    267282var i,startPos,endPos: integer;
    268283begin
     
    376391end;
    377392
     393function GetFineClearTypeAuto: TBGRAFontQuality;
     394var
     395  lclBmp: TBitmap;
     396  bgra: TBGRACustomBitmap;
     397  x,y: integer;
     398begin
     399  if fqFineClearTypeComputed then
     400  begin
     401    result:= fqFineClearTypeValue;
     402    exit;
     403  end;
     404  result := fqFineAntialiasing;
     405  if not LCLFontDisabledValue and not (WidgetSet.LCLPlatform = lpNoGUI) then
     406  begin
     407    lclBmp := TBitmap.Create;
     408    lclBmp.Canvas.FillRect(0,0,lclBmp.Width,lclBmp.Height);
     409    lclBmp.Canvas.Font.Height := -50;
     410    lclBmp.Canvas.Font.Quality := fqCleartype;
     411    with lclBmp.Canvas.TextExtent('/') do
     412    begin
     413      lclBmp.Width := cx;
     414      lclBmp.Height := cy;
     415    end;
     416    lclBmp.Canvas.TextOut(0,0,'/');
     417    bgra:= BGRABitmapFactory.Create(lclBmp);
     418    x:= bgra.Width div 2;
     419    for y := 0 to bgra.Height-1 do
     420      with bgra.GetPixel(x,y) do
     421        if (red<>blue) then
     422        begin
     423          if blue < red then
     424            result:= fqFineClearTypeRGB
     425          else
     426            result:= fqFineClearTypeBGR;
     427          break;
     428        end else
     429        if (green = 0) then break;
     430    lclBmp.Free;
     431  end;
     432  fqFineClearTypeValue := result;
     433  fqFineClearTypeComputed:= true;
     434end;
     435
    378436function FontEmHeightSign: integer;
    379437begin
     
    390448  if not FontHeightSignComputed then GetFontHeightSign;
    391449  result := not LCLFontDisabledValue;
    392 end;
    393 
    394 procedure BGRAFillClearTypeMaskPtr(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; maskData: PByte; maskPixelSize: NativeInt; maskRowSize: NativeInt; maskWidth,maskHeight: integer; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
    395 var
    396   pdest: PBGRAPixel;
    397   ClearTypePixel: array[0..2] of byte;
    398   curThird: integer;
    399 
    400   procedure OutputPixel; inline;
    401   begin
    402     if texture <> nil then
    403       color := texture.ScanNextPixel;
    404     if RGBOrder then
    405       ClearTypeDrawPixel(pdest, ClearTypePixel[0],ClearTypePixel[1],ClearTypePixel[2], color)
    406     else
    407       ClearTypeDrawPixel(pdest, ClearTypePixel[2],ClearTypePixel[1],ClearTypePixel[0], color);
    408   end;
    409 
    410   procedure NextAlpha(alphaValue: byte); inline;
    411   begin
    412     ClearTypePixel[curThird] := alphaValue;
    413     inc(curThird);
    414     if curThird = 3 then
    415     begin
    416       OutputPixel;
    417       curThird := 0;
    418       Fillchar(ClearTypePixel, sizeof(ClearTypePixel),0);
    419       inc(pdest);
    420     end;
    421   end;
    422 
    423   procedure EndRow; inline;
    424   begin
    425     if curThird > 0 then OutputPixel;
    426   end;
    427 
    428 var
    429   yMask,n: integer;
    430   a: byte;
    431   pmask: PByte;
    432   dx:integer;
    433   miny,maxy,minx,minxThird,maxx,alphaMinX,alphaMaxX,alphaLineLen: integer;
    434   leftOnSide, rightOnSide: boolean;
    435   countBetween: integer;
    436   v1,v2,v3: byte;
    437 
    438   procedure StartRow; inline;
    439   begin
    440     pdest := dest.Scanline[yMask+y]+minx;
    441     if texture <> nil then
    442       texture.ScanMoveTo(minx,yMask+y);
    443 
    444     curThird := minxThird;
    445     ClearTypePixel[0] := 0;
    446     ClearTypePixel[1] := 0;
    447     ClearTypePixel[2] := 0;
    448   end;
    449 
    450 begin
    451   alphaLineLen := maskWidth+2;
    452 
    453   xThird -= 1; //for first subpixel
    454 
    455   if xThird >= 0 then dx := xThird div 3
    456    else dx := -((-xThird+2) div 3);
    457   x += dx;
    458   xThird -= dx*3;
    459 
    460   if y >= dest.ClipRect.Top then miny := 0
    461     else miny := dest.ClipRect.Top-y;
    462   if y+maskHeight-1 < dest.ClipRect.Bottom then
    463     maxy := maskHeight-1 else
    464       maxy := dest.ClipRect.Bottom-1-y;
    465 
    466   if x >= dest.ClipRect.Left then
    467   begin
    468     minx := x;
    469     minxThird := xThird;
    470     alphaMinX := 0;
    471     leftOnSide := false;
    472   end else
    473   begin
    474     minx := dest.ClipRect.Left;
    475     minxThird := 0;
    476     alphaMinX := (dest.ClipRect.Left-x)*3 - xThird;
    477     leftOnSide := true;
    478   end;
    479 
    480   if x*3+xThird+maskWidth-1 < dest.ClipRect.Right*3 then
    481   begin
    482     maxx := (x*3+xThird+maskWidth-1) div 3;
    483     alphaMaxX := alphaLineLen-1;
    484     rightOnSide := false;
    485   end else
    486   begin
    487     maxx := dest.ClipRect.Right-1;
    488     alphaMaxX := maxx*3+2 - (x*3+xThird);
    489     rightOnSide := true;
    490   end;
    491 
    492   countBetween := alphaMaxX-alphaMinX-1;
    493 
    494   if (alphaMinX <= alphaMaxX) then
    495   begin
    496     for yMask := miny to maxy do
    497     begin
    498       StartRow;
    499 
    500       if leftOnSide then
    501       begin
    502         pmask := maskData + (yMask*maskRowSize)+ (alphaMinX-1)*maskPixelSize;
    503         a := pmask^ div 3;
    504         v1 := a+a;
    505         v2 := a;
    506         v3 := 0;
    507         inc(pmask, maskPixelSize);
    508       end else
    509       begin
    510         pmask := maskData + (yMask*maskRowSize);
    511         v1 := 0;
    512         v2 := 0;
    513         v3 := 0;
    514       end;
    515 
    516       for n := countBetween-1 downto 0 do
    517       begin
    518         a := pmask^ div 3;
    519         v1 += a;
    520         v2 += a;
    521         v3 += a;
    522         inc(pmask, maskPixelSize);
    523 
    524         NextAlpha(v1);
    525         v1 := v2;
    526         v2 := v3;
    527         v3 := 0;
    528       end;
    529 
    530       if rightOnSide then
    531       begin
    532         a := pmask^ div 3;
    533         v1 += a;
    534         v2 += a+a;
    535       end;
    536 
    537       NextAlpha(v1);
    538       NextAlpha(v2);
    539 
    540       EndRow;
    541     end;
    542   end;
    543450end;
    544451
     
    546453  y: integer; xThird: integer; mask: TGrayscaleMask; color: TBGRAPixel;
    547454  texture: IBGRAScanner; RGBOrder: boolean);
    548 var delta: NativeInt;
    549 begin
    550   delta := mask.Width;
    551   BGRAFillClearTypeMaskPtr(dest,x,y,xThird,mask.ScanLine[0],1,delta,mask.Width,mask.Height,color,texture,RGBOrder);
     455begin
     456  BGRAGrayscaleMask.BGRAFillClearTypeGrayscaleMask(dest,x,y,xThird,mask,color,texture,RGBOrder);
    552457end;
    553458
    554459procedure BGRAFillClearTypeMask(dest: TBGRACustomBitmap; x,y: integer; xThird: integer; mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner; RGBOrder: boolean);
    555 var delta: NativeInt;
    556 begin
    557   delta := mask.Width*sizeof(TBGRAPixel);
    558   if mask.LineOrder = riloBottomToTop then
    559     delta := -delta;
    560   BGRAFillClearTypeMaskPtr(dest,x,y,xThird,pbyte(mask.ScanLine[0])+1,sizeof(TBGRAPixel),delta,mask.Width,mask.Height,color,texture,RGBOrder);
     460begin
     461  BGRABlend.BGRAFillClearTypeMask(dest,x,y,xThird,mask,color,texture,RGBOrder);
    561462end;
    562463
     
    564465  mask: TBGRACustomBitmap; color: TBGRAPixel; texture: IBGRAScanner;
    565466  KeepRGBOrder: boolean);
    566 var
    567   minx,miny,maxx,maxy,countx,n,yb: integer;
    568   pdest,psrc: PBGRAPixel;
    569 begin
    570   if y >= dest.ClipRect.Top then miny := 0
    571     else miny := dest.ClipRect.Top-y;
    572   if y+mask.Height-1 < dest.ClipRect.Bottom then
    573     maxy := mask.Height-1 else
    574       maxy := dest.ClipRect.Bottom-1-y;
    575 
    576   if x >= dest.ClipRect.Left then minx := 0
    577     else minx := dest.ClipRect.Left-x;
    578   if x+mask.Width-1 < dest.ClipRect.Right then
    579     maxx := mask.Width-1 else
    580       maxx := dest.ClipRect.Right-1-x;
    581 
    582   countx := maxx-minx+1;
    583   if countx <= 0 then exit;
    584 
    585   for yb := miny to maxy do
    586   begin
    587     pdest := dest.ScanLine[y+yb]+(x+minx);
    588     psrc := mask.ScanLine[yb]+minx;
    589     if texture <> nil then
    590       texture.ScanMoveTo(x+minx, y+yb);
    591     if KeepRGBOrder then
    592     begin
    593       for n := countx-1 downto 0 do
    594       begin
    595         if texture <> nil then color := texture.ScanNextPixel;
    596         ClearTypeDrawPixel(pdest, psrc^.red, psrc^.green, psrc^.blue, color);
    597         inc(pdest);
    598         inc(psrc);
    599       end;
    600     end else
    601     begin
    602       for n := countx-1 downto 0 do
    603       begin
    604         if texture <> nil then color := texture.ScanNextPixel;
    605         ClearTypeDrawPixel(pdest, psrc^.blue, psrc^.green, psrc^.red, color);
    606         inc(pdest);
    607         inc(psrc);
    608       end;
    609     end;
    610   end;
     467begin
     468  BGRABlend.BGRAFillClearTypeRGBMask(dest,x,y,mask,color,texture,KeepRGBOrder);
    611469end;
    612470
     
    649507end;
    650508
    651 procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
    652 var p: integer;
    653 begin
    654   if (AAfter <> '') and (ABefore <> '') and (AAfter[1]<> ' ') and (ABefore[length(ABefore)] <> ' ') then
    655   begin
    656     p := length(ABefore);
    657     while (p > 1) and (ABefore[p-1] <> ' ') do dec(p);
    658     if p > 1 then //can put the word after
    659     begin
    660       AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
    661       ABefore := copy(ABefore,1,p-1);
    662     end else
    663     begin //cannot put the word after, so before
    664 
    665     end;
    666   end;
    667   while (ABefore <> '') and (ABefore[length(ABefore)] =' ') do delete(ABefore,length(ABefore),1);
    668   while (AAfter <> '') and (AAfter[1] =' ') do delete(AAfter,1,1);
    669 end;
    670 
    671509function BGRATextSize(Font: TFont; Quality: TBGRAFontQuality; sUTF8: string; CustomAntialiasingLevel: Integer): TSize;
    672510begin
     
    735573
    736574function CleanTextOutString(s: string): string;
    737 var idxIn, idxOut: integer;
    738 begin
    739   setlength(result, length(s));
    740   idxIn := 1;
    741   idxOut := 1;
    742   while IdxIn <= length(s) do
    743   begin
    744     if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8
    745     begin
    746       result[idxOut] := s[idxIn];
    747       inc(idxOut);
    748     end;
    749     inc(idxIn);
    750   end;
    751   setlength(result, idxOut-1);
     575begin
     576  result := BGRABitmapTypes.CleanTextOutString(s);
    752577end;
    753578
    754579function RemoveLineEnding(var s: string; indexByte: integer): boolean;
    755 begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long
    756       //so this function can be applied to UTF8 strings as well
    757   result := false;
    758   if length(s) >= indexByte then
    759   begin
    760     if s[indexByte] in[#13,#10] then
    761     begin
    762       result := true;
    763       if length(s) >= indexByte+1 then
    764       begin
    765         if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then
    766           delete(s,indexByte,2)
    767         else
    768           delete(s,indexByte,1);
    769       end
    770         else
    771           delete(s,indexByte,1);
    772     end;
    773   end;
     580begin
     581  result := BGRABitmapTypes.RemoveLineEnding(s, indexByte);
    774582end;
    775583
    776584function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
    777 var indexByte: integer;
    778     pIndex: PChar;
    779 begin
    780   pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8);
    781   if pIndex = nil then
    782   begin
    783     result := false;
    784     exit;
    785   end;
    786   indexByte := pIndex - @sUTF8[1];
    787   result := RemoveLineEnding(sUTF8, indexByte);
     585begin
     586  result := BGRABitmapTypes.RemoveLineEndingUTF8(sUTF8,indexUTF8);
    788587end;
    789588
     
    816615  size: TSize;
    817616  temp: TBGRACustomBitmap;
     617  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     618  tempLCL: TBitmap;
     619  {$ENDIF}
    818620  xMargin,xThird: integer;
    819621  tempSize: TSize;
     
    835637  end;
    836638
     639  {$IFDEF LCL_RENDERER_IS_FINE}
     640  if (Quality in [fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR]) and
     641     (BGRATextSize(Font, fqSystem, 'Hg', 1).cy >= 13) then
     642  begin
     643    if Quality = fqFineAntialiasing then Quality := fqSystem;
     644    {$IFDEF LCL_CLEARTYPE_RENDERER_IS_FINE}
     645    if Quality = GetFineClearTypeAuto then Quality := fqSystemClearType;
     646    {$ENDIF}
     647  end;
     648  {$ENDIF}
     649
    837650  size := BGRAOriginalTextSizeEx(Font,Quality,sUTF8,CustomAntialiasingLevel,sizeFactor);
    838651  if (size.cx = 0) or (size.cy = 0) then
     
    890703  tempSize.cx += xMargin*2;
    891704
     705  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     706  tempLCL := TBitmap.Create;
     707  tempLCL.Width := tempSize.cx;
     708  tempLCL.Height := tempSize.cy;
     709  tempLCL.Canvas.Brush.Color := clBlack;
     710  tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height);
     711  with tempLCL do begin
     712  {$ELSE}
    892713  temp := bmp.NewBitmap(tempSize.cx, tempSize.cy, BGRABlack);
    893   temp.Canvas.Font := Font;
    894   temp.Canvas.Font.Height := Font.Height*sizeFactor;
    895   temp.Canvas.Font.Color := clWhite;
    896   temp.Canvas.Brush.Style := bsClear;
    897   temp.Canvas.TextOut(xMargin+subX, subY, sUTF8);
     714  with temp do begin
     715  {$ENDIF}
     716    Canvas.Font := Font;
     717    Canvas.Font.Height := Font.Height*sizeFactor;
     718    Canvas.Font.Color := clWhite;
     719    Canvas.Brush.Style := bsClear;
     720    Canvas.TextOut(xMargin+subX, subY, sUTF8);
     721  end;
     722  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     723  temp := BGRABitmapFactory.create(tempLCL,False);
     724  tempLCL.Free;
     725  {$ENDIF}
    898726
    899727  FilterOriginalText(Quality,CustomAntialiasingLevel, temp, grayscale);
     
    921749  oldOrientation: integer;
    922750  grayscale:TGrayscaleMask;
     751  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     752  tempLCL: TBitmap;
     753  {$ENDIF}
    923754
    924755  procedure rotBoundsAdd(pt: TPointF);
     
    994825  if deltaY <> 0 then rotBounds.Bottom += sizeFactor;
    995826
     827  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     828  tempLCL := TBitmap.Create;
     829  tempLCL.Width := rotBounds.Right-rotBounds.Left;
     830  tempLCL.Height := rotBounds.Bottom-rotBounds.Top;
     831  tempLCL.Canvas.Brush.Color := clBlack;
     832  tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height);
     833  with tempLCL do begin
     834  {$ELSE}
    996835  temp := bmp.NewBitmap(rotBounds.Right-rotBounds.Left,rotBounds.Bottom-rotBounds.Top, BGRABlack);
    997   temp.Canvas.Font := Font;
    998   temp.Canvas.Font.Color := clWhite;
    999   temp.Canvas.Font.Orientation := orientationTenthDegCCW;
    1000   temp.Canvas.Font.Height := round(Font.Height*sizeFactor);
    1001   temp.Canvas.Brush.Style := bsClear;
    1002   temp.Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, sUTF8);
     836  with temp do begin
     837  {$ENDIF}
     838    Canvas.Font := Font;
     839    Canvas.Font.Color := clWhite;
     840    Canvas.Font.Orientation := orientationTenthDegCCW;
     841    Canvas.Font.Height := round(Font.Height*sizeFactor);
     842    Canvas.Brush.Style := bsClear;
     843    Canvas.TextOut(-rotBounds.Left+deltaX, -rotBounds.Top+deltaY, sUTF8);
     844  end;
     845  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     846  temp := BGRABitmapFactory.create(tempLCL,False);
     847  tempLCL.Free;
     848  {$ENDIF}
    1003849
    1004850  FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale);
     
    1017863  cr: TRect;
    1018864  grayscale:TGrayscaleMask;
     865  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     866  tempLCL: TBitmap;
     867  {$ENDIF}
    1019868begin
    1020869  if not LCLFontAvailable then exit;
     
    1038887    exit;
    1039888
     889  {$IFDEF LCL_RENDERER_IS_FINE}
     890  if (Quality in [fqFineAntialiasing, fqFineClearTypeRGB, fqFineClearTypeBGR]) and
     891     (BGRATextSize(Font, fqSystem, 'Hg', 1).cy >= 13) then
     892  begin
     893    if Quality = fqFineAntialiasing then Quality := fqSystem;
     894    {$IFDEF LCL_CLEARTYPE_RENDERER_IS_FINE}
     895    if Quality = GetFineClearTypeAuto then Quality := fqSystemClearType;
     896    {$ENDIF}
     897  end;
     898  {$ENDIF}
     899
    1040900  if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then
    1041901    sizeFactor := CustomAntialiasingLevel
     
    1043903    sizeFactor := 1;
    1044904
     905  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     906  tempLCL := TBitmap.Create;
     907  tempLCL.Width := tx*sizeFactor;
     908  tempLCL.Height := ty*sizeFactor;
     909  tempLCL.Canvas.Brush.Color := clBlack;
     910  tempLCL.Canvas.FillRect(0,0,tempLCL.Width,tempLCL.Height);
     911  with tempLCL do begin
     912  {$ELSE}
    1045913  temp := bmp.NewBitmap(tx*sizeFactor, ty*sizeFactor, BGRABlack);
    1046   temp.Canvas.Font := Font;
    1047   temp.Canvas.Font.Orientation := 0;
    1048   if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then temp.Canvas.Font.Height := Font.Height*CustomAntialiasingLevel
    1049      else temp.Canvas.Font.Height := Font.Height;
    1050   temp.Canvas.Font.Color := clWhite;
    1051   temp.Canvas.Brush.Style := bsClear;
    1052   temp.Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, sUTF8, style);
     914  with temp do begin
     915  {$ENDIF}
     916    Canvas.Font := Font;
     917    Canvas.Font.Orientation := 0;
     918    if Quality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB] then Canvas.Font.Height := Font.Height*CustomAntialiasingLevel
     919       else Canvas.Font.Height := Font.Height;
     920    Canvas.Font.Color := clWhite;
     921    Canvas.Brush.Style := bsClear;
     922    Canvas.TextRect(rect(lim.Left-ARect.Left, lim.Top-ARect.Top, (ARect.Right-ARect.Left)*sizeFactor, (ARect.Bottom-ARect.Top)*sizeFactor), (x - lim.Left)*sizeFactor, (y - lim.Top)*sizeFactor, sUTF8, style);
     923  end;
     924  {$IFDEF RENDER_TEXT_ON_TBITMAP}
     925  temp := BGRABitmapFactory.create(tempLCL,False);
     926  tempLCL.Free;
     927  {$ENDIF}
    1053928
    1054929  FilterOriginalText(Quality,CustomAntialiasingLevel,temp,grayscale);
     
    11631038  UpdateFont;
    11641039  if FontQuality in[fqSystem,fqSystemClearType] then
    1165     result := BGRAText.GetFontPixelMetric(FFont)
     1040    result := GetLCLFontPixelMetric(FFont)
    11661041  else
    11671042  begin
     
    11691044    FxFont.Assign(FFont);
    11701045    FxFont.Height := fxFont.Height*FontAntialiasingLevel;
    1171     Result:= BGRAText.GetFontPixelMetric(FxFont);
     1046    Result:= GetLCLFontPixelMetric(FxFont);
    11721047    if Result.Baseline <> -1 then Result.Baseline:= round((Result.Baseline-1)/FontAntialiasingLevel);
    11731048    if Result.CapLine <> -1 then Result.CapLine:= round(Result.CapLine/FontAntialiasingLevel);
     
    13541229
    13551230function TCustomLCLFontRenderer.TextSize(sUTF8: string): TSize;
    1356 begin
     1231var oldOrientation: integer;
     1232begin
     1233  oldOrientation:= FontOrientation;
     1234  FontOrientation:= 0;
    13571235  UpdateFont;
    13581236  result := TextSizeNoUpdateFont(sUTF8);
     1237  FontOrientation:= oldOrientation;
    13591238end;
    13601239
  • GraphicTest/Packages/bgrabitmap/bgratextfx.pas

    r472 r494  
    2626
    2727uses
    28   Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText, BGRAVectorize;
     28  Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText,
     29  BGRACustomTextFX, BGRAVectorize;
    2930
    3031type
     
    8081  { TBGRATextEffect }
    8182
    82   TBGRATextEffect = class
    83   private
    84     FShadowQuality: TRadialBlurType;
    85     function GetBounds: TRect;
    86     function GetMaskHeight: integer;
    87     class function GetOutlineWidth: integer; static;
    88     function GetShadowBounds(ARadius: integer): TRect;
    89     function GetMaskWidth: integer;
    90     function GetTextHeight: integer;
    91     function GetTextWidth: integer;
    92     procedure SetShadowQuality(AValue: TRadialBlurType);
     83  TBGRATextEffect = class(TBGRACustomTextEffect)
    9384  protected
    94     FTextMask: TBGRACustomBitmap;
    95     FShadowRadius: integer;
    96     FOutlineMask, FShadowMask, FShadingMask : TBGRACustomBitmap;
    97     FShadingAltitude: integer;
    98     FShadingRounded: boolean;
    99     FTextSize: TSize;
    100     FOffset: TPoint;
    101     function DrawMaskMulticolored(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; const AColors: array of TBGRAPixel): TRect;
    102     function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; AColor: TBGRAPixel): TRect;
    103     function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; ATexture: IBGRAScanner): TRect;
    104     function InternalDrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;
    10585    procedure InitImproveReadability(AText: string; Font: TFont; SubOffsetX,SubOffsetY: single);
    10686    procedure Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
     
    11494    constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
    11595    constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
    116     constructor Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,AHeight: integer; AOffset: TPoint);
    117     procedure ApplySphere;
    118     procedure ApplyVerticalCylinder;
    119     procedure ApplyHorizontalCylinder;
    120     function Draw(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
    121     function Draw(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
    122     function Draw(ADest: TBGRACustomBitmap; X, Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    123     function Draw(ADest: TBGRACustomBitmap; X, Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    124 
    125     function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ARounded: Boolean = true): TRect;
    126     function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; ARounded: Boolean = true): TRect;
    127     function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; AAlign: TAlignment; ARounded: Boolean = true): TRect;
    128     function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; AAlign: TAlignment; ARounded: Boolean = true): TRect;
    129 
    130     function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel): TRect;
    131     function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
    132     function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
    133     function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
    134     function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    135     function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    136     function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel): TRect;
    137     function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    138     destructor Destroy; override;
    139     property TextMask: TBGRACustomBitmap read FTextMask;
    140     property TextMaskOffset: TPoint read FOffset;
    141     property Width: integer read GetTextWidth; deprecated;
    142     property Height: integer read GetTextHeight; deprecated;
    143     property MaskWidth: integer read GetMaskWidth;
    144     property MaskHeight: integer read GetMaskHeight;
    145     property TextSize: TSize read FTextSize;
    146     property TextWidth: integer read GetTextWidth;
    147     property TextHeight: integer read GetTextHeight;
    148     property Bounds: TRect read GetBounds;
    149     property ShadowBounds[ARadius: integer]: TRect read GetShadowBounds;
    150     property ShadowQuality: TRadialBlurType read FShadowQuality write SetShadowQuality;
    151     class property OutlineWidth: integer read GetOutlineWidth;
    15296  end;
    15397
     
    160104
    161105uses BGRAGradientScanner, GraphType, Math, BGRAGrayscaleMask;
    162 
    163 const DefaultOutlineWidth = 3;
    164106
    165107procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
     
    190132  FxFont.Assign(AFont);
    191133  FxFont.Height := fxFont.Height*FontAntialiasingLevel;
    192   metric := GetFontPixelMetric(FxFont);
     134  metric := GetLCLFontPixelMetric(FxFont);
    193135  if not metric.Defined or (metric.Lineheight < 8*FontAntialiasingLevel) or (metric.Lineheight >= 24*FontAntialiasingLevel) then
    194136  begin
     
    579521{ TBGRATextEffect }
    580522
    581 function TBGRATextEffect.GetBounds: TRect;
    582 begin
    583   if TextMask = nil then
    584     result := EmptyRect else
    585   with TextMaskOffset do
    586     result := rect(X,Y,X+TextMask.Width,Y+TextMask.Height);
    587 end;
    588 
    589 function TBGRATextEffect.GetMaskHeight: integer;
    590 begin
    591   if FTextMask = nil then
    592     result := 0
    593   else
    594     result := FTextMask.Height;
    595 end;
    596 
    597 class function TBGRATextEffect.GetOutlineWidth: integer; static;
    598 begin
    599   result := DefaultOutlineWidth;
    600 end;
    601 
    602 function TBGRATextEffect.GetShadowBounds(ARadius: integer): TRect;
    603 begin
    604   result := Bounds;
    605   if (ARadius > 0) and not IsRectEmpty(result) then
    606   begin
    607     result.left -= ARadius;
    608     result.top -= ARadius;
    609     result.right += ARadius;
    610     result.bottom += ARadius;
    611   end;
    612 end;
    613 
    614 function TBGRATextEffect.GetMaskWidth: integer;
    615 begin
    616   if FTextMask = nil then
    617     result := 0
    618   else
    619     result := FTextMask.Width;
    620 end;
    621 
    622 function TBGRATextEffect.GetTextHeight: integer;
    623 begin
    624   result := FTextSize.cy;
    625 end;
    626 
    627 function TBGRATextEffect.GetTextWidth: integer;
    628 begin
    629   result := FTextSize.cx;
    630 end;
    631 
    632 procedure TBGRATextEffect.SetShadowQuality(AValue: TRadialBlurType);
    633 begin
    634   if FShadowQuality=AValue then Exit;
    635   FShadowQuality:=AValue;
    636   FreeAndNil(FShadowMask);
    637 end;
    638 
    639 function TBGRATextEffect.DrawMaskMulticolored(ADest: TBGRACustomBitmap;
    640   AMask: TBGRACustomBitmap; X, Y: Integer; const AColors: array of TBGRAPixel
    641   ): TRect;
    642 var
    643   scan: TBGRASolidColorMaskScanner;
    644   xb,yb,startX,numColor: integer;
    645   p0,p: PBGRAPixel;
    646   emptyCol, nextCol: boolean;
    647 begin
    648   if (AMask = nil) or (length(AColors)=0) then
    649   begin
    650     result := EmptyRect;
    651     exit;
    652   end;
    653   if (length(AColors)=0) then
    654   begin
    655     result := DrawMask(ADest,AMask,X,Y,AColors[0]);
    656     exit;
    657   end;
    658   scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColors[0]);
    659   numColor := 0;
    660   startX := -1;
    661   p0 := AMask.data;
    662   for xb := 0 to AMask.Width-1 do
    663   begin
    664     p := p0;
    665 
    666     if startX=-1 then
    667     begin
    668       emptyCol := true;
    669       for yb := AMask.Height-1 downto 0 do
    670       begin
    671         if (p^<>BGRABlack) then
    672         begin
    673           emptyCol := false;
    674           break;
    675         end;
    676         inc(p, AMask.Width);
    677       end;
    678 
    679       if not emptyCol then
    680       begin
    681         if startX=-1 then
    682           startX := xb;
    683       end else
    684       begin
    685         if startX<>-1 then
    686         begin
    687           ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);
    688           inc(numColor);
    689           if numColor = length(AColors) then
    690             numColor := 0;
    691           scan.Color := AColors[numColor];
    692           startX := -1;
    693         end;
    694       end;
    695 
    696     end else
    697     begin
    698       emptyCol := true;
    699       nextCol := true;
    700       for yb := AMask.Height-1 downto 0 do
    701       begin
    702         if (p^<>BGRABlack) then
    703         begin
    704           emptyCol := false;
    705           if ((p-1)^<>BGRABlack) then
    706           begin
    707             nextCol := false;
    708             break;
    709           end;
    710         end;
    711         inc(p, AMask.Width);
    712       end;
    713       if nextCol or emptyCol then
    714       begin
    715         ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);
    716         inc(numColor);
    717         if numColor = length(AColors) then
    718           numColor := 0;
    719         scan.Color := AColors[numColor];
    720         if emptyCol then startX := -1
    721          else startX := xb;
    722       end;
    723     end;
    724 
    725     inc(p0);
    726   end;
    727   if startX<>-1 then
    728     ADest.FillRect(X+startX,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    729   scan.Free;
    730   result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
    731 end;
    732 
    733 function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
    734   AMask: TBGRACustomBitmap; X, Y: Integer; AColor: TBGRAPixel): TRect;
    735 var
    736   scan: TBGRACustomScanner;
    737 begin
    738   if AMask = nil then
    739   begin
    740     result := EmptyRect;
    741     exit;
    742   end;
    743   scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColor);
    744   ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    745   scan.Free;
    746   result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
    747 end;
    748 
    749 function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
    750   AMask: TBGRACustomBitmap; X, Y: Integer; ATexture: IBGRAScanner): TRect;
    751 var
    752   scan: TBGRACustomScanner;
    753 begin
    754   if AMask = nil then
    755   begin
    756     result := EmptyRect;
    757     exit;
    758   end;
    759   scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),ATexture);
    760   ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
    761   scan.Free;
    762   result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
    763 end;
    764 
    765 function TBGRATextEffect.InternalDrawShaded(ADest: TBGRACustomBitmap; X,
    766   Y: integer; Shader: TCustomPhongShading; Altitude: integer;
    767   AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;
    768 var
    769   WithMargin,Map: TBGRACustomBitmap;
    770   p: PBGRAPixel;
    771   n,maxv: integer;
    772   v,blurRadius: single;
    773   iBlurRadius: integer;
    774 begin
    775   if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
    776   begin
    777     result := EmptyRect;
    778     exit;
    779   end;
    780 
    781   if (FShadingMask <> nil) and ((FShadingAltitude <> Altitude) or (FShadingRounded <> ARounded)) then
    782     FreeAndNil(FShadingMask);
    783 
    784   if FShadingMask = nil then
    785   begin
    786     FShadingRounded := ARounded;
    787     FShadingAltitude := Altitude;
    788 
    789     if ARounded then blurRadius := Altitude
    790       else blurRadius := Altitude*0.5;
    791 
    792     iBlurRadius := ceil(blurRadius);
    793 
    794     WithMargin := BGRABitmapFactory.Create(FTextMask.Width+iBlurRadius*2, FTextMask.Height+iBlurRadius*2,BGRABlack);
    795     WithMargin.PutImage(iBlurRadius,iBlurRadius,FTextMask,dmSet);
    796     if (iBlurRadius <> blurRadius) and (blurRadius < 3) then
    797       Map := WithMargin.FilterBlurRadial(round(blurRadius*10),rbPrecise)
    798     else
    799       Map := WithMargin.FilterBlurRadial(iBlurRadius,rbFast);
    800 
    801     p := Map.Data;
    802     maxv := 0;
    803     for n := Map.NbPixels-1 downto 0 do
    804     begin
    805       if p^.green > maxv then
    806         maxv := p^.green;
    807       inc(p);
    808     end;
    809 
    810     if maxv > 0 then
    811     begin
    812       p := Map.Data;
    813       for n := Map.NbPixels-1 downto 0 do
    814       begin
    815         v := p^.green/maxv;
    816         if ARounded then
    817         begin
    818           if v <= 0.5 then
    819             v := v*v*2 else
    820             v := 1-(1-v)*(1-v)*2;
    821         end;
    822         p^ := MapHeightToBGRA( v, p^.alpha);
    823         inc(p);
    824       end;
    825     end;
    826 
    827     Map.ApplyMask(WithMargin);
    828     WithMargin.Free;
    829     BGRAReplace(Map, Map.GetPart(rect(iBlurRadius,iBlurRadius,Map.Width-iBlurRadius,Map.Height-iBlurRadius)));
    830     FShadingMask := Map;
    831   end;
    832 
    833   inc(X, FOffset.X);
    834   Inc(Y, FOffset.Y);
    835   if ATexture <> nil then
    836     Shader.DrawScan(ADest,FShadingMask,Altitude,X,Y, ATexture)
    837   else
    838     Shader.Draw(ADest,FShadingMask,Altitude,X,Y, AColor);
    839   result := rect(X,Y, X+FShadingMask.Width,Y+FShadingMask.Height);
    840 end;
    841 
    842523procedure TBGRATextEffect.InitImproveReadability(AText: string; Font: TFont;
    843524  SubOffsetX, SubOffsetY: single);
     
    859540end;
    860541
    861 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    862   AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    863 begin
    864   Case AAlign of
    865   taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,AColor);
    866   taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,AColor);
    867   else result := Draw(ADest,X,Y,AColor);
    868   end;
    869 end;
    870 
    871 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    872   ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    873 begin
    874   Case AAlign of
    875   taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,ATexture);
    876   taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,ATexture);
    877   else result := Draw(ADest,X,Y,ATexture);
    878   end;
    879 end;
    880 
    881 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
    882   Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;
    883   ARounded: Boolean): TRect;
    884 begin
    885   result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,AColor,nil,ARounded);
    886 end;
    887 
    888 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
    889   Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;
    890   ARounded: Boolean): TRect;
    891 begin
    892   result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,BGRAPixelTransparent,ATexture,ARounded);
    893 end;
    894 
    895 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
    896   Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;
    897   AAlign: TAlignment; ARounded: Boolean): TRect;
    898 begin
    899   Case AAlign of
    900   taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,AColor,ARounded);
    901   taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,AColor,ARounded);
    902   taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,AColor,ARounded);
    903   else
    904     result := EmptyRect;
    905   end;
    906 end;
    907 
    908 function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
    909   Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;
    910   AAlign: TAlignment; ARounded: Boolean): TRect;
    911 begin
    912   Case AAlign of
    913   taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,ATexture,ARounded);
    914   taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,ATexture,ARounded);
    915   taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,ATexture,ARounded);
    916   else
    917     result := EmptyRect;
    918   end;
    919 end;
    920 
    921542constructor TBGRATextEffect.Create(AText: string; Font: TFont;
    922543  Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
     
    955576begin
    956577  InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, SubOffsetX, SubOffsetY);
    957 end;
    958 
    959 constructor TBGRATextEffect.Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,
    960   AHeight: integer; AOffset: TPoint);
    961 begin
    962   FTextSize := Size(AWidth,AHeight);
    963   FOffset := AOffset;
    964   if not AMaskOwner then
    965     FTextMask := AMask.Duplicate()
    966   else
    967     FTextMask := AMask;
    968578end;
    969579
     
    1126736end;
    1127737
    1128 procedure TBGRATextEffect.ApplySphere;
    1129 var sphere: TBGRACustomBitmap;
    1130 begin
    1131   if FTextMask = nil then exit;
    1132   FreeAndNil(FOutlineMask);
    1133   FreeAndNil(FShadowMask);
    1134   FShadowRadius := 0;
    1135   sphere := FTextMask.FilterSphere;
    1136   FTextMask.Fill(BGRABlack);
    1137   FTextMask.PutImage(0,0,sphere,dmDrawWithTransparency);
    1138   sphere.Free;
    1139 end;
    1140 
    1141 procedure TBGRATextEffect.ApplyVerticalCylinder;
    1142 begin
    1143   if FTextMask = nil then exit;
    1144   FreeAndNil(FOutlineMask);
    1145   FreeAndNil(FShadowMask);
    1146   FShadowRadius := 0;
    1147   BGRAReplace(FTextMask,FTextMask.FilterCylinder);
    1148 end;
    1149 
    1150 procedure TBGRATextEffect.ApplyHorizontalCylinder;
    1151 begin
    1152   if FTextMask = nil then exit;
    1153   FreeAndNil(FOutlineMask);
    1154   FreeAndNil(FShadowMask);
    1155   FShadowRadius := 0;
    1156   BGRAReplace(FTextMask,FTextMask.RotateCW);
    1157   BGRAReplace(FTextMask,FTextMask.FilterCylinder);
    1158   BGRAReplace(FTextMask,FTextMask.RotateCCW);
    1159 end;
    1160 
    1161 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    1162   AColor: TBGRAPixel): TRect;
    1163 begin
    1164   result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColor);
    1165 end;
    1166 
    1167 function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
    1168   ATexture: IBGRAScanner): TRect;
    1169 begin
    1170   result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,ATexture);
    1171 end;
    1172 
    1173 function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
    1174   Y: integer; const AColors: array of TBGRAPixel): TRect;
    1175 begin
    1176   result := DrawMaskMulticolored(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColors);
    1177 end;
    1178 
    1179 function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
    1180   Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
    1181 begin
    1182   Case AAlign of
    1183   taRightJustify: result := DrawMulticolored(ADest,X-TextSize.cx,Y,AColors);
    1184   taCenter: result := DrawMulticolored(ADest,X-TextSize.cx div 2,Y,AColors);
    1185   else result := DrawMulticolored(ADest,X,Y,AColors);
    1186   end;
    1187 end;
    1188 
    1189 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    1190   AColor: TBGRAPixel): TRect;
    1191 begin
    1192   if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
    1193   begin
    1194     result := EmptyRect;
    1195     exit;
    1196   end;
    1197   if FOutlineMask = nil then
    1198   begin
    1199     FOutlineMask := FTextMask.FilterContour;
    1200     FOutlineMask.LinearNegative;
    1201   end;
    1202   result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,AColor);
    1203 end;
    1204 
    1205 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    1206   ATexture: IBGRAScanner): TRect;
    1207 begin
    1208   if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
    1209   begin
    1210     result := EmptyRect;
    1211     exit;
    1212   end;
    1213   if FOutlineMask = nil then
    1214   begin
    1215     FOutlineMask := FTextMask.FilterContour;
    1216     FOutlineMask.LinearNegative;
    1217   end;
    1218   result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,ATexture);
    1219 end;
    1220 
    1221 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    1222   AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    1223 begin
    1224   Case AAlign of
    1225   taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,AColor);
    1226   taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,AColor);
    1227   else result := DrawOutline(ADest,X,Y,AColor);
    1228   end;
    1229 end;
    1230 
    1231 function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
    1232   ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
    1233 begin
    1234   Case AAlign of
    1235   taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,ATexture);
    1236   taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,ATexture);
    1237   else result := DrawOutline(ADest,X,Y,ATexture);
    1238   end;
    1239 end;
    1240 
    1241 function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
    1242   Radius: integer; AColor: TBGRAPixel): TRect;
    1243 begin
    1244   if (Radius <= 0) or (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
    1245   begin
    1246     result := Draw(ADest,X,Y,AColor);
    1247     exit;
    1248   end;
    1249   if (FShadowRadius <> Radius) or (FShadowMask = nil) then
    1250   begin
    1251     FShadowRadius := Radius;
    1252     FreeAndNil(FShadowMask);
    1253     FShadowMask := BGRABitmapFactory.Create(FTextMask.Width+Radius*2,FTextMask.Height+Radius*2,BGRABlack);
    1254     FShadowMask.PutImage(Radius,Radius,FTextMask,dmSet);
    1255     BGRAReplace(FShadowMask, FShadowMask.FilterBlurRadial(Radius,ShadowQuality));
    1256   end;
    1257   Inc(X,FOffset.X-Radius);
    1258   Inc(Y,FOffset.Y-Radius);
    1259   DrawMask(ADest,FShadowMask,X,Y,AColor);
    1260   result := rect(X,Y,X+FShadowMask.Width,Y+FShadowMask.Height);
    1261 end;
    1262 
    1263 function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
    1264   Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
    1265 begin
    1266   Case AAlign of
    1267   taRightJustify: result := DrawShadow(ADest,X-TextSize.cx,Y,Radius,AColor);
    1268   taCenter: result := DrawShadow(ADest,X-TextSize.cx div 2,Y,Radius,AColor);
    1269   else result := DrawShadow(ADest,X,Y,Radius,AColor);
    1270   end;
    1271 end;
    1272 
    1273 destructor TBGRATextEffect.Destroy;
    1274 begin
    1275   FShadowMask.free;
    1276   textMask.Free;
    1277   FOutlineMask.Free;
    1278   FShadingMask.Free;
    1279   inherited Destroy;
    1280 end;
    1281 
    1282738initialization
    1283739
  • GraphicTest/Packages/bgrabitmap/bgrathumbnail.pas

    r472 r494  
    22
    33{$mode objfpc}{$H+}
     4{$i bgrabitmap.inc}
    45
    56interface
     
    1516function GetOpenRasterThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
    1617function GetLazPaintThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
     18function GetPhoxoThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
    1719function GetJpegThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
    1820function GetPsdThumbnail(AStream: TStream; AWidth,AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
     
    2022function GetPaintDotNetThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
    2123function GetBmpThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
     24{$IFDEF BGRABITMAP_USE_LCL}
    2225function GetIcoThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
     26{$ENDIF}
    2327
    2428function GetPcxThumbnail(AStream: TStream; AWidth, AHeight: integer; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap= nil): TBGRABitmap;
     
    3438implementation
    3539
    36 uses Types, GraphType, Graphics, base64, lazutf8classes, LCLProc,
    37      DOM, XMLRead, FPReadJPEG, BGRAReadPng, BGRAReadGif, BGRAReadBMP,
     40uses Types, base64, BGRAUTF8, {$IFDEF BGRABITMAP_USE_LCL}Graphics, GraphType,{$ENDIF}
     41     DOM, XMLRead, BGRAReadJPEG, BGRAReadPng, BGRAReadGif, BGRAReadBMP,
    3842     BGRAReadPSD, BGRAReadIco, UnzipperExt, BGRAReadLzp;
    3943
     
    101105    ifGif: result := GetGifThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    102106    ifBmp: result := GetBmpThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
     107    {$IFDEF BGRABITMAP_USE_LCL}
    103108    ifIco: result := GetIcoThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
     109    {$ENDIF}
    104110    ifPcx: result := GetPcxThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    105111    ifPaintDotNet: result := GetPaintDotNetThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    106112    ifLazPaint: result := GetLazPaintThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    107113    ifOpenRaster: result := GetOpenRasterThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
     114    ifPhoxo: result := GetPhoxoThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    108115    ifPsd: result := GetPsdThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
    109116    ifTarga: result := GetTargaThumbnail(AStream, AWidth,AHeight, ABackColor, ACheckers, ADest);
     
    175182end;
    176183
     184function GetPhoxoThumbnail(AStream: TStream; AWidth, AHeight: integer;
     185  ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap;
     186var
     187  reader: TFPCustomImageReader;
     188begin
     189  if DefaultBGRAImageReader[ifPhoxo] = nil then
     190    result := nil
     191  else
     192  begin
     193    reader := CreateBGRAImageReader(ifPhoxo);
     194    result := GetStreamThumbnail(AStream, reader, AWidth,AHeight,ABackColor,ACheckers,ADest);
     195    reader.Free;
     196  end;
     197end;
     198
    177199function GetJpegThumbnail(AStream: TStream; AWidth, AHeight: integer
    178200  ; ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap;
    179201var
    180   jpeg: TFPReaderJPEG;
    181 begin
    182   jpeg := TFPReaderJPEG.Create;
     202  jpeg: TBGRAReaderJpeg;
     203begin
     204  jpeg := TBGRAReaderJpeg.Create;
    183205  jpeg.Performance := jpBestSpeed;
    184206  jpeg.MinWidth := AWidth;
     
    328350end;
    329351
     352{$IFDEF BGRABITMAP_USE_LCL}
    330353function GetIcoThumbnail(AStream: TStream; AWidth, AHeight: integer;
    331354  ABackColor: TBGRAPixel; ACheckers: boolean; ADest: TBGRABitmap): TBGRABitmap;
     
    372395  ico.Free;
    373396end;
    374 
    375 
     397{$ENDIF}
    376398
    377399function GetPcxThumbnail(AStream: TStream; AWidth, AHeight: integer;
  • GraphicTest/Packages/bgrabitmap/bgratransform.pas

    r472 r494  
    1212type
    1313  { Contains an affine matrix, i.e. a matrix to transform linearly and translate TPointF coordinates }
    14   TAffineMatrix = array[1..2,1..3] of single;
     14  TAffineMatrix = BGRABitmapTypes.TAffineMatrix;
    1515
    1616  { TAffineBox }
     
    4949    procedure SetMatrix(AMatrix: TAffineMatrix);
    5050    function InternalScanCurrentPixel: TBGRAPixel; virtual;
     51    function GetViewMatrix: TAffineMatrix;
     52    procedure SetViewMatrix(AValue: TAffineMatrix);
    5153  public
    5254    GlobalOpacity: Byte;
     
    6668    function ScanAt(X, Y: Single): TBGRAPixel; override;
    6769    property Matrix: TAffineMatrix read FMatrix write SetMatrix;
     70    property ViewMatrix: TAffineMatrix read GetViewMatrix write SetViewMatrix;
    6871  end;
    6972
     
    8184    FBuffer: PBGRAPixel;
    8285    FBufferSize: Int32or64;
    83     procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
     86    FIncludeEdges: boolean;
     87    procedure Init(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean= false; ARepeatImageY: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
    8488  public
    85     constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear);
    86     constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear);
     89    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImage: Boolean= false; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
     90    constructor Create(ABitmap: TBGRACustomBitmap; ARepeatImageX: Boolean; ARepeatImageY: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
    8791    destructor Destroy; override;
    8892    function InternalScanCurrentPixel: TBGRAPixel; override;
     
    9094    function IsScanPutPixelsDefined: boolean; override;
    9195    procedure Fit(Origin, HAxis, VAxis: TPointF); override;
     96  end;
     97
     98  { TBGRAQuadLinearScanner }
     99
     100  TBGRAQuadLinearScanner = class(TBGRACustomScanner)
     101  private
     102    FPoints,FVectors: array[0..3] of TPointF;
     103    FInvLengths,FDets: array[0..3] of single;
     104    FCoeffs: array[0..3] of TPointF;
     105    aa,bb0,cc0,inv2aa: double;
     106    FSource: IBGRAScanner;
     107    FSourceMatrix: TAffineMatrix;
     108    FUVVector: TPointF;
     109
     110    ScanParaBB, ScanParaCC, ScanParaBBInv: double;
     111
     112    ScanVertV0,ScanVertVStep0,ScanVertDenom0,ScanVertDenomStep0: double;
     113
     114    FShowC1, FShowC2: boolean;
     115    FScanFunc: TScanNextPixelFunction;
     116    FCurXF,FCurYF: single;
     117    FBuffer: PBGRAPixel;
     118    FBufferSize: Int32or64;
     119    FTextureInterpolation: Boolean;
     120    function GetCulling: TFaceCulling;
     121    function ScanGeneral: TBGRAPixel;
     122    procedure PrepareScanVert0;
     123    function ScanVert0: TBGRAPixel;
     124    procedure PrepareScanPara;
     125    function ScanPara: TBGRAPixel;
     126    function GetTexColorAt(u,v: Single; detNeg: boolean): TBGRAPixel; inline;
     127    procedure ScanMoveToF(X,Y: single); inline;
     128    procedure SetCulling(AValue: TFaceCulling);
     129    procedure Init(ASource: IBGRAScanner; const APoints: array of TPointF;
     130         ATextureInterpolation: boolean);
     131  public
     132    function ScanAt(X, Y: Single): TBGRAPixel; override;
     133    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); override;
     134    function IsScanPutPixelsDefined: boolean; override;
     135    procedure ScanMoveTo(X, Y: Integer); override;
     136    function ScanNextPixel: TBGRAPixel; override;
     137    constructor Create(ASource: IBGRAScanner;
     138      ASourceMatrix: TAffineMatrix; const APoints: array of TPointF;
     139      ATextureInterpolation: boolean = true);
     140    constructor Create(ASource: IBGRAScanner;
     141      const ATexCoords: array of TPointF; const APoints: array of TPointF;
     142      ATextureInterpolation: boolean = true);
     143    destructor Destroy; override;
     144    property Culling: TFaceCulling read GetCulling write SetCulling;
    92145  end;
    93146
     
    142195//matrix multiplication
    143196operator *(M,N: TAffineMatrix): TAffineMatrix;
     197operator =(M,N: TAffineMatrix): boolean;
    144198
    145199//matrix multiplication by a vector (apply transformation to that vector)
    146200operator *(M: TAffineMatrix; V: TPointF): TPointF;
     201operator *(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
    147202
    148203//check if matrix is inversible
     
    166221//define a scaling matrix
    167222function AffineMatrixScale(sx,sy: single): TAffineMatrix;
     223
     224function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
     225function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
     226function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
     227function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
    168228
    169229//define a linear matrix
     
    282342  end;
    283343
     344  { TBGRASphereDeformationScanner }
     345
     346  TBGRASphereDeformationScanner = Class(TBGRACustomScanner)
     347  protected
     348    FScanner: IBGRAScanner;
     349    FScanAtFunc: TScanAtFunction;
     350    FCenter: TPointF;
     351    FRadiusX, FRadiusY: Single;
     352  public
     353    constructor Create(AScanner: IBGRAScanner; ACenter: TPointF; ARadiusX,ARadiusY: single);
     354    function ScanAt(X, Y: Single): TBGRAPixel; override;
     355    property RadiusX: Single read FRadiusX;
     356    property RadiusY: Single read FRadiusY;
     357  end;
     358
     359  { TBGRAVerticalCylinderDeformationScanner }
     360
     361  TBGRAVerticalCylinderDeformationScanner = Class(TBGRACustomScanner)
     362  protected
     363    FScanner: IBGRAScanner;
     364    FScanAtFunc: TScanAtFunction;
     365    FCenterX: single;
     366    FRadiusX: Single;
     367  public
     368    constructor Create(AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single);
     369    function ScanAt(X, Y: Single): TBGRAPixel; override;
     370    property RadiusX: Single read FRadiusX;
     371  end;
     372
     373
    284374implementation
    285375
    286 uses BGRABlend, GraphType;
     376uses BGRABlend, Math;
    287377
    288378function AffineMatrix(m11, m12, m13, m21, m22, m23: single): TAffineMatrix;
     
    307397end;
    308398
     399operator=(M, N: TAffineMatrix): boolean;
     400begin
     401  result := CompareMem(@M,@N,SizeOf(TAffineMatrix));
     402end;
     403
    309404operator*(M: TAffineMatrix; V: TPointF): TPointF;
    310405begin
    311   result.X := V.X*M[1,1]+V.Y*M[1,2]+M[1,3];
    312   result.Y := V.X*M[2,1]+V.Y*M[2,2]+M[2,3];
     406  if isEmptyPointF(V) then
     407    result := EmptyPointF
     408  else
     409  begin
     410    result.X := V.X*M[1,1]+V.Y*M[1,2]+M[1,3];
     411    result.Y := V.X*M[2,1]+V.Y*M[2,2]+M[2,3];
     412  end;
     413end;
     414
     415operator*(M: TAffineMatrix; A: array of TPointF): ArrayOfTPointF;
     416var
     417  i: NativeInt;
     418  ofs: TPointF;
     419begin
     420  setlength(result, length(A));
     421  if IsAffineMatrixTranslation(M) then
     422  begin
     423    ofs := PointF(M[1,3],M[2,3]);
     424    for i := 0 to high(A) do
     425      result[i] := A[i]+ofs;
     426  end else
     427    for i := 0 to high(A) do
     428      result[i] := M*A[i];
    313429end;
    314430
     
    320436function IsAffineMatrixTranslation(M: TAffineMatrix): boolean;
    321437begin
    322   result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 1) and (m[2,2]=0);
     438  result := (m[1,1]=1) and (m[1,2]=0) and (m[2,1] = 0) and (m[2,2]=1);
    323439end;
    324440
     
    355471function AffineMatrixScale(sx, sy: single): TAffineMatrix;
    356472begin
    357   result := AffineMatrix(sx, 0,    0,
    358                          0,  sy, 0);
     473  result := AffineMatrix(sx, 0,   0,
     474                         0,  sy,  0);
     475end;
     476
     477function AffineMatrixSkewXDeg(AngleCW: single): TAffineMatrix;
     478begin
     479  result := AffineMatrix(1,tan(AngleCW*Pi/180),0,
     480                         0,        1,          0);
     481end;
     482
     483function AffineMatrixSkewYDeg(AngleCW: single): TAffineMatrix;
     484begin
     485  result := AffineMatrix(1,           0, 0,
     486                 tan(AngleCW*Pi/180), 1, 0)
     487end;
     488
     489function AffineMatrixSkewXRad(AngleCCW: single): TAffineMatrix;
     490begin
     491
     492  result := AffineMatrix(1,tan(-AngleCCW),0,
     493                         0,      1,       0);
     494end;
     495
     496function AffineMatrixSkewYRad(AngleCCW: single): TAffineMatrix;
     497begin
     498  result := AffineMatrix(1,          0, 0,
     499                    tan(-angleCCW),  1, 0)
    359500end;
    360501
     
    386527begin
    387528  result := PointF(M[1,1],M[2,1])*PointF(M[1,2],M[2,2]) = 0;
     529end;
     530
     531{ TBGRAVerticalCylinderDeformationScanner }
     532
     533constructor TBGRAVerticalCylinderDeformationScanner.Create(
     534  AScanner: IBGRAScanner; ACenterX: single; ARadiusX: single);
     535begin
     536  FScanner := AScanner;
     537  FScanAtFunc := @FScanner.ScanAt;
     538  FCenterX := ACenterX;
     539  FRadiusX := ARadiusX;
     540end;
     541
     542function TBGRAVerticalCylinderDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
     543var
     544  xn,len,fact: Single;
     545begin
     546  xn   := (x - FCenterX) / FRadiusX;
     547  len := abs(xn);
     548  if (len <= 1) then
     549  begin
     550    if (len > 0) then
     551    begin
     552      fact := 1 / len * arcsin(len) / (Pi / 2);
     553      xn    *= fact;
     554    end;
     555    result := FScanAtFunc(xn * FRadiusX + FCenterX, y);
     556  end
     557  else
     558    result := BGRAPixelTransparent;
     559end;
     560
     561{ TBGRASphereDeformationScanner }
     562
     563constructor TBGRASphereDeformationScanner.Create(AScanner: IBGRAScanner;
     564  ACenter: TPointF; ARadiusX, ARadiusY: single);
     565begin
     566  FScanner := AScanner;
     567  FScanAtFunc := @FScanner.ScanAt;
     568  FCenter := ACenter;
     569  FRadiusX := ARadiusX;
     570  FRadiusY := ARadiusY;
     571end;
     572
     573function TBGRASphereDeformationScanner.ScanAt(X, Y: Single): TBGRAPixel;
     574var
     575  xn, yn, len,fact: Single;
     576begin
     577  xn   := (x - FCenter.X) / FRadiusX;
     578  yn   := (y - FCenter.Y) / FRadiusY;
     579  len := sqrt(sqr(xn) + sqr(yn));
     580  if (len <= 1) then
     581  begin
     582    if (len > 0) then
     583    begin
     584      fact := 1 / len * arcsin(len) / (Pi / 2);
     585      xn    *= fact;
     586      yn    *= fact;
     587    end;
     588    result := FScanAtFunc(xn * FRadiusX + FCenter.X, yn * FRadiusY + FCenter.Y);
     589  end
     590  else
     591    result := BGRAPixelTransparent;
    388592end;
    389593
     
    466670function TBGRAScannerOffset.ScanAt(X, Y: Single): TBGRAPixel;
    467671begin
    468   Result:=FSource.ScanAt(X, Y);
     672  Result:=FSource.ScanAt(X - FOffset.X, Y - FOffset.Y);
    469673end;
    470674
     
    620824end;
    621825
     826function TBGRAAffineScannerTransform.GetViewMatrix: TAffineMatrix;
     827begin
     828  if FEmptyMatrix then
     829    result := AffineMatrixIdentity
     830  else
     831    result := AffineMatrixInverse(FMatrix);
     832end;
     833
     834procedure TBGRAAffineScannerTransform.SetViewMatrix(AValue: TAffineMatrix);
     835begin
     836  Matrix := AValue;
     837  Invert;
     838end;
     839
    622840procedure TBGRAAffineScannerTransform.SetMatrix(AMatrix: TAffineMatrix);
    623841begin
     
    711929end;
    712930
     931{ TBGRAQuadLinearScanner }
     932
     933function TBGRAQuadLinearScanner.GetTexColorAt(u, v: Single; detNeg: boolean
     934  ): TBGRAPixel;
     935begin
     936  if detNeg then
     937  begin
     938    if not FShowC2 then
     939    begin
     940      result := BGRAPixelTransparent;
     941      exit;
     942    end;
     943  end else
     944    if not FShowC1 then
     945    begin
     946      result := BGRAPixelTransparent;
     947      exit;
     948    end;
     949  with (FSourceMatrix * PointF(u,v) + FUVVector*(u*v)) do
     950    if FTextureInterpolation then
     951      result := FSource.ScanAt(x,y)
     952    else
     953      result := FSource.ScanAtInteger(System.round(x),System.round(y));
     954end;
     955
     956procedure TBGRAQuadLinearScanner.ScanMoveToF(X, Y: single);
     957begin
     958  FCurXF := X;
     959  FCurYF := Y;
     960  if (FVectors[0].x = 0) and (FVectors[2].x = 0) then
     961  begin
     962    PrepareScanVert0;
     963    FScanFunc := @ScanVert0;
     964  end else
     965  if aa = 0 then
     966  begin
     967    PrepareScanPara;
     968    FScanFunc := @ScanPara
     969  end
     970  else
     971    FScanFunc := @ScanGeneral;
     972end;
     973
     974procedure TBGRAQuadLinearScanner.SetCulling(AValue: TFaceCulling);
     975begin
     976  FShowC1 := AValue in [fcKeepCW,fcNone];
     977  FShowC2 := AValue in [fcKeepCCW,fcNone];
     978end;
     979
     980procedure TBGRAQuadLinearScanner.Init(ASource: IBGRAScanner;
     981  const APoints: array of TPointF; ATextureInterpolation: boolean);
     982var
     983  i: NativeInt;
     984  v: TPointF;
     985  len: single;
     986begin
     987  if length(APoints)<>4 then
     988    raise exception.Create('Expecting 4 points');
     989  FTextureInterpolation:= ATextureInterpolation;
     990  FSource := ASource;
     991  FSourceMatrix := AffineMatrixIdentity;
     992  FUVVector := PointF(0,0);
     993  for i := 0 to 3 do
     994  begin
     995    FPoints[i] := APoints[i];
     996    v := APoints[(i+1) mod 4] - APoints[i];
     997    len := sqrt(v*v);
     998    if len > 0 then FInvLengths[i] := 1/len
     999      else FInvLengths[i] := 0;
     1000    FVectors[i] := v*FInvLengths[i];
     1001  end;
     1002
     1003  FCoeffs[0] := FPoints[0];
     1004  FCoeffs[1] := FPoints[1]-FPoints[0];
     1005  FCoeffs[2] := FPoints[3]-FPoints[0];
     1006  FCoeffs[3] := FPoints[0]+FPoints[2]-FPoints[1]-FPoints[3];
     1007
     1008  aa := VectDet(FCoeffs[3],FCoeffs[2]);
     1009  bb0 := VectDet(FCoeffs[3],FCoeffs[0]) + VectDet(FCoeffs[1],FCoeffs[2]);
     1010  cc0 := VectDet(FCoeffs[1],FCoeffs[0]);
     1011  for i := 0 to 3 do
     1012    FDets[i] := VectDet(FVectors[i],FVectors[(i+1) mod 4]);
     1013  if aa <> 0 then inv2aa := 1/(2*aa) else inv2aa := 1;
     1014
     1015  FShowC1 := true;
     1016  FShowC2 := true;
     1017
     1018  FBuffer := nil;
     1019  FBufferSize := 0;
     1020
     1021  ScanMoveToF(0,0);
     1022end;
     1023
     1024function TBGRAQuadLinearScanner.ScanAt(X, Y: Single): TBGRAPixel;
     1025begin
     1026  ScanMoveToF(X,Y);
     1027  Result:= FScanFunc();
     1028end;
     1029
     1030procedure TBGRAQuadLinearScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
     1031  mode: TDrawMode);
     1032var
     1033  n: NativeInt;
     1034  p: PBGRAPixel;
     1035begin
     1036  if mode = dmSet then
     1037    p := pdest
     1038  else
     1039  begin
     1040    if count > FBufferSize then
     1041    begin
     1042      FBufferSize := count;
     1043      ReAllocMem(FBuffer, FBufferSize*sizeof(TBGRAPixel));
     1044    end;
     1045    p := FBuffer;
     1046  end;
     1047  for n := count-1 downto 0 do
     1048  begin
     1049    p^ := FScanFunc();
     1050    inc(p);
     1051  end;
     1052  if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255);
     1053end;
     1054
     1055function TBGRAQuadLinearScanner.IsScanPutPixelsDefined: boolean;
     1056begin
     1057  result := true;
     1058end;
     1059
     1060procedure TBGRAQuadLinearScanner.ScanMoveTo(X, Y: Integer);
     1061begin
     1062  ScanMoveToF(X,Y);
     1063end;
     1064
     1065function TBGRAQuadLinearScanner.ScanNextPixel: TBGRAPixel;
     1066begin
     1067  Result:= FScanFunc();
     1068end;
     1069
     1070function TBGRAQuadLinearScanner.ScanGeneral: TBGRAPixel;
     1071var u1,u2,v1,v2,x,y: double;
     1072  bb,cc,det,delta,denom: double;
     1073
     1074  procedure ReturnC1C2; inline;
     1075  var c1,c2: TBGRAPixel;
     1076  begin
     1077    with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do
     1078      if FTextureInterpolation then
     1079        c1 := FSource.ScanAt(x,y)
     1080      else
     1081        c1 := FSource.ScanAtInteger(System.round(x),System.round(y));
     1082    with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do
     1083      if FTextureInterpolation then
     1084        c2 := FSource.ScanAt(x,y)
     1085      else
     1086        c2 := FSource.ScanAtInteger(System.round(x),System.round(y));
     1087    result := MergeBGRA(c1,c2);
     1088  end;
     1089
     1090begin
     1091  x := FCurXF;
     1092  y := FCurYF;
     1093  FCurXF += 1;
     1094  if (Y = FPoints[0].y) and (FVectors[0].y = 0) then
     1095  begin
     1096    if FVectors[0].x = 0 then
     1097    begin
     1098      result := BGRAPixelTransparent;
     1099      exit;
     1100    end;
     1101    u1 := (X - FPoints[0].x)/(FPoints[1].x-FPoints[0].x);
     1102    if (u1 >= 0) and (u1 <= 1) then
     1103    begin
     1104      result := GetTexColorAt(u1,0,FDets[0]<0);
     1105      exit;
     1106    end;
     1107  end;
     1108  if (X = FPoints[1].x) and (FVectors[1].x = 0) then
     1109  begin
     1110    if FVectors[1].y = 0 then
     1111    begin
     1112      result := BGRAPixelTransparent;
     1113      exit;
     1114    end;
     1115    v1 := (Y - FPoints[1].y)/(FPoints[2].y-FPoints[1].y);
     1116    if (v1 >= 0) and (v1 <= 1) then
     1117    begin
     1118      result := GetTexColorAt(0,v1,FDets[1]<0);
     1119      exit;
     1120    end;
     1121  end;
     1122  if (Y = FPoints[2].y) and (FVectors[2].y = 0) then
     1123  begin
     1124    if FVectors[2].x = 0 then
     1125    begin
     1126      result := BGRAPixelTransparent;
     1127      exit;
     1128    end;
     1129    u1 := (X - FPoints[3].x)/(FPoints[2].x-FPoints[3].x);
     1130    if (u1 >= 0) and (u1 <= 1) then
     1131    begin
     1132      result := GetTexColorAt(u1,1,FDets[2]<0);
     1133      exit;
     1134    end;
     1135  end;
     1136  if (X = FPoints[3].x) and (FVectors[3].x = 0) then
     1137  begin
     1138    if FVectors[3].y = 0 then
     1139    begin
     1140      result := BGRAPixelTransparent;
     1141      exit;
     1142    end;
     1143    v1 := (Y - FPoints[0].y)/(FPoints[3].y-FPoints[0].y);
     1144    if (v1 >= 0) and (v1 <= 1) then
     1145    begin
     1146      result := GetTexColorAt(0,v1,FDets[3]<0);
     1147      exit;
     1148    end;
     1149  end;
     1150
     1151  bb := bb0 + x*FCoeffs[3].y - y*FCoeffs[3].x;
     1152  cc := cc0 + x*FCoeffs[1].y - y*FCoeffs[1].x;
     1153  if cc = 0 then
     1154  begin
     1155    v1 := -bb*2*inv2aa;
     1156    denom := FCoeffs[1].x+FCoeffs[3].x*v1;
     1157    if denom = 0 then
     1158    begin
     1159      result := BGRAPixelTransparent;
     1160      exit;
     1161    end
     1162    else
     1163      u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom;
     1164
     1165    if (u1>=0) and (u1<=1) and (v1 >= 0) and (v1 <= 1) then
     1166      result := GetTexColorAt(u1,v1,bb<0)
     1167    else
     1168      result := BGRAPixelTransparent;
     1169  end else
     1170  begin
     1171    delta := bb*bb - 4*aa*cc;
     1172
     1173    if delta < 0 then
     1174    begin
     1175      result := BGRAPixelTransparent;
     1176      exit;
     1177    end;
     1178    det := sqrt(delta);
     1179    v1 := (-bb+det)*inv2aa;
     1180    if v1 = 0 then
     1181      u1 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0])
     1182    else if v1 = 1 then
     1183      u1 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2])
     1184    else
     1185    begin
     1186      denom := FCoeffs[1].x+FCoeffs[3].x*v1;
     1187      if abs(denom)<1e-6 then
     1188      begin
     1189        u1 := (bb+det)*inv2aa;
     1190        denom := FCoeffs[1].y+FCoeffs[3].y*u1;
     1191        if denom = 0 then
     1192        begin
     1193          result := BGRAPixelTransparent;
     1194          exit;
     1195        end
     1196        else v1 := (y-FCoeffs[0].y-FCoeffs[2].y*u1)/denom;
     1197      end
     1198      else u1 := (x-FCoeffs[0].x-FCoeffs[2].x*v1)/denom;
     1199    end;
     1200
     1201    v2 := (-bb-det)*inv2aa;
     1202    if v2 = 0 then
     1203      u2 := (FVectors[0]*FInvLengths[0])*(PointF(x,y)-FPoints[0])
     1204    else if v2 = 1 then
     1205      u2 := 1 - (FVectors[2]*FInvLengths[2])*(PointF(x,y)-FPoints[2])
     1206    else
     1207    begin
     1208      denom := FCoeffs[1].x+FCoeffs[3].x*v2;
     1209      if abs(denom)<1e-6 then
     1210      begin
     1211        u2 := (bb-det)*inv2aa;
     1212        denom := FCoeffs[1].y+FCoeffs[3].y*u2;
     1213        if denom = 0 then
     1214        begin
     1215          result := BGRAPixelTransparent;
     1216          exit;
     1217        end
     1218        else v2 := (y-FCoeffs[0].y-FCoeffs[2].y*u2)/denom;
     1219      end
     1220      else u2 := (x-FCoeffs[0].x-FCoeffs[2].x*v2)/denom;
     1221    end;
     1222
     1223    if (u1 >= 0) and (u1 <= 1) and (v1 >= 0) and (v1 <= 1) and FShowC1 then
     1224    begin
     1225      if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then
     1226        ReturnC1C2
     1227      else
     1228        with (FSourceMatrix * PointF(u1,v1) + FUVVector*(u1*v1)) do
     1229          if FTextureInterpolation then
     1230            result := FSource.ScanAt(x,y)
     1231          else
     1232            result := FSource.ScanAtInteger(System.round(x),System.round(y));
     1233    end
     1234    else
     1235    if (u2 >= 0) and (u2 <= 1) and (v2 >= 0) and (v2 <= 1) and FShowC2 then
     1236    begin
     1237      with (FSourceMatrix * PointF(u2,v2) + FUVVector*(u2*v2)) do
     1238        if FTextureInterpolation then
     1239          result := FSource.ScanAt(x,y)
     1240        else
     1241          result := FSource.ScanAtInteger(System.round(x),System.round(y));
     1242    end
     1243    else
     1244      result := BGRAPixelTransparent;
     1245  end;
     1246end;
     1247
     1248function TBGRAQuadLinearScanner.GetCulling: TFaceCulling;
     1249begin
     1250  if FShowC1 and FShowC2 then
     1251    result := fcNone
     1252  else if FShowC1 then
     1253    result := fcKeepCW
     1254  else
     1255    result := fcKeepCCW;
     1256end;
     1257
     1258procedure TBGRAQuadLinearScanner.PrepareScanVert0;
     1259begin
     1260  if (FVectors[1].x <> 0) then
     1261  begin
     1262    ScanVertVStep0 := 1/(FPoints[2].x-FPoints[1].x);
     1263    ScanVertV0 := (FCurXF-FPoints[1].x)*ScanVertVStep0;
     1264    ScanVertDenom0 := (FPoints[1].y-FPoints[0].y)*(1-ScanVertV0) + (FPoints[2].y-FPoints[3].y)*ScanVertV0;
     1265    ScanVertDenomStep0 := (FPoints[2].y-FPoints[3].y-FPoints[1].y+FPoints[0].y)*ScanVertVStep0;
     1266  end
     1267  else
     1268  begin
     1269    ScanVertV0 := 0;
     1270    ScanVertVStep0 := EmptySingle;
     1271  end;
     1272end;
     1273
     1274function TBGRAQuadLinearScanner.ScanVert0: TBGRAPixel;
     1275var u: single;
     1276begin
     1277  FCurXF += 1;
     1278  if ScanVertVStep0 = EmptySingle then
     1279  begin
     1280    result := BGRAPixelTransparent;
     1281    exit;
     1282  end;
     1283  if (ScanVertV0 >= 0) and (ScanVertV0 <= 1) then
     1284  begin
     1285    if ScanVertDenom0 = 0 then
     1286      result := BGRAPixelTransparent
     1287    else
     1288    begin
     1289      u := (FCurYF-(FPoints[0].y*(1-ScanVertV0) + FPoints[3].y*ScanVertV0))/ScanVertDenom0;
     1290      if (u >= 0) and (u <= 1) then
     1291        result := GetTexColorAt(u,ScanVertV0,FDets[0]<0)
     1292      else
     1293        result := BGRAPixelTransparent;
     1294    end;
     1295  end else
     1296    result := BGRAPixelTransparent;
     1297
     1298  ScanVertV0 += ScanVertVStep0;
     1299  ScanVertDenom0 += ScanVertDenomStep0;
     1300end;
     1301
     1302procedure TBGRAQuadLinearScanner.PrepareScanPara;
     1303begin
     1304  ScanParaBB := bb0 + FCurXF*FCoeffs[3].y - FCurYF*FCoeffs[3].x;
     1305  ScanParaCC := cc0 + FCurXF*FCoeffs[1].y - FCurYF*FCoeffs[1].x;
     1306  if ScanParaBB <> 0 then
     1307    ScanParaBBInv := 1/ScanParaBB
     1308  else
     1309    ScanParaBBInv := 1;
     1310end;
     1311
     1312function TBGRAQuadLinearScanner.ScanPara: TBGRAPixel;
     1313var
     1314  u,v,denom: Single;
     1315begin
     1316  FCurXF += 1;
     1317
     1318  if ScanParaBB = 0 then
     1319    result := BGRAPixelTransparent
     1320  else
     1321  begin
     1322    v := -ScanParaCC*ScanParaBBInv;
     1323    denom := FCoeffs[1].x+FCoeffs[3].x*v;
     1324    if denom = 0 then
     1325      result := BGRAPixelTransparent
     1326    else
     1327    begin
     1328      u := (FCurXF-1-FCoeffs[0].x-FCoeffs[2].x*v)/denom;
     1329
     1330      if (u>=0) and (u<=1) and (v >= 0) and (v <= 1) then
     1331        result := GetTexColorAt(u,v,FDets[0]<0)
     1332      else
     1333        result := BGRAPixelTransparent;
     1334    end;
     1335  end;
     1336
     1337  if FCoeffs[3].y <> 0 then
     1338  begin
     1339    ScanParaBB += FCoeffs[3].y;
     1340    if ScanParaBB <> 0 then
     1341      ScanParaBBInv := 1/ScanParaBB
     1342    else
     1343      ScanParaBBInv := 1;
     1344  end;
     1345  ScanParaCC += FCoeffs[1].y;
     1346end;
     1347
     1348constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
     1349  ASourceMatrix: TAffineMatrix; const APoints: array of TPointF;
     1350  ATextureInterpolation: boolean);
     1351begin
     1352  Init(ASource, APoints, ATextureInterpolation);
     1353  FSourceMatrix := ASourceMatrix;
     1354end;
     1355
     1356constructor TBGRAQuadLinearScanner.Create(ASource: IBGRAScanner;
     1357  const ATexCoords: array of TPointF; const APoints: array of TPointF;
     1358  ATextureInterpolation: boolean);
     1359begin
     1360  Init(ASource, APoints, ATextureInterpolation);
     1361  FSourceMatrix := AffineMatrixTranslation(ATexCoords[0].x,ATexCoords[0].y)*
     1362                AffineMatrixLinear(ATexCoords[1]-ATexCoords[0],ATexCoords[3]-ATexCoords[0]);
     1363  FUVVector := ATexCoords[2] - (ATexCoords[1]+ATexCoords[3]-ATexCoords[0]);
     1364end;
     1365
     1366destructor TBGRAQuadLinearScanner.Destroy;
     1367begin
     1368  freemem(FBuffer);
     1369  inherited Destroy;
     1370end;
     1371
    7131372{ TBGRAAffineBitmapTransform }
    7141373
    7151374procedure TBGRAAffineBitmapTransform.Init(ABitmap: TBGRACustomBitmap;
    7161375  ARepeatImageX: Boolean; ARepeatImageY: Boolean;
    717   AResampleFilter: TResampleFilter);
     1376  AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
    7181377begin
    7191378  if (ABitmap.Width = 0) or (ABitmap.Height = 0) then
     
    7241383  FRepeatImageY := ARepeatImageY;
    7251384  FResampleFilter:= AResampleFilter;
     1385  FBuffer := nil;
    7261386  FBufferSize:= 0;
     1387  FIncludeEdges := AIncludeEdges;
    7271388end;
    7281389
    7291390constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
    730   ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear);
    731 begin
    732   Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter);
     1391  ARepeatImage: Boolean; AResampleFilter: TResampleFilter = rfLinear; AIncludeEdges: boolean = false);
     1392begin
     1393  Init(ABitmap,ARepeatImage,ARepeatImage,AResampleFilter,AIncludeEdges);
    7331394end;
    7341395
    7351396constructor TBGRAAffineBitmapTransform.Create(ABitmap: TBGRACustomBitmap;
    7361397  ARepeatImageX: Boolean; ARepeatImageY: Boolean;
    737   AResampleFilter: TResampleFilter);
    738 begin
    739   Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter);
     1398  AResampleFilter: TResampleFilter; AIncludeEdges: boolean = false);
     1399begin
     1400  Init(ABitmap,ARepeatImageX,ARepeatImageY,AResampleFilter,AIncludeEdges);
    7401401end;
    7411402
     
    7521413procedure TBGRAAffineBitmapTransform.ScanPutPixels(pdest: PBGRAPixel;
    7531414  count: integer; mode: TDrawMode);
     1415const PrecisionShift = {$IFDEF CPU64}24{$ELSE}12{$ENDIF};
     1416      Precision = 1 shl PrecisionShift;
    7541417var p: PBGRAPixel;
    7551418  n: integer;
    756   posX4096, posY4096: Int32or64;
    757   deltaX4096,deltaY4096: Int32or64;
    758   ix,iy,shrMask,w,h: Int32or64;
     1419  posXPrecision, posYPrecision: NativeInt;
     1420  deltaXPrecision,deltaYPrecision: NativeInt;
     1421  ix,iy,shrMask,w,h: NativeInt;
    7591422  py0: PByte;
    760   deltaRow: Int32or64;
     1423  deltaRow: NativeInt;
    7611424begin
    7621425  w := FBitmap.Width;
     
    7641427  if (w = 0) or (h = 0) then exit;
    7651428
    766   posX4096 := round(FCurX*4096);
    767   deltaX4096:= round(FMatrix[1,1]*4096);
    768   posY4096 := round(FCurY*4096);
    769   deltaY4096:= round(FMatrix[2,1]*4096);
     1429  if GlobalOpacity = 0 then
     1430  begin
     1431    if mode = dmSet then
     1432      FillDWord(pdest^, count, DWord(BGRAPixelTransparent));
     1433    exit;
     1434  end;
     1435
     1436  posXPrecision := round(FCurX*Precision);
     1437  deltaXPrecision:= round(FMatrix[1,1]*Precision);
     1438  posYPrecision := round(FCurY*Precision);
     1439  deltaYPrecision:= round(FMatrix[2,1]*Precision);
    7701440  shrMask := -1;
    771   shrMask := shrMask shr 12;
     1441  shrMask := shrMask shr PrecisionShift;
    7721442  shrMask := not shrMask;
    7731443
     
    7861456  if FResampleFilter = rfBox then
    7871457  begin
    788     posX4096 += 2048;
    789     posY4096 += 2048;
     1458    posXPrecision += Precision shr 1;
     1459    posYPrecision += Precision shr 1;
    7901460    py0 := PByte(FBitmap.ScanLine[0]);
    7911461    if FBitmap.LineOrder = riloTopToBottom then
     
    7961466      for n := count-1 downto 0 do
    7971467      begin
    798         if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
    799         if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     1468        if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
     1469        if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
    8001470        if FRepeatImageX then ix := PositiveMod(ix,w);
    8011471        if FRepeatImageY then iy := PositiveMod(iy,h);
     
    8051475          p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
    8061476        inc(p);
    807         posX4096 += deltaX4096;
    808         posY4096 += deltaY4096;
     1477        posXPrecision += deltaXPrecision;
     1478        posYPrecision += deltaYPrecision;
    8091479      end;
    8101480    end else
     
    8121482     for n := count-1 downto 0 do
    8131483     begin
    814        if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
    815        if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
     1484       if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
     1485       if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
    8161486       if (ix < 0) or (iy < 0) or (ix >= w) or (iy >= h) then
    8171487         p^ := BGRAPixelTransparent
     
    8191489         p^ := (PBGRAPixel(py0 + iy*deltaRow)+ix)^;
    8201490       inc(p);
    821        posX4096 += deltaX4096;
    822        posY4096 += deltaY4096;
     1491       posXPrecision += deltaXPrecision;
     1492       posYPrecision += deltaYPrecision;
    8231493     end;
    8241494    end;
     
    8291499     for n := count-1 downto 0 do
    8301500     begin
    831        if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
    832        if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
    833        p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);
     1501       if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
     1502       if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
     1503       p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter);
    8341504       inc(p);
    835        posX4096 += deltaX4096;
    836        posY4096 += deltaY4096;
     1505       posXPrecision += deltaXPrecision;
     1506       posYPrecision += deltaYPrecision;
    8371507     end;
    8381508   end else
     
    8411511     for n := count-1 downto 0 do
    8421512     begin
    843        if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
    844        if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
    845        p^ := FBitmap.GetPixelCycle256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY);
     1513       if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
     1514       if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
     1515       p^ := FBitmap.GetPixelCycle256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter, FRepeatImageX,FRepeatImageY);
    8461516       inc(p);
    847        posX4096 += deltaX4096;
    848        posY4096 += deltaY4096;
     1517       posXPrecision += deltaXPrecision;
     1518       posYPrecision += deltaYPrecision;
    8491519     end;
    8501520   end else
     
    8521522    for n := count-1 downto 0 do
    8531523    begin
    854       if posX4096 < 0 then ix := (posX4096 shr 12) or shrMask else ix := posX4096 shr 12;
    855       if posY4096 < 0 then iy := (posY4096 shr 12) or shrMask else iy := posY4096 shr 12;
    856       p^ := FBitmap.GetPixel256(ix,iy, (posX4096 shr 4) and 255, (posY4096 shr 4) and 255,FResampleFilter);
     1524      if posXPrecision < 0 then ix := (posXPrecision shr PrecisionShift) or shrMask else ix := posXPrecision shr PrecisionShift;
     1525      if posYPrecision < 0 then iy := (posYPrecision shr PrecisionShift) or shrMask else iy := posYPrecision shr PrecisionShift;
     1526      p^ := FBitmap.GetPixel256(ix,iy, (posXPrecision shr (PrecisionShift-8)) and 255, (posYPrecision shr (PrecisionShift-8)) and 255,FResampleFilter);
    8571527      inc(p);
    858       posX4096 += deltaX4096;
    859       posY4096 += deltaY4096;
     1528      posXPrecision += deltaXPrecision;
     1529      posYPrecision += deltaYPrecision;
    8601530    end;
    8611531   end;
    8621532  end;
    8631533
     1534  if GlobalOpacity < 255 then
     1535  begin
     1536    if mode = dmSet then
     1537      p := pdest
     1538    else
     1539      p := FBuffer;
     1540    for n := count-1 downto 0 do
     1541    begin
     1542      p^.alpha := ApplyOpacity(p^.alpha,GlobalOpacity);
     1543      if p^.alpha = 0 then p^ := BGRAPixelTransparent;
     1544      inc(p);
     1545    end;
     1546  end;
     1547
    8641548  if mode <> dmSet then PutPixels(pdest,FBuffer,count,mode,255);
    8651549end;
     
    8721556procedure TBGRAAffineBitmapTransform.Fit(Origin, HAxis, VAxis: TPointF);
    8731557begin
    874   SetMatrix(AffineMatrix((HAxis.X-Origin.X)/FBitmap.Width, (VAxis.X-Origin.X)/FBitmap.Height, 0,
    875                          (HAxis.Y-Origin.Y)/FBitmap.Width, (VAxis.Y-Origin.Y)/FBitmap.Height, 0));
     1558  if (FBitmap.Width = 0) or (FBitmap.Height = 0) then exit;
     1559  Matrix := AffineMatrix(HAxis.X-Origin.X, VAxis.X-Origin.X, Origin.X,
     1560                         HAxis.Y-Origin.Y, VAxis.Y-Origin.Y, Origin.Y);
    8761561  Invert;
    877   Translate(Origin.X,Origin.Y);
     1562  if FIncludeEdges then
     1563  begin
     1564    Matrix := AffineMatrixTranslation(-0.5,-0.5)*AffineMatrixScale(FBitmap.Width,FBitmap.Height)*Matrix;
     1565  end else
     1566    Matrix := AffineMatrixScale(FBitmap.Width-1,FBitmap.Height-1)*Matrix;
    8781567end;
    8791568
  • GraphicTest/Packages/bgrabitmap/bgratypewriter.pas

    r472 r494  
    123123implementation
    124124
    125 uses LCLProc, lazutf8classes;
    126 
    127 {$i winstream.inc}
     125uses BGRAUTF8;
     126
     127procedure LEWritePointF(Stream: TStream; AValue: TPointF);
     128begin
     129  LEWriteSingle(Stream,AValue.x);
     130  LEWriteSingle(Stream,AValue.y);
     131end;
     132
     133function LEReadPointF(Stream: TStream): TPointF;
     134begin
     135  result.x := LEReadSingle(Stream);
     136  result.y := LEReadSingle(Stream);
     137end;
    128138
    129139function ComputeEasyBezier(APoints: array of TPointF; AClosed: boolean; AMinimumDotProduct: single = 0.707): ArrayOfTPointF;
     
    266276begin
    267277  inherited WriteContent(AStream);
    268   WinWritePointF(AStream, Offset);
    269   WinWriteLongint(AStream,length(Points));
     278  LEWritePointF(AStream, Offset);
     279  LEWriteLongint(AStream,length(Points));
    270280  for i := 0 to high(Points) do
    271     WinWritePointF(AStream, Points[i]);
     281    LEWritePointF(AStream, Points[i]);
    272282end;
    273283
     
    277287begin
    278288  inherited ReadContent(AStream);
    279   Offset := WinReadPointF(AStream);
    280   SetLength(tempPts, WinReadLongint(AStream));
     289  Offset := LEReadPointF(AStream);
     290  SetLength(tempPts, LEReadLongint(AStream));
    281291  for i := 0 to high(tempPts) do
    282     tempPts[i] := WinReadPointF(AStream);
     292    tempPts[i] := LEReadPointF(AStream);
    283293  SetPoints(tempPts);
    284294end;
     
    390400  AContentSize: longint);
    391401begin
    392   WinWriteByte(AStream, length(AName));
     402  LEWriteByte(AStream, length(AName));
    393403  AStream.Write(AName[1],length(AName));
    394   WinWriteLongint(AStream, AContentSize);
     404  LEWriteLongint(AStream, AContentSize);
    395405end;
    396406
     
    399409var NameLength: integer;
    400410begin
    401   NameLength := WinReadByte(AStream);
     411  NameLength := LEReadByte(AStream);
    402412  setlength(AName,NameLength);
    403413  AStream.Read(AName[1],length(AName));
    404   AContentSize := WinReadLongint(AStream);
     414  AContentSize := LEReadLongint(AStream);
    405415end;
    406416
     
    417427procedure TBGRAGlyph.WriteContent(AStream: TStream);
    418428begin
    419   WinWriteLongint(AStream,length(FIdentifier));
     429  LEWriteLongint(AStream,length(FIdentifier));
    420430  AStream.Write(FIdentifier[1],length(FIdentifier));
    421   WinWriteSingle(AStream,Width);
    422   WinWriteSingle(AStream,Height);
     431  LEWriteSingle(AStream,Width);
     432  LEWriteSingle(AStream,Height);
    423433end;
    424434
     
    426436var lIdentifierLength: integer;
    427437begin
    428   lIdentifierLength:= WinReadLongint(AStream);
     438  lIdentifierLength:= LEReadLongint(AStream);
    429439  setlength(FIdentifier, lIdentifierLength);
    430440  AStream.Read(FIdentifier[1],length(FIdentifier));
    431   Width := WinReadSingle(AStream);
    432   Height := WinReadSingle(AStream);
     441  Width := LEReadSingle(AStream);
     442  Height := LEReadSingle(AStream);
    433443end;
    434444
     
    711721begin
    712722  for c := AUnicodeFrom to AUnicodeTo do
    713     GetGlyph(UnicodeToUTF8(c));
     723    GetGlyph(UnicodeCharToUTF8(c));
    714724end;
    715725
     
    805815var Enumerator: TAvgLvlTreeNodeEnumerator;
    806816begin
    807   WinWriteLongint(AStream,CustomHeaderSize);
     817  LEWriteLongint(AStream,CustomHeaderSize);
    808818  WriteCustomHeader(AStream);
    809819
     
    833843  GlyphStartPosition: Int64;
    834844begin
    835   HeaderSize := WinReadLongint(AStream);
     845  HeaderSize := LEReadLongint(AStream);
    836846  GlyphStartPosition:= AStream.Position+HeaderSize;
    837847  Header := ReadCustomTypeWriterHeader(AStream);
     
    919929begin
    920930  lHeaderName:= HeaderName;
    921   WinWriteByte(AStream,length(lHeaderName));
     931  LEWriteByte(AStream,length(lHeaderName));
    922932  AStream.Write(lHeaderName[1],length(lHeaderName));
    923   WinWriteLongint(AStream,FGlyphs.Count);
     933  LEWriteLongint(AStream,FGlyphs.Count);
    924934end;
    925935
     
    927937  ): TBGRACustomTypeWriterHeader;
    928938begin
    929   setlength(result.HeaderName, WinReadByte(AStream));
     939  setlength(result.HeaderName, LEReadByte(AStream));
    930940  AStream.Read(result.HeaderName[1],length(result.HeaderName));
    931   result.NbGlyphs:= WinReadLongint(AStream);
     941  result.NbGlyphs:= LEReadLongint(AStream);
    932942end;
    933943
  • GraphicTest/Packages/bgrabitmap/bgravectorize.pas

    r472 r494  
    190190implementation
    191191
    192 uses LCLProc, FileUtil, lazutf8classes;
    193 
    194 {$i winstream.inc}
     192uses BGRAUTF8;
     193
    195194function VectorizeMonochrome(ASource: TBGRACustomBitmap; zoom: single; PixelCenteredCoordinates: boolean): ArrayOfTPointF;
    196195const unitShift = 6;
     
    12851284  if not FFontPixelMetricComputed and (FFont <> nil) then
    12861285  begin
    1287     FFontPixelMetric := BGRAText.GetFontPixelMetric(FFont);
     1286    FFontPixelMetric := BGRAText.GetLCLFontPixelMetric(FFont);
    12881287    FFontPixelMetricComputed := true;
    12891288  end;
     
    19161915    end;
    19171916  until FindNext(SearchRec) <> 0;
     1917  FindClose(SearchRec);
    19181918  SetLength(FDirectoryContent,NbFiles);
    19191919end;
     
    20102010begin
    20112011  inherited WriteCustomHeader(AStream);
    2012   WinWriteLongint(AStream, length(FName));
     2012  LEWriteLongint(AStream, length(FName));
    20132013  AStream.Write(FName[1],length(FName));
    2014   WinWriteLongint(AStream, integer(FStyle));
    2015   WinWriteSingle(AStream, FontEmHeightRatio);
    2016   WinWriteLongint(AStream, Resolution);
     2014  LEWriteLongint(AStream, integer(FStyle));
     2015  LEWriteSingle(AStream, FontEmHeightRatio);
     2016  LEWriteLongint(AStream, Resolution);
    20172017  metric := FontPixelMetric;
    2018   WinWriteLongint(AStream, metric.Baseline);
    2019   WinWriteLongint(AStream, metric.xLine);
    2020   WinWriteLongint(AStream, metric.CapLine);
    2021   WinWriteLongint(AStream, metric.DescentLine);
    2022   WinWriteLongint(AStream, metric.Lineheight);
     2018  LEWriteLongint(AStream, metric.Baseline);
     2019  LEWriteLongint(AStream, metric.xLine);
     2020  LEWriteLongint(AStream, metric.CapLine);
     2021  LEWriteLongint(AStream, metric.DescentLine);
     2022  LEWriteLongint(AStream, metric.Lineheight);
    20232023end;
    20242024
     
    20482048var lNameLength: integer;
    20492049begin
    2050   lNameLength := WinReadLongint(AStream);
     2050  lNameLength := LEReadLongint(AStream);
    20512051  setlength(result.Name, lNameLength);
    20522052  AStream.Read(result.Name[1],length(result.Name));
    2053   result.Style := TFontStyles(WinReadLongint(AStream));
    2054   result.EmHeightRatio:= WinReadSingle(AStream);
    2055   result.Resolution := WinReadLongint(AStream);
    2056   result.PixelMetric.Baseline := WinReadLongint(AStream);
    2057   result.PixelMetric.xLine := WinReadLongint(AStream);
    2058   result.PixelMetric.CapLine := WinReadLongint(AStream);
    2059   result.PixelMetric.DescentLine := WinReadLongint(AStream);
    2060   result.PixelMetric.Lineheight := WinReadLongint(AStream);
     2053  result.Style := TFontStyles(LEReadLongint(AStream));
     2054  result.EmHeightRatio:= LEReadSingle(AStream);
     2055  result.Resolution := LEReadLongint(AStream);
     2056  result.PixelMetric.Baseline := LEReadLongint(AStream);
     2057  result.PixelMetric.xLine := LEReadLongint(AStream);
     2058  result.PixelMetric.CapLine := LEReadLongint(AStream);
     2059  result.PixelMetric.DescentLine := LEReadLongint(AStream);
     2060  result.PixelMetric.Lineheight := LEReadLongint(AStream);
    20612061  result.PixelMetric.Defined := result.PixelMetric.Lineheight > 0;
    20622062end;
  • GraphicTest/Packages/bgrabitmap/bgrawinbitmap.pas

    r472 r494  
    2929
    3030uses
    31   Classes, SysUtils, BGRADefaultBitmap, Windows, Graphics, GraphType;
     31  Classes, SysUtils, BGRALCLBitmap, Windows, Graphics, GraphType;
    3232
    3333type
    3434  { TBGRAWinBitmap }
    3535
    36   TBGRAWinBitmap = class(TBGRADefaultBitmap)
     36  TBGRAWinBitmap = class(TBGRALCLBitmap)
    3737  private
    3838    procedure AlphaCorrectionNeeded;
    3939  protected
    4040    DIB_SectionHandle: HBITMAP;
     41    FReversed: boolean;
    4142    function DIBitmapInfo(AWidth, AHeight: integer): TBitmapInfo;
    4243
     
    4849
    4950    procedure Init; override;
     51    function GetBitmap: TBitmap; override;
    5052
    5153  public
     54    procedure LoadFromBitmapIfNeeded; override;
     55    procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean=True); override;
     56    procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); override;
    5257    procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
    5358      ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); override;
     
    5661
    5762implementation
     63
     64uses BGRADefaultBitmap, BGRABitmapTypes;
    5865
    5966type
     
    118125end;
    119126
     127function TBGRAWinBitmap.GetBitmap: TBitmap;
     128begin
     129  Result:=inherited GetBitmap;
     130  if (LineOrder = riloTopToBottom) and not FReversed then
     131  begin
     132    VerticalFlip;
     133    FReversed:= true;
     134  end;
     135end;
     136
     137procedure TBGRAWinBitmap.LoadFromBitmapIfNeeded;
     138begin
     139  if FReversed then
     140  begin
     141    FReversed := false;
     142    VerticalFlip;
     143  end;
     144  if FAlphaCorrectionNeeded then
     145  begin
     146    DoAlphaCorrection;
     147  end;
     148end;
     149
     150procedure TBGRAWinBitmap.Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean);
     151begin
     152  if self = nil then exit;
     153  Draw(ACanvas, Classes.Rect(x,y,x+Width,y+Height), Opaque);
     154end;
     155
     156procedure TBGRAWinBitmap.Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean);
     157var
     158  info:      TBITMAPINFO;
     159begin
     160  if (self = nil) or (Width = 0) or (Height = 0) then exit;
     161  if TBGRAPixel_RGBAOrder then SwapRedBlue;
     162  if Opaque then
     163  begin
     164    info := DIBitmapInfo(Width, Height);
     165    if LineOrder = riloTopToBottom then
     166      StretchDIBits(ACanvas.Handle, Rect.Left, Rect.Bottom, Rect.Right -
     167        Rect.Left, Rect.Top - Rect.Bottom,
     168        0, 0, Width, Height, Data, info, DIB_RGB_COLORS, SRCCOPY)
     169    else
     170      StretchDIBits(ACanvas.Handle, Rect.Left, Rect.Top, Rect.Right -
     171        Rect.Left, Rect.Bottom - Rect.Top,
     172        0, 0, Width, Height, Data, info, DIB_RGB_COLORS, SRCCOPY);
     173  end
     174  else
     175  begin
     176    if Empty then exit;
     177    if LineOrder = riloTopToBottom then VerticalFlip;
     178    LoadFromBitmapIfNeeded;
     179    ACanvas.StretchDraw(Rect, Bitmap);
     180    if LineOrder = riloTopToBottom then VerticalFlip;
     181  end;
     182  if TBGRAPixel_RGBAOrder then SwapRedBlue;
     183end;
     184
    120185procedure TBGRAWinBitmap.DataDrawOpaque(ACanvas: TCanvas; Rect: TRect;
    121186  AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer);
     
    133198    IsFlipped := True;
    134199  end;
     200  if TBGRAPixel_RGBAOrder then
     201  begin
     202    if Temp = nil then
     203      Temp := TBGRAPtrBitmap.Create(AWidth, AHeight, AData);
     204    Temp.SwapRedBlue;
     205  end;
    135206
    136207  info := DIBitmapInfo(AWidth, AHeight);
     
    141212  if Temp <> nil then
    142213  begin
     214    if TBGRAPixel_RGBAOrder then Temp.SwapRedBlue;
    143215    if IsFlipped then
    144216      Temp.VerticalFlip;
     
    154226function TBGRAWinBitmap.DIBitmapInfo(AWidth, AHeight: integer): TBitmapInfo;
    155227begin
    156   with Result.bmiHeader do
     228  with {%H-}Result.bmiHeader do
    157229  begin
    158230    biSize      := sizeof(Result.bmiHeader);
  • GraphicTest/Packages/bgrabitmap/bgrawritelzp.pas

    r472 r494  
    3333implementation
    3434
    35 uses BGRACompressableBitmap, FPWritePNG;
     35uses BGRACompressableBitmap;
    3636
    3737{ TBGRAWriterLazPaint }
     
    4242  OldResampleFilter: TResampleFilter;
    4343  thumbnail: TBGRACustomBitmap;
    44   p: PBGRAPixel;
    45   n: integer;
    4644begin
    4745  result := false;
     
    6866    thumbnail := TBGRACustomBitmap(Img).Resample(w,h,rmFineResample);
    6967    TBGRACustomBitmap(Img).ResampleFilter := OldResampleFilter;
    70 
    71     p := thumbnail.data; //avoid PNG bug with black color transformed into transparent
    72     for n := thumbnail.NbPixels-1 downto 0 do
    73     begin
    74       if (p^.alpha <> 0) and (p^.red = 0) and (p^.green = 0) and (p^.blue = 0) then
    75         p^.blue := 1;
    76       inc(p);
    77     end;
    7868
    7969    try
     
    8979    finally
    9080      thumbnail.Free;
     81    end;
     82  end else
     83  begin
     84    thumbStream := TMemoryStream.Create;
     85    try
     86      TBGRACustomBitmap(Img).SaveToStreamAsPng(thumbStream);
     87      thumbStream.Position:= 0;
     88      Str.CopyFrom(thumbStream, thumbStream.Size);
     89      result := true;
     90    finally
     91      thumbStream.Free;
    9192    end;
    9293  end;
  • GraphicTest/Packages/bgrabitmap/blendpixelinline.inc

    r472 r494  
    914914begin
    915915  result := ((not a)*b shr 7 + a)*a div 255;
    916 { SVG specification would be :
    917 
    918   if b <= 128 then
    919     result := a - (((256 - b-b)*a shr 8)*(not a) shr 8)
    920   else if a <= 64 then
    921     result := a + ((b+b - 256)*((((a shl 2)*(a shl 2 + 256) shr 8)*integer(a - 256) shr 8) + a*7) shr 8)
    922   else
    923     result := a + ((b+b - 255)*(ByteSqrt(a)-a) shr 8);}
    924916end;
    925917
     
    946938      c.green * (not destalpha)) shr 8;
    947939    dest^.blue  := (ByteSoftLightInline(dest^.blue, c.blue) * destalpha +
     940      c.blue * (not destalpha)) shr 8;
     941    dest^.alpha := c.alpha;
     942  end;
     943end;
     944
     945function ByteSvgSoftLightInline(a,b: byte): byte; inline;
     946begin
     947  if b <= 128 then
     948    result := a - (((256 - b-b)*a shr 8)*(not a) shr 8)
     949  else
     950  begin
     951    dec(b, 128);
     952    if a <= 64 then
     953      result := a + ((b+b)   * NativeUInt(a*7 - ((a shl 2)*(a shl 2 + 256)*NativeUInt(256 - a) shr 16)) shr 8)
     954    else
     955      result := a + ((b+b+1) * NativeUInt(ByteSqrt(a)-a) shr 8);
     956  end;
     957end;
     958
     959procedure SvgSoftLightPixelInline(dest: PBGRAPixel; c: TBGRAPixel); inline;
     960var
     961  destalpha: byte;
     962begin
     963  destalpha   := dest^.alpha;
     964  if destalpha = 0 then
     965  begin
     966    dest^ := c
     967  end else
     968  if destalpha = 255 then
     969  begin
     970    dest^.red := ByteSvgSoftLightInline(dest^.red, c.red);
     971    dest^.green := ByteSvgSoftLightInline(dest^.green, c.green);
     972    dest^.blue  := ByteSvgSoftLightInline(dest^.blue, c.blue);
     973    dest^.alpha := c.alpha;
     974  end else
     975  begin
     976    dest^.red   := (ByteSvgSoftLightInline(dest^.red, c.red) * destalpha +
     977      c.red * (not destalpha)) shr 8;
     978    dest^.green := (ByteSvgSoftLightInline(dest^.green, c.green) * destalpha +
     979      c.green * (not destalpha)) shr 8;
     980    dest^.blue  := (ByteSvgSoftLightInline(dest^.blue, c.blue) * destalpha +
    948981      c.blue * (not destalpha)) shr 8;
    949982    dest^.alpha := c.alpha;
  • GraphicTest/Packages/bgrabitmap/blendpixels.inc

    r452 r494  
    290290        begin
    291291          SoftLightPixelInline(pdest, psrc^);
     292          Inc(pdest);
     293          Inc(psrc);
     294          Dec(Count);
     295        end;
     296end;
     297
     298procedure SvgSoftLightPixels(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer);
     299begin
     300  while Count > 0 do
     301        begin
     302          SvgSoftLightPixelInline(pdest, psrc^);
    292303          Inc(pdest);
    293304          Inc(psrc);
     
    327338      @GlowPixels, @ReflectPixels, @LinearOverlayPixels, @OverlayPixels, @DarkenPixels, @LinearMultiplyPixels, @ColorBurnPixels,
    328339      @DifferencePixels, @LinearDifferencePixels, @ExclusionPixels, @LinearExclusionPixels, @SubtractPixels, @LinearSubtractPixels,
    329       @SubtractInversePixels, @LinearSubtractInversePixels, @NegationPixels, @LinearNegationPixels, @BlendXorPixels);
     340      @SubtractInversePixels, @LinearSubtractInversePixels, @NegationPixels, @LinearNegationPixels, @BlendXorPixels, @SvgSoftLightPixels);
    330341
    331342procedure BlendPixels(pdest: PBGRAPixel; psrc: PBGRAPixel;
  • GraphicTest/Packages/bgrabitmap/blendpixelsover.inc

    r452 r494  
    709709end;
    710710
     711procedure SvgSoftLightPixelsLinearOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte);
     712var temp: TBGRAPixel;
     713begin
     714  while Count > 0 do
     715        begin
     716          temp := pdest^;
     717          SvgSoftLightPixelInline(@temp, psrc^);
     718          FastBlendPixelInline(pdest, temp, opacity);
     719          Inc(pdest);
     720          Inc(psrc);
     721          Dec(Count);
     722        end;
     723end;
     724
    711725procedure SoftLightPixelsDrawOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte);
    712726var temp: TBGRAPixel;
     
    716730          temp := pdest^;
    717731          SoftLightPixelInline(@temp, psrc^);
     732          DrawPixelInlineWithAlphaCheck(pdest, temp, opacity);
     733          Inc(pdest);
     734          Inc(psrc);
     735          Dec(Count);
     736        end;
     737end;
     738
     739procedure SvgSoftLightPixelsDrawOver(pdest: PBGRAPixel; psrc: PBGRAPixel; Count: integer; Opacity: byte);
     740var temp: TBGRAPixel;
     741begin
     742  while Count > 0 do
     743        begin
     744          temp := pdest^;
     745          SvgSoftLightPixelInline(@temp, psrc^);
    718746          DrawPixelInlineWithAlphaCheck(pdest, temp, opacity);
    719747          Inc(pdest);
     
    790818      @GlowPixelsDrawOver, @ReflectPixelsDrawOver, @LinearOverlayPixelsDrawOver, @OverlayPixelsDrawOver, @DarkenPixelsDrawOver, @LinearMultiplyPixelsDrawOver, @ColorBurnPixelsDrawOver,
    791819      @DifferencePixelsDrawOver, @LinearDifferencePixelsDrawOver, @ExclusionPixelsDrawOver, @LinearExclusionPixelsDrawOver, @SubtractPixelsDrawOver, @LinearSubtractPixelsDrawOver,
    792       @SubtractInversePixelsDrawOver, @LinearSubtractInversePixelsDrawOver, @NegationPixelsDrawOver, @LinearNegationPixelsDrawOver, @BlendXorPixelsDrawOver),
     820      @SubtractInversePixelsDrawOver, @LinearSubtractInversePixelsDrawOver, @NegationPixelsDrawOver, @LinearNegationPixelsDrawOver, @BlendXorPixelsDrawOver, @SvgSoftLightPixelsDrawOver),
    793821      (@FastBlendPixelsWithOpacity, @FastBlendPixelsWithOpacity,
    794822      @LightenPixelsLinearOver, @ScreenPixelsLinearOver, @AddPixelsLinearOver, @LinearAddPixelsLinearOver, @ColorDodgePixelsLinearOver, @DividePixelsLinearOver, @NiceGlowPixelsLinearOver, @SoftLightPixelsLinearOver, @HardLightPixelsLinearOver,
    795823      @GlowPixelsLinearOver, @ReflectPixelsLinearOver, @LinearOverlayPixelsLinearOver, @OverlayPixelsLinearOver, @DarkenPixelsLinearOver, @LinearMultiplyPixelsLinearOver, @ColorBurnPixelsLinearOver,
    796824      @DifferencePixelsLinearOver, @LinearDifferencePixelsLinearOver, @ExclusionPixelsLinearOver, @LinearExclusionPixelsLinearOver, @SubtractPixelsLinearOver, @LinearSubtractPixelsLinearOver,
    797       @SubtractInversePixelsLinearOver, @LinearSubtractInversePixelsLinearOver, @NegationPixelsLinearOver, @LinearNegationPixelsLinearOver, @BlendXorPixelsLinearOver));
     825      @SubtractInversePixelsLinearOver, @LinearSubtractInversePixelsLinearOver, @NegationPixelsLinearOver, @LinearNegationPixelsLinearOver, @BlendXorPixelsLinearOver, @SvgSoftLightPixelsLinearOver));
    798826
    799827{************************* calling procedure ***************************}
  • GraphicTest/Packages/bgrabitmap/blurfast.inc

    r472 r494  
    11
    22var
    3   blurRow: array of UInt32or64;
     3  blurRowY,blurRowX: packed array of NativeUInt;
     4  iRadiusX,iRadiusY: NativeInt;
     5  weightFactor: NativeUInt;
    46
    57  { Compute weights of pixels in a row }
    68  procedure ComputeBlurRow;
    79  var
    8     i: Integer;
    9   begin
    10     SetLength(blurRow, 2*radius+1);
    11     for i := 0 to radius do
    12     begin
    13       blurRow[i] := i+1;
    14       blurRow[high(blurRow)-i] := blurRow[i];
     10    i: NativeInt;
     11    ofs: single;
     12  begin
     13    SetLength(blurRowX, 2*iRadiusX+1);
     14    if frac(radiusX)=0 then ofs := 1 else ofs := frac(radiusX);
     15    for i := 0 to iRadiusX do
     16    begin
     17      blurRowX[i] := round((i+ofs)*weightFactor);
     18      blurRowX[high(blurRowX)-i] := blurRowX[i];
     19    end;
     20    SetLength(blurRowY, 2*iRadiusY+1);
     21    if frac(radiusY)=0 then ofs := 1 else ofs := frac(radiusY);
     22    for i := 0 to iRadiusY do
     23    begin
     24      blurRowY[i] := round((i+ofs)*weightFactor);
     25      blurRowY[high(blurRowY)-i] := blurRowY[i];
    1526    end;
    1627  end;
     
    1930var
    2031  srcDelta,
    21   verticalWeightShift, horizontalWeightShift: integer;
     32  verticalWeightShift, horizontalWeightShift: NativeInt;
     33  ys1,ys2: NativeInt;
    2234
    2335  { Compute blur result in a vertical direction }
    24   procedure ComputeVerticalRow(psrc: PBGRAPixel; var sums: TRowSum; ys1,ys2: integer); inline;
    25   var ys: integer;
    26       c: TBGRAPixel;
    27       w,aw: cardinal;
    28   begin
    29     for ys := ys1 to ys2 do
     36  procedure ComputeVerticalRow(psrc: PBGRAPixel; var sums: TRowSum; pw: PNativeUInt; count: NativeInt);
     37  var w: NativeUInt;
     38      c: DWord;
     39  begin
     40    while count > 0 do
    3041    with sums do
    3142    begin
    32       c := psrc^;
    33       w := blurRow[ys]; //apply pixel weight
    34       aw := c.alpha*w;
    35       sumA += aw;
     43      dec(count);
     44      w := pw^; //apply pixel weight
     45      inc(pw);
     46      c := PDWord(psrc)^;
     47      inc(PByte(psrc),srcDelta);
    3648      aDiv += w;
    37 
    38       aw := aw shr verticalWeightShift;
     49      w *= ((c shr TBGRAPixel_AlphaShift) and $ff);
     50      sumA += w;
     51      w := w shr verticalWeightShift;
     52      rgbDiv += w;
    3953      {$hints off}
    40       sumR += c.red*aw;
    41       sumG += c.green*aw;
    42       sumB += c.blue*aw;
    43       rgbDiv += aw;
     54      sumR += ((c shr TBGRAPixel_RedShift) and $ff)*w;
     55      sumG += ((c shr TBGRAPixel_GreenShift) and $ff)*w;
     56      sumB += ((c shr TBGRAPixel_BlueShift) and $ff)*w;
    4457      {$hints on}
    45       inc(psrc,srcDelta);
    4658    end;
    4759  end;
    4860
    4961var
    50   sums: array of TRowSum;
    51   sumStartIndex,curIndex: integer;
     62  psum, psumEnd: PRowSum;
     63  sums: packed array of TRowSum;
     64  sumStartIndex: NativeInt;
    5265  total: TRowSum;
    5366  extendedTotal: TExtendedRowSum;
    54   yb,xb,xs,ys1,ys2,x: integer;
    55   w: cardinal;
    56   pdest: PBGRAPixel;
    57   bmpWidth,bmpHeight : integer;
     67  yb,xb,xs,x,xEnd: NativeInt;
     68  w: NativeUInt;
     69  pw: PNativeUInt;
     70  psrc,pdest: PBGRAPixel;
     71  bmpWidth,bmpHeight : NativeInt;
    5872  accumulationFactor: double;
    5973  bounds: TRect;
     74  highSum: NativeInt;
     75  tempDest: TBGRACustomBitmap;
    6076
    6177begin
    62   if radius = 0 then
     78  radiusX := round(radiusX*10)*0.1;
     79  radiusY := round(radiusY*10)*0.1;
     80  if (radiusX <= 0) and (radiusY <= 0) then
    6381  begin
    6482    ADestination.PutImage(0,0,bmp,dmSet);
    6583    exit;
    6684  end;
     85  iRadiusX := ceil(radiusX);
     86  iRadiusY := ceil(radiusY);
     87  if (frac(radiusX)=0) and (frac(radiusY)=0) then
     88    weightFactor:= 1
     89  else
     90    weightFactor:= 10;
    6791  bmpWidth := bmp.Width;
    6892  bmpHeight := bmp.Height;
     
    7296  bounds := bmp.GetImageBounds;
    7397  if IsRectEmpty(bounds) then exit;
    74   bounds.Left   := max(0, bounds.Left - radius);
    75   bounds.Top    := max(0, bounds.Top - radius);
    76   bounds.Right  := min(bmp.Width, bounds.Right + radius);
    77   bounds.Bottom := min(bmp.Height, bounds.Bottom + radius);
     98  bounds.Left   := max(0, bounds.Left - iRadiusX);
     99  bounds.Top    := max(0, bounds.Top - iRadiusY);
     100  bounds.Right  := min(bmp.Width, bounds.Right + iRadiusX);
     101  bounds.Bottom := min(bmp.Height, bounds.Bottom + iRadiusY);
    78102  if not IntersectRect(bounds,bounds,ABounds) then exit;
    79103
    80   accumulationFactor := (radius+2)*(radius+1) div 2 + (radius+1)*radius div 2;
     104  if radiusX*radiusY >= 100 then
     105  begin
     106    tempDest := ADestination.NewBitmap(ADestination.Width,ADestination.Height);
     107    FilterBlurBox(bmp,bounds,radiusX/3.2,radiusY/3.2,tempDest);
     108    FilterBlurBox(tempDest,bounds,radiusX/2.9,radiusY/2.9,ADestination);
     109    FilterBlurBox(ADestination,bounds,radiusX/3.2,radiusY/3.2,tempDest);
     110    FilterBlurBox(tempDest,bounds,radiusX/2.3,radiusY/2.3,ADestination, ACheckShouldStop);
     111    tempDest.Free;
     112    exit;
     113  end;
     114
     115  accumulationFactor := (iRadiusY+2)*(iRadiusY+1) div 2 + (iRadiusY+1)*iRadiusY div 2;
     116  accumulationFactor *= sqr(weightFactor);
    81117  verticalWeightShift := 0;
    82   while accumulationFactor > (high(UInt32or64) shr 16) + 1 do
     118  while accumulationFactor > (high(NativeUInt) shr 16) + 1 do
    83119  begin
    84120    inc(verticalWeightShift);
     
    86122  end;
    87123  horizontalWeightShift:= 0;
    88   accumulationFactor *= ((radius+2)*(radius+1) div 2 + (radius+1)*radius div 2);
    89   while accumulationFactor > (high(UInt32or64) shr 16) + 1 do
     124  accumulationFactor *= ((iRadiusX+2)*(iRadiusX+1) div 2 + (iRadiusX+1)*iRadiusX div 2);
     125  accumulationFactor *= sqr(weightFactor);
     126  while accumulationFactor > (high(NativeUInt) shr 16) + 1 do
    90127  begin
    91128    inc(horizontalWeightShift);
     
    94131  ComputeBlurRow;
    95132  //current vertical sums
    96   setlength(sums, 2*radius+1);
     133  setlength(sums, 2*iRadiusX+1);
     134  highSum := high(Sums);
     135  psumEnd := @sums[highSum];
     136  inc(psumEnd);
    97137  if bmp.LineOrder = riloTopToBottom then
    98     srcDelta := bmpWidth else
    99       srcDelta := -bmpWidth;
     138    srcDelta := bmpWidth*sizeof(TBGRAPixel) else
     139      srcDelta := -bmpWidth*sizeof(TBGRAPixel);
     140
     141  xEnd := bounds.left-iRadiusX+highSum;
     142  if xEnd >= bmpWidth then xEnd := bmpWidth-1;
    100143  //loop through destination bitmap
    101144  for yb := bounds.top to bounds.bottom-1 do
     
    103146    if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
    104147    //evalute available vertical range
    105     if yb - radius < 0 then
    106       ys1 := radius - yb
     148    if yb - iRadiusY < 0 then
     149      ys1 := iRadiusY - yb
    107150    else
    108151      ys1 := 0;
    109     if yb + radius >= bmpHeight then
    110       ys2 := bmpHeight - yb + radius - 1
     152    if yb + iRadiusY >= bmpHeight then
     153      ys2 := bmpHeight-1 - yb + iRadiusY
    111154    else
    112       ys2 := high(sums);
     155      ys2 := 2*iRadiusY;
    113156
    114157    { initial vertical rows are computed here. Later,
    115158      for each pixel, vertical sums are shifted, so there
    116159      is only one vertical sum to calculate }
    117     for xs := 0 to high(sums) do
    118     begin
    119       fillchar(sums[xs],sizeof(TRowSum),0);
    120       x := bounds.left-radius+xs;
    121       if (x >= 0) and (x < bmpWidth) then
    122         ComputeVerticalRow(bmp.ScanLine[yb-radius+ys1]+x,sums[xs],ys1,ys2);
     160    fillchar(sums[0],sizeof(TRowSum)*length(sums),0);
     161    x := bounds.left-iRadiusX;
     162    if x < 0 then
     163    begin
     164      xs := -x;
     165      x := 0;
     166    end else
     167      xs := 0;
     168    psrc := bmp.ScanLine[yb-iRadiusY+ys1]+x;
     169    psum := @sums[xs];
     170    pw := @blurRowY[ys1];
     171    while true do
     172    begin
     173      ComputeVerticalRow(psrc,psum^,pw,ys2-ys1+1);
     174      inc(x);
     175      inc(psrc);
     176      if x > xEnd then break;
     177      inc(psum);
    123178    end;
    124179    sumStartIndex := 0;
     
    128183    begin
    129184      //add vertical rows
    130       curIndex:= sumStartIndex;
     185      pw := @blurRowX[0];
     186      psum := @sums[sumStartIndex];
    131187      if horizontalWeightShift > 4 then
    132188      begin //we don't want to loose too much precision
    133         {$hints off}
    134         fillchar(extendedTotal,sizeof(extendedTotal),0);
    135         {$hints on}
    136         for xs := 0 to high(sums) do
    137         with sums[curIndex] do
     189        fillchar({%H-}extendedTotal,sizeof(extendedTotal),0);
     190        for xs := highSum downto 0 do
     191        with psum^ do
    138192        begin
    139           w := blurRow[xs];
     193          w := pw^;
     194          inc(pw);
    140195          extendedTotal.sumA += TExtendedRowValue(sumA)*w;
    141196          extendedTotal.aDiv += TExtendedRowValue(aDiv)*w;
     
    144199          extendedTotal.sumB += TExtendedRowValue(sumB)*w;
    145200          extendedTotal.rgbDiv += TExtendedRowValue(rgbDiv)*w;
    146           inc(curIndex);
    147           if curIndex = length(sums) then curIndex := 0;
     201          inc(psum);
     202          if psum >= psumEnd then pSum := @sums[0];
    148203        end;
    149204        if (extendedTotal.aDiv > 0) and (extendedTotal.rgbDiv > 0) then
     
    154209      if horizontalWeightShift > 0 then
    155210      begin //lossy but efficient way
    156         {$hints off}
    157         fillchar(total,sizeof(total),0);
    158         {$hints on}
    159         for xs := 0 to high(sums) do
    160         with sums[curIndex] do
     211        fillchar({%H-}total,sizeof(total),0);
     212        for xs := highSum downto 0 do
     213        with psum^ do
    161214        begin
    162           w := blurRow[xs];
     215          w := pw^;
     216          inc(pw);
    163217          total.sumA += sumA*w shr horizontalWeightShift;
    164218          total.aDiv += aDiv*w shr horizontalWeightShift;
     
    167221          total.sumB += sumB*w shr horizontalWeightShift;
    168222          total.rgbDiv += rgbDiv*w shr horizontalWeightShift;
    169           inc(curIndex);
    170           if curIndex = length(sums) then curIndex := 0;
     223          inc(psum);
     224          if psum >= psumEnd then pSum := @sums[0];
    171225        end;
    172226        if (total.aDiv > 0) and (total.rgbDiv > 0) then
     
    179233        fillchar(total,sizeof(total),0);
    180234        {$hints on}
    181         for xs := 0 to high(sums) do
    182         with sums[curIndex] do
     235        for xs := highSum downto 0 do
     236        with psum^ do
    183237        begin
    184           w := blurRow[xs];
     238          w := pw^;
     239          inc(pw);
    185240          total.sumA += sumA*w;
    186241          total.aDiv += aDiv*w;
     
    189244          total.sumB += sumB*w;
    190245          total.rgbDiv += rgbDiv*w;
    191           inc(curIndex);
    192           if curIndex = length(sums) then curIndex := 0;
     246          inc(psum);
     247          if psum >= psumEnd then pSum := @sums[0];
    193248        end;
    194249        if (total.aDiv > 0) and (total.rgbDiv > 0) then
    195           pdest^:= ComputeAverage(total)
     250          pdest^ := ComputeAverage(total)
    196251        else
    197252          pdest^:= BGRAPixelTransparent;
     
    199254      inc(pdest);
    200255      //shift vertical rows
    201       fillchar(sums[sumStartIndex],sizeof(TRowSum),0);
    202       x := xb+1-radius+high(sums);
    203       if (x >= 0) and (x < bmpWidth) then
    204         ComputeVerticalRow(bmp.ScanLine[yb-radius+ys1]+x,sums[sumStartIndex],ys1,ys2);
     256      psum := @sums[sumStartIndex];
     257      fillchar(psum^,sizeof(TRowSum),0);
     258      if x < bmpWidth then
     259      begin
     260        ComputeVerticalRow(psrc,psum^,@blurRowY[ys1],ys2-ys1+1);
     261        inc(x);
     262        inc(psrc);
     263      end;
    205264      inc(sumStartIndex);
    206       if sumStartIndex = length(sums) then sumStartIndex := 0;
     265      if sumStartIndex > highSum then sumStartIndex := 0;
    207266    end;
    208267  end;
  • GraphicTest/Packages/bgrabitmap/blurnormal.inc

    r472 r494  
     1type
     2  PWeightedPixel = ^TWeightedPixel;
     3  TWeightedPixel = packed record
     4    Coord: TPoint;
     5    Weight: NativeInt;
     6    PtrOfs: NativeInt;
     7  end;
     8
    19var
    210  maskWidth,maskHeight: integer;
    3   blurOfs:      TPoint;
    4   PixelWeight:  array of integer;
    5   PixelOfs:     array of TPoint;
     11  blurOfs: TPoint;
     12  ppixel: PWeightedPixel;
     13  Pixel: array of TWeightedPixel;
    614  PixelArrayLineStart: array of integer;
    7   DiffPixelWeight:  array of integer;
    8   DiffPixelOfs:     array of TPoint;
     15  DiffPixel: array of TWeightedPixel;
    916  DiffPixelArrayLineStart: array of integer;
    1017
    11   procedure LoadMask;
    12   var x,y,n: integer;
    13       tempWeight: integer;
    14       diffMask: array of array of integer;
     18  bmpWidth,bmpHeight,lineDelta: NativeInt;
     19
     20  procedure LoadMask(out ABlurOfs: TPoint);
     21  var x,y,n,i: NativeInt;
     22      tempWeight: NativeInt;
     23      diffMask: array of packed array of NativeInt;
     24      p: PBGRAPixel;
    1525  begin
    16     blurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1);
     26    ABlurOfs := point(blurMask.Width shr 1, blurMask.Height shr 1);
    1727
    1828    //count number of non empty pixels
     
    2030    maskHeight := blurMask.Height;
    2131    n := 0;
    22     for y := 0 to maskHeight - 1 do
    23       for x := 0 to maskWidth - 1 do
    24         if blurMask.GetPixel(x, y).red <> 0 then Inc(n);
     32    p := blurMask.Data;
     33    for i := blurMask.NbPixels-1 downto 0 do
     34    begin
     35      if p^.red <> 0 then inc(n);
     36      inc(p);
     37    end;
    2538
    2639    //initialize arrays
    2740    setlength(diffMask, maskHeight, maskWidth+1);
    2841    for y := 0 to maskHeight - 1 do
    29       for x := 0 to maskWidth do
    30         diffMask[y,x] := 0;
    31 
    32     setlength(PixelWeight, n);
    33     setlength(PixelOfs, n);
     42      fillchar(diffMask[y,0], (maskWidth+1)*sizeof(NativeInt), 0);
     43
     44    setlength(Pixel, n);
    3445    setlength(PixelArrayLineStart, maskHeight+1);  //stores the first pixel of each line
    3546    n := 0;
     
    3849    begin
    3950      PixelArrayLineStart[y] := n;
     51      p := blurMask.ScanLine[y];
    4052      for x := 0 to maskWidth - 1 do
    4153      begin
    42         tempWeight := blurMask.GetPixel(x, y).red;
     54        tempWeight := p^.red;
     55        inc(p);
    4356        diffMask[y,x] -= tempWeight;
    4457        diffMask[y,x+1] += tempWeight;
     
    4659        if tempWeight <> 0 then
    4760        begin
    48           PixelWeight[n] := tempWeight;
    49           PixelOfs[n] := Point(x,y);
     61          Pixel[n].Weight := tempWeight;
     62          Pixel[n].Coord := Point(x,y);
     63          Pixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X)*sizeof(TBGRAPixel);
    5064          Inc(n);
    5165        end;
     
    6175
    6276    //initialize arrays
    63     setlength(DiffPixelWeight, n);
    64     setlength(DiffPixelOfs, n);
     77    setlength(DiffPixel, n);
    6578    setlength(DiffPixelArrayLineStart, maskHeight+1);  //stores the first pixel of each diff line
    6679    n := 0;
     
    7487        if tempWeight <> 0 then
    7588        begin
    76           DiffPixelWeight[n] := tempWeight;
    77           DiffPixelOfs[n] := Point(x-1,y);
     89          DiffPixel[n].Weight := tempWeight;
     90          DiffPixel[n].Coord := Point(x-1,y);
     91          DiffPixel[n].PtrOfs := (y-ABlurOfs.Y)*lineDelta + (x-ABlurOfs.X-1)*sizeof(TBGRAPixel);
    7892          Inc(n);
    7993        end;
     
    8397  end;
    8498
    85 var
    86   curScans: array of PBGRAPixel;
    87   bounds: TRect;
    88 
    89   {procedure ShowCurScans;
    90   var str: string;
    91     i: Integer;
    92   begin
    93     str := '';
    94     for i := 0 to high(curScans) do
    95     begin
    96       if i <> 0 then str += ', ';
    97       if curScans[i]=nil then str += 'nil' else
    98         str += 'bmp['+inttostr(curScans[i]-bmp.Data)+']';
    99     end;
    100     ShowMessage(str);
    101   end;}
    102 
    103   function PrepareScan: boolean;
    104   var
    105     bmpY: integer;
    106     y   : Integer;
     99  function PrepareScan(AWantedBounds: TRect; out AClippedBounds: TRect): boolean;
    107100  begin
    108101    //evaluate required bounds taking blur radius into acount
    109     bounds := bmp.GetImageBounds;
    110     if IsRectEmpty(bounds) then
     102    AClippedBounds := bmp.GetImageBounds;
     103    if IsRectEmpty(AClippedBounds) then
    111104    begin
    112105      result := false;
    113106      exit;
    114107    end;
    115     bounds.Left   := max(0, bounds.Left - blurOfs.X);
    116     bounds.Top    := max(0, bounds.Top - blurOfs.Y);
    117     bounds.Right  := min(bmp.Width, bounds.Right + maskWidth - 1 - blurOfs.X);
    118     bounds.Bottom := min(bmp.Height, bounds.Bottom + maskHeight - 1 - blurOfs.Y);
    119     if not IntersectRect(bounds, bounds, ABounds) then
     108    AClippedBounds.Left   := max(0, AClippedBounds.Left - blurOfs.X);
     109    AClippedBounds.Top    := max(0, AClippedBounds.Top - blurOfs.Y);
     110    AClippedBounds.Right  := min(bmpWidth, AClippedBounds.Right + maskWidth - 1 - blurOfs.X);
     111    AClippedBounds.Bottom := min(bmpHeight, AClippedBounds.Bottom + maskHeight - 1 - blurOfs.Y);
     112    if not IntersectRect(AClippedBounds, AClippedBounds, AWantedBounds) then
    120113    begin
    121114      result := false;
     
    123116    end;
    124117
    125     //init scanlines
    126     setlength(curScans, maskHeight);
    127     for y := 0 to maskHeight-1 do
    128     begin
    129       bmpY := y+bounds.Top-blurOfs.Y;
    130       if (bmpY < 0) or (bmpY >= bmp.Height) then
    131         curScans[y] := nil else
    132           curScans[y] := bmp.ScanLine[bmpY];
    133     end;
    134     //ShowCurScans;
    135118    result := true;
    136119  end;
    137120
    138   procedure ShiftScan(NewY: integer); inline;
    139   var y: integer;
    140   begin
    141     for y := 0 to maskHeight-2 do
    142      curScans[y] := curScans[y+1];
    143 
    144     //get next scanline
    145     if newY >= bmp.Height then
    146       curScans[maskHeight-1] := nil
    147     else
    148       curScans[maskHeight-1] := bmp.ScanLine[newY];
    149     //ShowCurScans;
    150   end;
    151 
    152121var
    153   yb, xb: integer;
    154   mindy, maxdy, n: integer;
    155   bmpWidth,bmpX: integer;
    156   pixMaskAlpha, maskAlpha: integer;
     122  bounds: TRect;
     123  yb, xb: NativeInt;
     124  mindy, maxdy, n, nStart, nCount, nDiffStart, nDiffCount: NativeInt;
     125  bmpX,bmpXBase,bmpYBase: NativeInt;
     126  pixMaskAlpha, maskAlpha: NativeInt;
    157127  tempPixel: TBGRAPixel;
    158128  pdest : PBGRAPixel;
    159   pt: TPoint;
     129  psrc : PByte;
    160130
    161131begin
    162   LoadMask;
    163 
    164   if (ADestination.Width <> bmp.Width) or (ADestination.Height <> bmp.Height) then
     132  bmpWidth := bmp.Width;
     133  bmpHeight:= bmp.Height;
     134  if bmp.LineOrder = riloTopToBottom then
     135    lineDelta := bmpWidth*sizeof(TBGRAPixel) else
     136    lineDelta := -bmpWidth*sizeof(TBGRAPixel);
     137
     138  if (ADestination.Width <> bmpWidth) or (ADestination.Height <> bmpHeight) then
    165139    raise exception.Create('Dimension mismatch');
    166140
    167   if not PrepareScan then exit; //nothing to do
    168 
    169   bmpWidth := bmp.Width;
     141  LoadMask(blurOfs);
     142  if not PrepareScan(ABounds, bounds) then exit; //nothing to do
     143
     144  bmpYBase := bounds.Top - blurOfs.Y;
     145
    170146  //loop through destination
    171147  for yb := bounds.Top to bounds.Bottom - 1 do
    172148  begin
    173149    if (ACheckShouldStop <> nil) and ACheckShouldStop(yb) then break;
     150    psrc := PByte(bmp.ScanLine[yb]+bounds.Left);
    174151    pdest := ADestination.ScanLine[yb] + bounds.Left;
    175152    //compute vertical range
    176153    mindy := max(-blurOfs.Y, -yb);
    177     maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmp.Height - 1 - yb);
     154    maxdy := min(blurMask.Height - 1 - blurOfs.Y, bmpHeight - 1 - yb);
    178155
    179156    sumR   := 0;
     
    186163    {$endif}
    187164
     165    bmpXBase := bounds.Left-blurOfs.X;
     166    nStart := PixelArrayLineStart[mindy+blurOfs.Y];
     167    nCount  := PixelArrayLineStart[maxdy+blurOfs.Y+1]-nStart;
     168    ppixel:= @Pixel[nStart];
    188169    //go through pixel list of the current vertical range
    189     for n := PixelArrayLineStart[mindy+blurOfs.Y] to PixelArrayLineStart[maxdy+blurOfs.Y+1]-1 do
    190     begin
    191       pt := PixelOfs[n];
    192       bmpX := bounds.Left-blurOfs.X+pt.x;
     170    for n := nCount-1 downto 0 do
     171    begin
     172      bmpX := bmpXBase+ppixel^.Coord.x;
    193173      //check horizontal range
    194174      if (bmpX >= 0) and (bmpX < bmpWidth) then
    195175      begin
    196         tempPixel := (curScans[pt.y]+bmpX)^;
    197         maskAlpha := PixelWeight[n];
     176        tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
     177        maskAlpha := ppixel^.Weight;
    198178        pixMaskAlpha := maskAlpha * tempPixel.alpha;
    199179        sumA    += pixMaskAlpha;
     
    209189        {$hints on}
    210190      end;
    211     end;
    212 
    213     for xb := bounds.Left to Bounds.Right - 1 do
    214     begin
    215       if xb > bounds.left then
    216       begin
    217         for n := DiffPixelArrayLineStart[mindy+blurOfs.Y] to DiffPixelArrayLineStart[maxdy+blurOfs.Y+1]-1 do
    218         begin
    219           pt := DiffPixelOfs[n];
    220           bmpX := xb-blurOfs.X+pt.x;
     191      inc(ppixel);
     192    end;
     193
     194    //compute average
     195    if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
     196      pdest^ := BGRAPixelTransparent
     197    else
     198      pdest^ := computeAverage;
     199
     200    nDiffStart := DiffPixelArrayLineStart[mindy+blurOfs.Y];
     201    nDiffCount := DiffPixelArrayLineStart[maxdy+blurOfs.Y+1]-nDiffStart;
     202
     203    if nDiffCount < nCount then
     204    begin
     205      for xb := bounds.Left+1 to Bounds.Right - 1 do
     206      begin
     207        Inc(pdest);
     208        inc(bmpXBase);
     209        inc(psrc,sizeof(TBGRAPixel));
     210
     211        ppixel:= @DiffPixel[nDiffStart];
     212        for n := nDiffCount-1 downto 0 do
     213        begin
     214          bmpX := bmpXBase+ppixel^.Coord.x;
    221215          if (bmpX >= 0) and (bmpX < bmpWidth) then
    222216          begin
    223             tempPixel := (curScans[pt.y]+bmpX)^;
    224             maskAlpha := DiffPixelWeight[n];
     217            tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
     218            maskAlpha := ppixel^.Weight;
    225219            pixMaskAlpha := maskAlpha * tempPixel.alpha;
    226220            sumA    += pixMaskAlpha;
     
    236230            {$hints on}
    237231          end;
    238         end;
    239       end;
    240 
    241       //compute average
    242       if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
    243         pdest^ := BGRAPixelTransparent
    244       else
    245         pdest^ := computeAverage;
    246 
    247       Inc(pdest);
    248     end;
    249 
    250     ShiftScan(yb-blurOfs.Y+maskHeight);
     232          inc(ppixel);
     233        end;
     234
     235        //compute average
     236        if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
     237          pdest^ := BGRAPixelTransparent
     238        else
     239          pdest^ := ComputeAverage;
     240      end;
     241    end else
     242    begin
     243      for xb := bounds.Left+1 to Bounds.Right - 1 do
     244      begin
     245        Inc(pdest);
     246        inc(bmpXBase);
     247        inc(psrc,sizeof(TBGRAPixel));
     248
     249        sumR   := 0;
     250        sumG   := 0;
     251        sumB   := 0;
     252        sumA   := 0;
     253        Adiv   := 0;
     254        {$ifdef PARAM_MASKSHIFT}
     255        RGBdiv := 0;
     256        {$endif}
     257
     258        ppixel:= @Pixel[nStart];
     259        for n := nCount-1 downto 0 do
     260        begin
     261          bmpX := bmpXBase+ppixel^.Coord.x;
     262          //check horizontal range
     263          if (bmpX >= 0) and (bmpX < bmpWidth) then
     264          begin
     265            tempPixel := PBGRAPixel(psrc + ppixel^.PtrOfs)^;
     266            maskAlpha := ppixel^.Weight;
     267            pixMaskAlpha := maskAlpha * tempPixel.alpha;
     268            sumA    += pixMaskAlpha;
     269            Adiv    += maskAlpha;
     270            {$ifdef PARAM_MASKSHIFT}
     271            pixMaskAlpha := pixMaskAlpha shr maskShift;
     272            RGBdiv  += pixMaskAlpha;
     273            {$endif}
     274            {$hints off}
     275            sumR    += tempPixel.red * pixMaskAlpha;
     276            sumG    += tempPixel.green * pixMaskAlpha;
     277            sumB    += tempPixel.blue * pixMaskAlpha;
     278            {$hints on}
     279          end;
     280          inc(ppixel);
     281        end;
     282
     283        //compute average
     284        if (Adiv <= 0) {$ifdef PARAM_MASKSHIFT} or (RGBdiv <= 0) {$endif} then
     285          pdest^ := BGRAPixelTransparent
     286        else
     287          pdest^ := computeAverage;
     288      end;
     289    end;
     290
     291    inc(bmpYBase);
    251292  end;
    252293  ADestination.InvalidateBitmap;
  • GraphicTest/Packages/bgrabitmap/csscolorconst.inc

    r472 r494  
    1 {$IFDEF INCLUDE_COLOR_CONST}
    2 {$UNDEF INCLUDE_COLOR_CONST}
     1{=== Color definitions ===}
     2
     3{$IFDEF INCLUDE_INTERFACE}
     4{$UNDEF INCLUDE_INTERFACE}
     5var
     6  {* This is the value used for transparent pixels. In theory, any
     7     color with alpha = 0 is transparent, however it is recommended to
     8     use all other channels to zero as well. }
     9  BGRAPixelTransparent: TBGRAPixel;
     10
     11  {* [#FFFFFF] White opaque }
     12  BGRAWhite: TBGRAPixel;
     13  {* [#000000] Black opaque }
     14  BGRABlack: TBGRAPixel;
     15
    316const
     17  {* This color [#000001] looks just like black. It is needed for drawing black
     18     shapes using the ''Canvas'' property of ''TBGRABitmap''. This is a standard
     19     ''TCanvas'' and when drawing with pure black (''clBlack''), there is no way to know if
     20     something has been drawn or if it is transparent }
     21  clBlackOpaque = TColor($010000);
     22
     23var
    424  //VGA colors
    5   VGABlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
    6   VGAGray: TBGRAPixel = (blue: 128; green: 128; red: 128; alpha: 255);
    7   VGASilver: TBGRAPixel = (blue: 192; green: 192; red: 192; alpha: 255);
    8   VGAWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255);
    9   VGAMaroon: TBGRAPixel = (blue: 0; green: 0; red: 128; alpha: 255);
    10   VGARed: TBGRAPixel = (blue: 0; green: 0; red: 255; alpha: 255);
    11   VGAPurple: TBGRAPixel = (blue: 128; green: 0; red: 128; alpha: 255);
    12   VGAFuchsia: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255);
    13   VGAGreen: TBGRAPixel = (blue: 0; green: 128; red: 0; alpha: 255);
    14   VGALime: TBGRAPixel = (blue: 0; green: 255; red: 0; alpha: 255);
    15   VGAOlive: TBGRAPixel = (blue: 0; green: 128; red: 128; alpha: 255);
    16   VGAYellow: TBGRAPixel = (blue: 0; green: 255; red: 255; alpha: 255);
    17   VGANavy: TBGRAPixel = (blue: 128; green: 0; red: 0; alpha: 255);
    18   VGABlue: TBGRAPixel = (blue: 255; green: 0; red: 0; alpha: 255);
    19   VGATeal: TBGRAPixel = (blue: 128; green: 128; red: 0; alpha: 255);
    20   VGAAqua: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255);
     25  VGABlack,VGAGray,VGASilver,VGAWhite,
     26  VGAMaroon,VGARed,VGAPurple,VGAFuchsia,
     27  VGAGreen,VGALime,VGAOlive,VGAYellow,
     28  VGANavy,VGABlue,VGATeal,VGAAqua: TBGRAPixel;
    2129
    2230  //Red colors
    23   CSSIndianRed: TBGRAPixel = (blue: 92; green: 92; red: 205; alpha: 255);
    24   CSSLightCoral: TBGRAPixel = (blue: 128; green: 128; red: 240; alpha: 255);
    25   CSSSalmon: TBGRAPixel = (blue: 114; green: 128; red: 250; alpha: 255);
    26   CSSDarkSalmon: TBGRAPixel = (blue: 122; green: 150; red: 233; alpha: 255);
    27   CSSRed: TBGRAPixel = (blue: 0; green: 0; red: 255; alpha: 255);
    28   CSSCrimson: TBGRAPixel = (blue: 60; green: 20; red: 220; alpha: 255);
    29   CSSFireBrick: TBGRAPixel = (blue: 34; green: 34; red: 178; alpha: 255);
    30   CSSDarkRed: TBGRAPixel = (blue: 0; green: 0; red: 139; alpha: 255);
    31 
     31  CSSIndianRed,CSSLightCoral,CSSSalmon,CSSDarkSalmon,
     32  CSSRed,CSSCrimson,CSSFireBrick,CSSDarkRed: TBGRAPixel;
    3233  //Pink colors
    33   CSSPink: TBGRAPixel = (blue: 203; green: 192; red: 255; alpha: 255);
    34   CSSLightPink: TBGRAPixel = (blue: 193; green: 182; red: 255; alpha: 255);
    35   CSSHotPink: TBGRAPixel = (blue: 180; green: 105; red: 255; alpha: 255);
    36   CSSDeepPink: TBGRAPixel = (blue: 147; green: 20; red: 255; alpha: 255);
    37   CSSMediumVioletRed: TBGRAPixel = (blue: 133; green: 21; red: 199; alpha: 255);
    38   CSSPaleVioletRed: TBGRAPixel = (blue: 147; green: 112; red: 219; alpha: 255);
    39 
     34  CSSPink,CSSLightPink,CSSHotPink,CSSDeepPink,
     35  CSSMediumVioletRed,CSSPaleVioletRed: TBGRAPixel;
    4036  //Orange colors
    41   CSSLightSalmon: TBGRAPixel = (blue: 122; green: 160; red: 255; alpha: 255);
    42   CSSCoral: TBGRAPixel = (blue: 80; green: 127; red: 255; alpha: 255);
    43   CSSTomato: TBGRAPixel = (blue: 71; green: 99; red: 255; alpha: 255);
    44   CSSOrangeRed: TBGRAPixel = (blue: 0; green: 69; red: 255; alpha: 255);
    45   CSSDarkOrange: TBGRAPixel = (blue: 0; green: 140; red: 255; alpha: 255);
    46   CSSOrange: TBGRAPixel = (blue: 0; green: 165; red: 255; alpha: 255);
    47 
     37  CSSLightSalmon,CSSCoral,CSSTomato,CSSOrangeRed,
     38  CSSDarkOrange,CSSOrange: TBGRAPixel;
    4839  //Yellow colors
    49   CSSGold: TBGRAPixel = (blue: 0; green: 215; red: 255; alpha: 255);
    50   CSSYellow: TBGRAPixel = (blue: 0; green: 255; red: 255; alpha: 255);
    51   CSSLightYellow: TBGRAPixel = (blue: 224; green: 255; red: 255; alpha: 255);
    52   CSSLemonChiffon: TBGRAPixel = (blue: 205; green: 250; red: 255; alpha: 255);
    53   CSSLightGoldenrodYellow: TBGRAPixel = (blue: 210; green: 250; red: 250; alpha: 255);
    54   CSSPapayaWhip: TBGRAPixel = (blue: 213; green: 239; red: 255; alpha: 255);
    55   CSSMoccasin: TBGRAPixel = (blue: 181; green: 228; red: 255; alpha: 255);
    56   CSSPeachPuff: TBGRAPixel = (blue: 185; green: 218; red: 255; alpha: 255);
    57   CSSPaleGoldenrod: TBGRAPixel = (blue: 170; green: 232; red: 238; alpha: 255);
    58   CSSKhaki: TBGRAPixel = (blue: 140; green: 230; red: 240; alpha: 255);
    59   CSSDarkKhaki: TBGRAPixel = (blue: 107; green: 183; red: 189; alpha: 255);
    60 
     40  CSSGold,CSSYellow,CSSLightYellow,CSSLemonChiffon,
     41  CSSLightGoldenrodYellow,CSSPapayaWhip,CSSMoccasin,CSSPeachPuff,
     42  CSSPaleGoldenrod,CSSKhaki,CSSDarkKhaki: TBGRAPixel;
    6143  //Purple colors
    62   CSSLavender: TBGRAPixel = (blue: 250; green: 230; red: 230; alpha: 255);
    63   CSSThistle: TBGRAPixel = (blue: 216; green: 191; red: 216; alpha: 255);
    64   CSSPlum: TBGRAPixel = (blue: 221; green: 160; red: 221; alpha: 255);
    65   CSSViolet: TBGRAPixel = (blue: 238; green: 130; red: 238; alpha: 255);
    66   CSSOrchid: TBGRAPixel = (blue: 214; green: 112; red: 218; alpha: 255);
    67   CSSFuchsia: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255);
    68   CSSMagenta: TBGRAPixel = (blue: 255; green: 0; red: 255; alpha: 255);
    69   CSSMediumOrchid: TBGRAPixel = (blue: 211; green: 85; red: 186; alpha: 255);
    70   CSSMediumPurple: TBGRAPixel = (blue: 219; green: 112; red: 147; alpha: 255);
    71   CSSBlueViolet: TBGRAPixel = (blue: 226; green: 43; red: 138; alpha: 255);
    72   CSSDarkViolet: TBGRAPixel = (blue: 211; green: 0; red: 148; alpha: 255);
    73   CSSDarkOrchid: TBGRAPixel = (blue: 204; green: 50; red: 153; alpha: 255);
    74   CSSDarkMagenta: TBGRAPixel = (blue: 139; green: 0; red: 139; alpha: 255);
    75   CSSPurple: TBGRAPixel = (blue: 128; green: 0; red: 128; alpha: 255);
    76   CSSIndigo: TBGRAPixel = (blue: 130; green: 0; red: 75; alpha: 255);
    77   CSSDarkSlateBlue: TBGRAPixel = (blue: 139; green: 61; red: 72; alpha: 255);
    78   CSSSlateBlue: TBGRAPixel = (blue: 205; green: 90; red: 106; alpha: 255);
    79   CSSMediumSlateBlue: TBGRAPixel = (blue: 238; green: 104; red: 123; alpha: 255);
    80 
     44  CSSLavender,CSSThistle,CSSPlum,CSSViolet,
     45  CSSOrchid,CSSFuchsia,CSSMagenta,CSSMediumOrchid,
     46  CSSMediumPurple,CSSBlueViolet,CSSDarkViolet,CSSDarkOrchid,
     47  CSSDarkMagenta,CSSPurple,CSSIndigo,CSSDarkSlateBlue,
     48  CSSSlateBlue,CSSMediumSlateBlue: TBGRAPixel;
    8149  //Green colors
    82   CSSGreenYellow: TBGRAPixel = (blue: 47; green: 255; red: 173; alpha: 255);
    83   CSSChartreuse: TBGRAPixel = (blue: 0; green: 255; red: 127; alpha: 255);
    84   CSSLawnGreen: TBGRAPixel = (blue: 0; green: 252; red: 124; alpha: 255);
    85   CSSLime: TBGRAPixel = (blue: 0; green: 255; red: 0; alpha: 255);
    86   CSSLimeGreen: TBGRAPixel = (blue: 50; green: 205; red: 50; alpha: 255);
    87   CSSPaleGreen: TBGRAPixel = (blue: 152; green: 251; red: 152; alpha: 255);
    88   CSSLightGreen: TBGRAPixel = (blue: 144; green: 238; red: 144; alpha: 255);
    89   CSSMediumSpringGreen: TBGRAPixel = (blue: 154; green: 250; red: 0; alpha: 255);
    90   CSSSpringGreen: TBGRAPixel = (blue: 127; green: 255; red: 0; alpha: 255);
    91   CSSMediumSeaGreen: TBGRAPixel = (blue: 113; green: 179; red: 60; alpha: 255);
    92   CSSSeaGreen: TBGRAPixel = (blue: 87; green: 139; red: 46; alpha: 255);
    93   CSSForestGreen: TBGRAPixel = (blue: 34; green: 139; red: 34; alpha: 255);
    94   CSSGreen: TBGRAPixel = (blue: 0; green: 128; red: 0; alpha: 255);
    95   CSSDarkGreen: TBGRAPixel = (blue: 0; green: 100; red: 0; alpha: 255);
    96   CSSYellowGreen: TBGRAPixel = (blue: 50; green: 205; red: 154; alpha: 255);
    97   CSSOliveDrab: TBGRAPixel = (blue: 35; green: 142; red: 107; alpha: 255);
    98   CSSOlive: TBGRAPixel = (blue: 0; green: 128; red: 128; alpha: 255);
    99   CSSDarkOliveGreen: TBGRAPixel = (blue: 47; green: 107; red: 85; alpha: 255);
    100   CSSMediumAquamarine: TBGRAPixel = (blue: 170; green: 205; red: 102; alpha: 255);
    101   CSSDarkSeaGreen: TBGRAPixel = (blue: 143; green: 188; red: 143; alpha: 255);
    102   CSSLightSeaGreen: TBGRAPixel = (blue: 170; green: 178; red: 32; alpha: 255);
    103   CSSDarkCyan: TBGRAPixel = (blue: 139; green: 139; red: 0; alpha: 255);
    104   CSSTeal: TBGRAPixel = (blue: 128; green: 128; red: 0; alpha: 255);
    105 
     50  CSSGreenYellow,CSSChartreuse,CSSLawnGreen,CSSLime,
     51  CSSLimeGreen,CSSPaleGreen,CSSLightGreen,CSSMediumSpringGreen,
     52  CSSSpringGreen,CSSMediumSeaGreen,CSSSeaGreen,CSSForestGreen,
     53  CSSGreen,CSSDarkGreen,CSSYellowGreen,CSSOliveDrab,
     54  CSSOlive,CSSDarkOliveGreen,CSSMediumAquamarine,CSSDarkSeaGreen,
     55  CSSLightSeaGreen,CSSDarkCyan,CSSTeal: TBGRAPixel;
    10656  //Blue/Cyan colors
    107   CSSAqua: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255);
    108   CSSCyan: TBGRAPixel = (blue: 255; green: 255; red: 0; alpha: 255);
    109   CSSLightCyan: TBGRAPixel = (blue: 255; green: 255; red: 224; alpha: 255);
    110   CSSPaleTurquoise: TBGRAPixel = (blue: 238; green: 238; red: 175; alpha: 255);
    111   CSSAquamarine: TBGRAPixel = (blue: 212; green: 255; red: 127; alpha: 255);
    112   CSSTurquoise: TBGRAPixel = (blue: 208; green: 224; red: 64; alpha: 255);
    113   CSSMediumTurquoise: TBGRAPixel = (blue: 204; green: 209; red: 72; alpha: 255);
    114   CSSDarkTurquoise: TBGRAPixel = (blue: 209; green: 206; red: 0; alpha: 255);
    115   CSSCadetBlue: TBGRAPixel = (blue: 160; green: 158; red: 95; alpha: 255);
    116   CSSSteelBlue: TBGRAPixel = (blue: 180; green: 130; red: 70; alpha: 255);
    117   CSSLightSteelBlue: TBGRAPixel = (blue: 222; green: 196; red: 176; alpha: 255);
    118   CSSPowderBlue: TBGRAPixel = (blue: 230; green: 224; red: 176; alpha: 255);
    119   CSSLightBlue: TBGRAPixel = (blue: 230; green: 216; red: 173; alpha: 255);
    120   CSSSkyBlue: TBGRAPixel = (blue: 235; green: 206; red: 135; alpha: 255);
    121   CSSLightSkyBlue: TBGRAPixel = (blue: 250; green: 206; red: 135; alpha: 255);
    122   CSSDeepSkyBlue: TBGRAPixel = (blue: 255; green: 191; red: 0; alpha: 255);
    123   CSSDodgerBlue: TBGRAPixel = (blue: 255; green: 144; red: 30; alpha: 255);
    124   CSSCornflowerBlue: TBGRAPixel = (blue: 237; green: 149; red: 100; alpha: 255);
    125   CSSRoyalBlue: TBGRAPixel = (blue: 255; green: 105; red: 65; alpha: 255);
    126   CSSBlue: TBGRAPixel = (blue: 255; green: 0; red: 0; alpha: 255);
    127   CSSMediumBlue: TBGRAPixel = (blue: 205; green: 0; red: 0; alpha: 255);
    128   CSSDarkBlue: TBGRAPixel = (blue: 139; green: 0; red: 0; alpha: 255);
    129   CSSNavy: TBGRAPixel = (blue: 128; green: 0; red: 0; alpha: 255);
    130   CSSMidnightBlue: TBGRAPixel = (blue: 112; green: 25; red: 25; alpha: 255);
    131 
     57  CSSAqua,CSSCyan,CSSLightCyan,CSSPaleTurquoise,
     58  CSSAquamarine,CSSTurquoise,CSSMediumTurquoise,CSSDarkTurquoise,
     59  CSSCadetBlue,CSSSteelBlue,CSSLightSteelBlue,CSSPowderBlue,
     60  CSSLightBlue,CSSSkyBlue,CSSLightSkyBlue,CSSDeepSkyBlue,
     61  CSSDodgerBlue,CSSCornflowerBlue,CSSRoyalBlue,CSSBlue,
     62  CSSMediumBlue,CSSDarkBlue,CSSNavy,CSSMidnightBlue: TBGRAPixel;
    13263  //Brown colors
    133   CSSCornsilk: TBGRAPixel = (blue: 220; green: 248; red: 255; alpha: 255);
    134   CSSBlanchedAlmond: TBGRAPixel = (blue: 205; green: 235; red: 255; alpha: 255);
    135   CSSBisque: TBGRAPixel = (blue: 196; green: 228; red: 255; alpha: 255);
    136   CSSNavajoWhite: TBGRAPixel = (blue: 173; green: 222; red: 255; alpha: 255);
    137   CSSWheat: TBGRAPixel = (blue: 179; green: 222; red: 245; alpha: 255);
    138   CSSBurlyWood: TBGRAPixel = (blue: 135; green: 184; red: 222; alpha: 255);
    139   CSSTan: TBGRAPixel = (blue: 140; green: 180; red: 210; alpha: 255);
    140   CSSRosyBrown: TBGRAPixel = (blue: 143; green: 143; red: 188; alpha: 255);
    141   CSSSandyBrown: TBGRAPixel = (blue: 96; green: 164; red: 244; alpha: 255);
    142   CSSGoldenrod: TBGRAPixel = (blue: 32; green: 165; red: 218; alpha: 255);
    143   CSSDarkGoldenrod: TBGRAPixel = (blue: 11; green: 134; red: 184; alpha: 255);
    144   CSSPeru: TBGRAPixel = (blue: 63; green: 133; red: 205; alpha: 255);
    145   CSSChocolate: TBGRAPixel = (blue: 30; green: 105; red: 210; alpha: 255);
    146   CSSSaddleBrown: TBGRAPixel = (blue: 19; green: 69; red: 139; alpha: 255);
    147   CSSSienna: TBGRAPixel = (blue: 45; green: 82; red: 160; alpha: 255);
    148   CSSBrown: TBGRAPixel = (blue: 42; green: 42; red: 165; alpha: 255);
    149   CSSMaroon: TBGRAPixel = (blue: 0; green: 0; red: 128; alpha: 255);
    150 
     64  CSSCornsilk, CSSBlanchedAlmond, CSSBisque, CSSNavajoWhite,
     65  CSSWheat, CSSBurlyWood, CSSTan, CSSRosyBrown,
     66  CSSSandyBrown, CSSGoldenrod, CSSDarkGoldenrod, CSSPeru,
     67  CSSChocolate, CSSSaddleBrown, CSSSienna, CSSBrown,
     68  CSSMaroon: TBGRAPixel;
    15169  //White colors
    152   CSSWhite: TBGRAPixel = (blue: 255; green: 255; red: 255; alpha: 255);
    153   CSSSnow: TBGRAPixel = (blue: 250; green: 250; red: 255; alpha: 255);
    154   CSSHoneydew: TBGRAPixel = (blue: 240; green: 255; red: 250; alpha: 255);
    155   CSSMintCream: TBGRAPixel = (blue: 250; green: 255; red: 245; alpha: 255);
    156   CSSAzure: TBGRAPixel = (blue: 255; green: 255; red: 240; alpha: 255);
    157   CSSAliceBlue: TBGRAPixel = (blue: 255; green: 248; red: 240; alpha: 255);
    158   CSSGhostWhite: TBGRAPixel = (blue: 255; green: 248; red: 248; alpha: 255);
    159   CSSWhiteSmoke: TBGRAPixel = (blue: 245; green: 245; red: 245; alpha: 255);
    160   CSSSeashell: TBGRAPixel = (blue: 255; green: 245; red: 238; alpha: 255);
    161   CSSBeige: TBGRAPixel = (blue: 220; green: 245; red: 245; alpha: 255);
    162   CSSOldLace: TBGRAPixel = (blue: 230; green: 245; red: 253; alpha: 255);
    163   CSSFloralWhite: TBGRAPixel = (blue: 240; green: 250; red: 255; alpha: 255);
    164   CSSIvory: TBGRAPixel = (blue: 240; green: 255; red: 255; alpha: 255);
    165   CSSAntiqueWhite: TBGRAPixel = (blue: 215; green: 235; red: 250; alpha: 255);
    166   CSSLinen: TBGRAPixel = (blue: 230; green: 240; red: 250; alpha: 255);
    167   CSSLavenderBlush: TBGRAPixel = (blue: 245; green: 240; red: 255; alpha: 255);
    168   CSSMistyRose: TBGRAPixel = (blue: 255; green: 228; red: 255; alpha: 255);
    169 
     70  CSSWhite, CSSSnow, CSSHoneydew, CSSMintCream,
     71  CSSAzure, CSSAliceBlue, CSSGhostWhite, CSSWhiteSmoke,
     72  CSSSeashell, CSSBeige, CSSOldLace, CSSFloralWhite,
     73  CSSIvory, CSSAntiqueWhite, CSSLinen, CSSLavenderBlush,
     74  CSSMistyRose: TBGRAPixel;
    17075  //Gray colors
    171   CSSGainsboro: TBGRAPixel = (blue: 220; green: 220; red: 220; alpha: 255);
    172   CSSLightGray: TBGRAPixel = (blue: 211; green: 211; red: 211; alpha: 255);
    173   CSSSilver: TBGRAPixel = (blue: 192; green: 192; red: 192; alpha: 255);
    174   CSSDarkGray: TBGRAPixel = (blue: 169; green: 169; red: 169; alpha: 255);
    175   CSSGray: TBGRAPixel = (blue: 128; green: 128; red: 128; alpha: 255);
    176   CSSDimGray: TBGRAPixel = (blue: 105; green: 105; red: 105; alpha: 255);
    177   CSSLightSlateGray: TBGRAPixel = (blue: 153; green: 136; red: 119; alpha: 255);
    178   CSSSlateGray: TBGRAPixel = (blue: 144; green: 128; red: 112; alpha: 255);
    179   CSSDarkSlateGray: TBGRAPixel = (blue: 79; green: 79; red: 47; alpha: 255);
    180   CSSBlack: TBGRAPixel = (blue: 0; green: 0; red: 0; alpha: 255);
     76  CSSGainsboro, CSSLightGray, CSSSilver, CSSDarkGray,
     77  CSSGray, CSSDimGray, CSSLightSlateGray, CSSSlateGray,
     78  CSSDarkSlateGray, CSSBlack: TBGRAPixel;
     79
     80type
     81  TBGRAColorDefinition = record
     82    Name: string;
     83    Color: TBGRAPixel;
     84  end;
     85
     86  { TBGRAColorList }
     87  {* Contains a fixed list of colors }
     88  TBGRAColorList = class
     89  protected
     90    FFinished: boolean;
     91    FNbColors: integer;
     92    FColors: array of TBGRAColorDefinition;
     93    function GetByIndex(Index: integer): TBGRAPixel;
     94    function GetByName(Name: string): TBGRAPixel;
     95    function GetName(Index: integer): string;
     96    procedure Add(Name: string; out Color: TBGRAPixel; red,green,blue: byte); overload;
     97  public
     98    {** Creates an empty color list }
     99    constructor Create;
     100    {** Add a color to the list }
     101    procedure Add(Name: string; const Color: TBGRAPixel);
     102    {** Ends the color list and prevents further modifications }
     103    procedure Finished;
     104    {** Returns the index of a color with a given name }
     105    function IndexOf(Name: string): integer;
     106    {** Returns the index of a color. Colors are considered to match if
     107        the difference is less than or equal to ''AMaxDiff'' }
     108    function IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
     109
     110    {** Gets the color associated with a color name }
     111    property ByName[Name: string]: TBGRAPixel read GetByName;
     112    {** Gets the color at the specified index }
     113    property ByIndex[Index: integer]: TBGRAPixel read GetByIndex; default;
     114    {** Gets the name of the color at the specified index }
     115    property Name[Index: integer]: string read GetName;
     116    {** Gets the number of colors }
     117    property Count: integer read FNbColors;
     118  end;
     119
     120var
     121  {* List of VGA colors:
     122   * [#000000] Black, [#808080] Gray, [#C0C0C0] Silver, [#FFFFFF] White,
     123   * [#800000] Maroon, [#FF0000] Red,  [#800080] Purple, [#FF00FF] Fuchsia,
     124   * [#008000] Green,  [#00FF00] Lime, [#808000] Olive,  [#FFFF00] Yellow,
     125   * [#000080] Navy,   [#0000FF] Blue, [#008080] Teal,   [#00FFFF] Aqua.
     126   * Shortcut constants are provided: [#000000] ''VGABlack'', [#808080] ''VGAGray''... }
     127  VGAColors: TBGRAColorList;
     128  {* List of [http://www.w3schools.com/cssref/css_colornames.asp web colors].
     129  Shortcut constants are provided: [#000000] ''CSSBlack'', [#FF0000] ''CSSRed''... }
     130  CSSColors: TBGRAColorList;
     131
     132{------------------- string conversion ------------------------}
     133
     134{* Converts a ''TBGRAPixel'' value into a string, using color names provided in ''AColorList'', and
     135   considering that a color matches in the color list if its difference is within ''AMaxDiff'' }
     136function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
     137{* Converts a fully defined string into a ''TBGRAPixel'' value. Color names from ''VGAColors'' and ''CSSColors''
     138   are used if there is an exact match }
     139function StrToBGRA(str: string): TBGRAPixel;
     140{* Converts a string into a ''TBGRAPixel'' value. If the value is not fully defined or that
     141   there is an error, ''DefaultColor'' is returned.
     142   Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. }
     143function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;
     144{* Converts a string into a ''TBGRAPixel'' value. If the value is not fully defined, missing channels (expressed with '?')
     145   are filled with fallbackValues. You can check if there was an error with the provided boolean.
     146   Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. }
     147function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out error: boolean): TBGRAPixel;
     148{* Converts a string into a ''TBGRAPixel'' value into ''parsedValue''. ''parsedValue'' is not changed if
     149   some channels are missing (expressed with '?'). You can check if there was an error with the provided boolean.
     150   Color names from ''VGAColors'' and ''CSSColors'' are used if there is an exact match. }
     151procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
    181152{$ENDIF}
    182 {$IFDEF INCLUDE_COLOR_LIST}
    183 {$UNDEF INCLUDE_COLOR_LIST}
     153
     154{$IFDEF INCLUDE_IMPLEMENTATION}
     155{$UNDEF INCLUDE_IMPLEMENTATION}
     156{ TBGRAColorList }
     157
     158function TBGRAColorList.GetByIndex(Index: integer): TBGRAPixel;
     159begin
     160  if (Index < 0) or (Index >= FNbColors) then
     161    result := BGRAPixelTransparent
     162  else
     163    result := FColors[Index].Color;
     164end;
     165
     166function TBGRAColorList.GetByName(Name: string): TBGRAPixel;
     167var i: integer;
     168begin
     169  i := IndexOf(Name);
     170  if i = -1 then
     171    result := BGRAPixelTransparent
     172  else
     173    result := FColors[i].Color;
     174end;
     175
     176function TBGRAColorList.GetName(Index: integer): string;
     177begin
     178  if (Index < 0) or (Index >= FNbColors) then
     179    result := ''
     180  else
     181    result := FColors[Index].Name;
     182end;
     183
     184procedure TBGRAColorList.Add(Name: string; out Color: TBGRAPixel; red, green,
     185  blue: byte);
     186begin
     187  Color := BGRA(red,green,blue);
     188  Add(Name,Color);
     189end;
     190
     191constructor TBGRAColorList.Create;
     192begin
     193  FNbColors:= 0;
     194  FColors := nil;
     195  FFinished:= false;
     196end;
     197
     198procedure TBGRAColorList.Add(Name: string; const Color: TBGRAPixel);
     199begin
     200  if FFinished then
     201    raise Exception.Create('This list is already finished');
     202  if length(FColors) = FNbColors then
     203    SetLength(FColors, FNbColors*2+1);
     204  FColors[FNbColors].Name := Name;
     205  FColors[FNbColors].Color := Color;
     206  inc(FNbColors);
     207end;
     208
     209procedure TBGRAColorList.Finished;
     210begin
     211  if FFinished then exit;
     212  FFinished := true;
     213  SetLength(FColors, FNbColors);
     214end;
     215
     216function TBGRAColorList.IndexOf(Name: string): integer;
     217var i: integer;
     218begin
     219  for i := 0 to FNbColors-1 do
     220    if CompareText(Name, FColors[i].Name) = 0 then
     221    begin
     222      result := i;
     223      exit;
     224    end;
     225  result := -1;
     226end;
     227
     228function TBGRAColorList.IndexOfColor(const AColor: TBGRAPixel; AMaxDiff: Word = 0): integer;
     229var i: integer;
     230  MinDiff,CurDiff: Word;
     231begin
     232  if AMaxDiff = 0 then
     233  begin
     234    for i := 0 to FNbColors-1 do
     235      if AColor = FColors[i].Color then
     236      begin
     237        result := i;
     238        exit;
     239      end;
     240    result := -1;
     241  end else
     242  begin
     243    MinDiff := AMaxDiff;
     244    result := -1;
     245    for i := 0 to FNbColors-1 do
     246    begin
     247      CurDiff := BGRAWordDiff(AColor,FColors[i].Color);
     248      if CurDiff <= MinDiff then
     249      begin
     250        result := i;
     251        MinDiff := CurDiff;
     252        if MinDiff = 0 then exit;
     253      end;
     254    end;
     255  end;
     256end;
     257
     258{------------------- string conversion ---------------------------------}
     259
     260{ Write a color in hexadecimal format RRGGBBAA or using the name in a color list }
     261function BGRAToStr(c: TBGRAPixel; AColorList: TBGRAColorList = nil; AMaxDiff: Word= 0): string;
     262var idx: integer;
     263begin
     264  if Assigned(AColorList) then
     265  begin
     266    idx := AColorList.IndexOfColor(c, AMaxDiff);
     267    if idx<> -1 then
     268    begin
     269      result := AColorList.Name[idx];
     270      exit;
     271    end;
     272  end;
     273  result := IntToHex(c.red,2)+IntToHex(c.green,2)+IntToHex(c.Blue,2)+IntToHex(c.Alpha,2);
     274end;
     275
     276type
     277    arrayOfString = array of string;
     278
     279function SimpleParseFuncParam(str: string; var flagError: boolean): arrayOfString;
     280var idxOpen,start,cur: integer;
     281begin
     282    result := nil;
     283    idxOpen := pos('(',str);
     284    if idxOpen = 0 then
     285    begin
     286      start := 1;
     287      //find first space
     288      while (start <= length(str)) and (str[start]<>' ') do inc(start);
     289    end else
     290      start := idxOpen+1;
     291    cur := start;
     292    while cur <= length(str) do
     293    begin
     294       if str[cur] in[',',')'] then
     295       begin
     296         setlength(result,length(result)+1);
     297         result[high(result)] := trim(copy(str,start,cur-start));
     298         start := cur+1;
     299         if str[cur] = ')' then exit;
     300       end;
     301       inc(cur);
     302    end;
     303    if idxOpen <> 0 then flagError := true; //should exit on ')'
     304    if start <= length(str) then
     305    begin
     306      setlength(result,length(result)+1);
     307      result[high(result)] := copy(str,start,length(str)-start+1);
     308    end;
     309end;
     310
     311function ParseColorValue(str: string; var flagError: boolean): byte;
     312var pourcent,unclipped,{%H-}errPos: integer;
     313begin
     314  if str = '' then result := 0 else
     315  begin
     316    if str[length(str)]='%' then
     317    begin
     318      val(copy(str,1,length(str)-1),pourcent,errPos);
     319      if errPos <> 0 then flagError := true;
     320      if pourcent < 0 then result := 0 else
     321      if pourcent > 100 then result := 255 else
     322        result := pourcent*255 div 100;
     323    end else
     324    begin
     325      val(str,unclipped,errPos);
     326      if errPos <> 0 then flagError := true;
     327      if unclipped < 0 then result := 0 else
     328      if unclipped > 255 then result := 255 else
     329        result := unclipped;
     330    end;
     331  end;
     332end;
     333
     334//this function returns the parsed value only if it contains no error nor missing values, otherwise
     335//it returns BGRAPixelTransparent
     336function StrToBGRA(str: string): TBGRAPixel;
     337var missingValues, error: boolean;
     338begin
     339  result := BGRABlack;
     340  TryStrToBGRA(str, result, missingValues, error);
     341  if missingValues or error then result := BGRAPixelTransparent;
     342end;
     343
     344//this function changes the content of parsedValue depending on available and parsable information.
     345//set parsedValue to the fallback values before calling this function.
     346//missing values are expressed by empty string or by '?', for example 'rgb(255,?,?,?)' will change only the red value.
     347//note that if alpha is not expressed by the string format, it will be opaque. So 'rgb(255,?,?)' will change the red value and the alpha value.
     348//the last parameter of rgba() is a floating point number where 1 is opaque and 0 is transparent.
     349procedure TryStrToBGRA(str: string; var parsedValue: TBGRAPixel; out missingValues: boolean; out error: boolean);
     350var errPos: integer;
     351    values: array of string;
     352    alphaF: single;
     353    idx: integer;
     354begin
     355  str := Trim(str);
     356  error := false;
     357  if (str = '') or (str = '?') then
     358  begin
     359    missingValues := true;
     360    exit;
     361  end else
     362    missingValues := false;
     363  str := StringReplace(lowerCase(str),'grey','gray',[]);
     364
     365  //VGA color names
     366  idx := VGAColors.IndexOf(str);
     367  if idx <> -1 then
     368  begin
     369    parsedValue := VGAColors[idx];
     370    exit;
     371  end;
     372  if str='transparent' then parsedValue := BGRAPixelTransparent else
     373  begin
     374    //check CSS color
     375    idx := CSSColors.IndexOf(str);
     376    if idx <> -1 then
     377    begin
     378      parsedValue := CSSColors[idx];
     379      exit;
     380    end;
     381
     382    //CSS RGB notation
     383    if (copy(str,1,4)='rgb(') or (copy(str,1,5)='rgba(') or
     384      (copy(str,1,4)='rgb ') or (copy(str,1,5)='rgba ') then
     385    begin
     386      values := SimpleParseFuncParam(str,error);
     387      if (length(values)=3) or (length(values)=4) then
     388      begin
     389        if (values[0] <> '') and (values[0] <> '?') then
     390           parsedValue.red := ParseColorValue(values[0], error)
     391        else
     392           missingValues := true;
     393        if (values[1] <> '') and (values[1] <> '?') then
     394           parsedValue.green := ParseColorValue(values[1], error)
     395        else
     396           missingValues := true;
     397        if (values[2] <> '') and (values[2] <> '?') then
     398           parsedValue.blue := ParseColorValue(values[2], error)
     399        else
     400           missingValues := true;
     401        if length(values)=4 then
     402        begin
     403          if (values[3] <> '') and (values[3] <> '?') then
     404          begin
     405            val(values[3],alphaF,errPos);
     406            if errPos <> 0 then
     407            begin
     408               parsedValue.alpha := 255;
     409               error := true;
     410            end
     411            else
     412            begin
     413              if alphaF < 0 then
     414                parsedValue.alpha := 0 else
     415              if alphaF > 1 then
     416                parsedValue.alpha := 255
     417              else
     418                parsedValue.alpha := round(alphaF*255);
     419            end;
     420          end else
     421            missingValues := true;
     422        end else
     423          parsedValue.alpha := 255;
     424      end else
     425        error := true;
     426      exit;
     427    end;
     428
     429    //remove HTML notation header
     430    if str[1]='#' then delete(str,1,1);
     431
     432    //add alpha if missing (if you want an undefined alpha use '??' or '?')
     433    if length(str)=6 then str += 'FF';
     434    if length(str)=3 then str += 'F';
     435
     436    //hex notation
     437    if length(str)=8 then
     438    begin
     439      if copy(str,1,2) <> '??' then
     440      begin
     441        val('$'+copy(str,1,2),parsedValue.red,errPos);
     442        if errPos <> 0 then error := true;
     443      end else missingValues := true;
     444      if copy(str,3,2) <> '??' then
     445      begin
     446        val('$'+copy(str,3,2),parsedValue.green,errPos);
     447        if errPos <> 0 then error := true;
     448      end else missingValues := true;
     449      if copy(str,5,2) <> '??' then
     450      begin
     451        val('$'+copy(str,5,2),parsedValue.blue,errPos);
     452        if errPos <> 0 then error := true;
     453      end else missingValues := true;
     454      if copy(str,7,2) <> '??' then
     455      begin
     456        val('$'+copy(str,7,2),parsedValue.alpha,errPos);
     457        if errPos <> 0 then
     458        begin
     459          error := true;
     460          parsedValue.alpha := 255;
     461        end;
     462      end else missingValues := true;
     463    end else
     464    if length(str)=4 then
     465    begin
     466      if str[1] <> '?' then
     467      begin
     468        val('$'+str[1],parsedValue.red,errPos);
     469        if errPos <> 0 then error := true;
     470        parsedValue.red *= $11;
     471      end else missingValues := true;
     472      if str[2] <> '?' then
     473      begin
     474        val('$'+str[2],parsedValue.green,errPos);
     475        if errPos <> 0 then error := true;
     476        parsedValue.green *= $11;
     477      end else missingValues := true;
     478      if str[3] <> '?' then
     479      begin
     480        val('$'+str[3],parsedValue.blue,errPos);
     481        if errPos <> 0 then error := true;
     482        parsedValue.blue *= $11;
     483      end else missingValues := true;
     484      if str[4] <> '?' then
     485      begin
     486        val('$'+str[4],parsedValue.alpha,errPos);
     487        if errPos <> 0 then
     488        begin
     489          error := true;
     490          parsedValue.alpha := 255;
     491        end else
     492          parsedValue.alpha *= $11;
     493      end else missingValues := true;
     494    end else
     495      error := true; //string format not recognised
     496  end;
     497
     498end;
     499
     500//this function returns the values that can be read from the string, otherwise
     501//it fills the gaps with the fallback values. The error boolean is True only
     502//if there was invalid values, it is not set to True if there was missing values.
     503function PartialStrToBGRA(str: string; const fallbackValues: TBGRAPixel; out
     504  error: boolean): TBGRAPixel;
     505var missingValues: boolean;
     506begin
     507  result := fallbackValues;
     508  TryStrToBGRA(str, result, missingValues, error);
     509end;
     510
     511{ Read a color, for example in hexadecimal format RRGGBB(AA) or RGB(A). Partial colors are not accepted by this function. }
     512function StrToBGRA(str: string; const DefaultColor: TBGRAPixel): TBGRAPixel;
     513var missingValues, error: boolean;
     514begin
     515  result := BGRABlack;
     516  TryStrToBGRA(str, result, missingValues, error);
     517  if missingValues or error then result := DefaultColor;
     518end;
     519
     520function BlueGreenRedToBGRA(blue,green,red: byte): TBGRAPixel;
     521begin
     522  result := BGRA(red,green,blue);
     523end;
     524
     525{$ENDIF}
     526
     527{$IFDEF INCLUDE_INIT}
     528{$UNDEF INCLUDE_INIT}
     529  BGRAPixelTransparent := BGRA(0,0,0,0);
     530  BGRAWhite := BGRA(255,255,255);
     531  BGRABlack := BGRA(0,0,0);
     532
    184533  VGAColors := TBGRAColorList.Create;
    185   VGAColors.Add('Black',VGABlack);
    186   VGAColors.Add('Gray',VGAGray);
    187   VGAColors.Add('Silver',VGASilver);
    188   VGAColors.Add('White',VGAWhite);
    189   VGAColors.Add('Maroon',VGAMaroon);
    190   VGAColors.Add('Red',VGARed);
    191   VGAColors.Add('Purple',VGAPurple);
    192   VGAColors.Add('Fuchsia',VGAFuchsia);
    193   VGAColors.Add('Green',VGAGreen);
    194   VGAColors.Add('Lime',VGALime);
    195   VGAColors.Add('Olive',VGAOlive);
    196   VGAColors.Add('Yellow',VGAYellow);
    197   VGAColors.Add('Navy',VGANavy);
    198   VGAColors.Add('Blue',VGABlue);
    199   VGAColors.Add('Teal',VGATeal);
    200   VGAColors.Add('Aqua',VGAAqua);
     534  VGAColors.Add('Black',VGABlack,0,0,0);
     535  VGAColors.Add('Gray',VGAGray,128,128,128);
     536  VGAColors.Add('Silver',VGASilver,192,192,192);
     537  VGAColors.Add('White',VGAWhite,255,255,255);
     538  VGAColors.Add('Maroon',VGAMaroon,128,0,0);
     539  VGAColors.Add('Red',VGARed,255,0,0);
     540  VGAColors.Add('Purple',VGAPurple,128,0,128);
     541  VGAColors.Add('Fuchsia',VGAFuchsia,255,0,255);
     542  VGAColors.Add('Green',VGAGreen,0,128,0);
     543  VGAColors.Add('Lime',VGALime,0,255,0);
     544  VGAColors.Add('Olive',VGAOlive,128,128,0);
     545  VGAColors.Add('Yellow',VGAYellow,255,255,0);
     546  VGAColors.Add('Navy',VGANavy,0,0,128);
     547  VGAColors.Add('Blue',VGABlue,0,0,255);
     548  VGAColors.Add('Teal',VGATeal,0,128,128);
     549  VGAColors.Add('Aqua',VGAAqua,0,255,255);
    201550  VGAColors.Finished;
     551
     552  //Red colors
     553  CSSIndianRed:= BlueGreenRedToBGRA(92, 92, 205);
     554  CSSLightCoral:= BlueGreenRedToBGRA(128, 128, 240);
     555  CSSSalmon:= BlueGreenRedToBGRA(114, 128, 250);
     556  CSSDarkSalmon:= BlueGreenRedToBGRA(122, 150, 233);
     557  CSSRed:= BlueGreenRedToBGRA(0, 0, 255);
     558  CSSCrimson:= BlueGreenRedToBGRA(60, 20, 220);
     559  CSSFireBrick:= BlueGreenRedToBGRA(34, 34, 178);
     560  CSSDarkRed:= BlueGreenRedToBGRA(0, 0, 139);
     561
     562  //Pink colors
     563  CSSPink:= BlueGreenRedToBGRA(203, 192, 255);
     564  CSSLightPink:= BlueGreenRedToBGRA(193, 182, 255);
     565  CSSHotPink:= BlueGreenRedToBGRA(180, 105, 255);
     566  CSSDeepPink:= BlueGreenRedToBGRA(147, 20, 255);
     567  CSSMediumVioletRed:= BlueGreenRedToBGRA(133, 21, 199);
     568  CSSPaleVioletRed:= BlueGreenRedToBGRA(147, 112, 219);
     569
     570  //Orange colors
     571  CSSLightSalmon:= BlueGreenRedToBGRA(122, 160, 255);
     572  CSSCoral:= BlueGreenRedToBGRA(80, 127, 255);
     573  CSSTomato:= BlueGreenRedToBGRA(71, 99, 255);
     574  CSSOrangeRed:= BlueGreenRedToBGRA(0, 69, 255);
     575  CSSDarkOrange:= BlueGreenRedToBGRA(0, 140, 255);
     576  CSSOrange:= BlueGreenRedToBGRA(0, 165, 255);
     577
     578  //Yellow colors
     579  CSSGold:= BlueGreenRedToBGRA(0, 215, 255);
     580  CSSYellow:= BlueGreenRedToBGRA(0, 255, 255);
     581  CSSLightYellow:= BlueGreenRedToBGRA(224, 255, 255);
     582  CSSLemonChiffon:= BlueGreenRedToBGRA(205, 250, 255);
     583  CSSLightGoldenrodYellow:= BlueGreenRedToBGRA(210, 250, 250);
     584  CSSPapayaWhip:= BlueGreenRedToBGRA(213, 239, 255);
     585  CSSMoccasin:= BlueGreenRedToBGRA(181, 228, 255);
     586  CSSPeachPuff:= BlueGreenRedToBGRA(185, 218, 255);
     587  CSSPaleGoldenrod:= BlueGreenRedToBGRA(170, 232, 238);
     588  CSSKhaki:= BlueGreenRedToBGRA(140, 230, 240);
     589  CSSDarkKhaki:= BlueGreenRedToBGRA(107, 183, 189);
     590
     591  //Purple colors
     592  CSSLavender:= BlueGreenRedToBGRA(250, 230, 230);
     593  CSSThistle:= BlueGreenRedToBGRA(216, 191, 216);
     594  CSSPlum:= BlueGreenRedToBGRA(221, 160, 221);
     595  CSSViolet:= BlueGreenRedToBGRA(238, 130, 238);
     596  CSSOrchid:= BlueGreenRedToBGRA(214, 112, 218);
     597  CSSFuchsia:= BlueGreenRedToBGRA(255, 0, 255);
     598  CSSMagenta:= BlueGreenRedToBGRA(255, 0, 255);
     599  CSSMediumOrchid:= BlueGreenRedToBGRA(211, 85, 186);
     600  CSSMediumPurple:= BlueGreenRedToBGRA(219, 112, 147);
     601  CSSBlueViolet:= BlueGreenRedToBGRA(226, 43, 138);
     602  CSSDarkViolet:= BlueGreenRedToBGRA(211, 0, 148);
     603  CSSDarkOrchid:= BlueGreenRedToBGRA(204, 50, 153);
     604  CSSDarkMagenta:= BlueGreenRedToBGRA(139, 0, 139);
     605  CSSPurple:= BlueGreenRedToBGRA(128, 0, 128);
     606  CSSIndigo:= BlueGreenRedToBGRA(130, 0, 75);
     607  CSSDarkSlateBlue:= BlueGreenRedToBGRA(139, 61, 72);
     608  CSSSlateBlue:= BlueGreenRedToBGRA(205, 90, 106);
     609  CSSMediumSlateBlue:= BlueGreenRedToBGRA(238, 104, 123);
     610
     611  //Green colors
     612  CSSGreenYellow:= BlueGreenRedToBGRA(47, 255, 173);
     613  CSSChartreuse:= BlueGreenRedToBGRA(0, 255, 127);
     614  CSSLawnGreen:= BlueGreenRedToBGRA(0, 252, 124);
     615  CSSLime:= BlueGreenRedToBGRA(0, 255, 0);
     616  CSSLimeGreen:= BlueGreenRedToBGRA(50, 205, 50);
     617  CSSPaleGreen:= BlueGreenRedToBGRA(152, 251, 152);
     618  CSSLightGreen:= BlueGreenRedToBGRA(144, 238, 144);
     619  CSSMediumSpringGreen:= BlueGreenRedToBGRA(154, 250, 0);
     620  CSSSpringGreen:= BlueGreenRedToBGRA(127, 255, 0);
     621  CSSMediumSeaGreen:= BlueGreenRedToBGRA(113, 179, 60);
     622  CSSSeaGreen:= BlueGreenRedToBGRA(87, 139, 46);
     623  CSSForestGreen:= BlueGreenRedToBGRA(34, 139, 34);
     624  CSSGreen:= BlueGreenRedToBGRA(0, 128, 0);
     625  CSSDarkGreen:= BlueGreenRedToBGRA(0, 100, 0);
     626  CSSYellowGreen:= BlueGreenRedToBGRA(50, 205, 154);
     627  CSSOliveDrab:= BlueGreenRedToBGRA(35, 142, 107);
     628  CSSOlive:= BlueGreenRedToBGRA(0, 128, 128);
     629  CSSDarkOliveGreen:= BlueGreenRedToBGRA(47, 107, 85);
     630  CSSMediumAquamarine:= BlueGreenRedToBGRA(170, 205, 102);
     631  CSSDarkSeaGreen:= BlueGreenRedToBGRA(143, 188, 143);
     632  CSSLightSeaGreen:= BlueGreenRedToBGRA(170, 178, 32);
     633  CSSDarkCyan:= BlueGreenRedToBGRA(139, 139, 0);
     634  CSSTeal:= BlueGreenRedToBGRA(128, 128, 0);
     635
     636  //Blue/Cyan colors
     637  CSSAqua:= BlueGreenRedToBGRA(255, 255, 0);
     638  CSSCyan:= BlueGreenRedToBGRA(255, 255, 0);
     639  CSSLightCyan:= BlueGreenRedToBGRA(255, 255, 224);
     640  CSSPaleTurquoise:= BlueGreenRedToBGRA(238, 238, 175);
     641  CSSAquamarine:= BlueGreenRedToBGRA(212, 255, 127);
     642  CSSTurquoise:= BlueGreenRedToBGRA(208, 224, 64);
     643  CSSMediumTurquoise:= BlueGreenRedToBGRA(204, 209, 72);
     644  CSSDarkTurquoise:= BlueGreenRedToBGRA(209, 206, 0);
     645  CSSCadetBlue:= BlueGreenRedToBGRA(160, 158, 95);
     646  CSSSteelBlue:= BlueGreenRedToBGRA(180, 130, 70);
     647  CSSLightSteelBlue:= BlueGreenRedToBGRA(222, 196, 176);
     648  CSSPowderBlue:= BlueGreenRedToBGRA(230, 224, 176);
     649  CSSLightBlue:= BlueGreenRedToBGRA(230, 216, 173);
     650  CSSSkyBlue:= BlueGreenRedToBGRA(235, 206, 135);
     651  CSSLightSkyBlue:= BlueGreenRedToBGRA(250, 206, 135);
     652  CSSDeepSkyBlue:= BlueGreenRedToBGRA(255, 191, 0);
     653  CSSDodgerBlue:= BlueGreenRedToBGRA(255, 144, 30);
     654  CSSCornflowerBlue:= BlueGreenRedToBGRA(237, 149, 100);
     655  CSSRoyalBlue:= BlueGreenRedToBGRA(255, 105, 65);
     656  CSSBlue:= BlueGreenRedToBGRA(255, 0, 0);
     657  CSSMediumBlue:= BlueGreenRedToBGRA(205, 0, 0);
     658  CSSDarkBlue:= BlueGreenRedToBGRA(139, 0, 0);
     659  CSSNavy:= BlueGreenRedToBGRA(128, 0, 0);
     660  CSSMidnightBlue:= BlueGreenRedToBGRA(112, 25, 25);
     661
     662  //Brown colors
     663  CSSCornsilk:= BlueGreenRedToBGRA(220, 248, 255);
     664  CSSBlanchedAlmond:= BlueGreenRedToBGRA(205, 235, 255);
     665  CSSBisque:= BlueGreenRedToBGRA(196, 228, 255);
     666  CSSNavajoWhite:= BlueGreenRedToBGRA(173, 222, 255);
     667  CSSWheat:= BlueGreenRedToBGRA(179, 222, 245);
     668  CSSBurlyWood:= BlueGreenRedToBGRA(135, 184, 222);
     669  CSSTan:= BlueGreenRedToBGRA(140, 180, 210);
     670  CSSRosyBrown:= BlueGreenRedToBGRA(143, 143, 188);
     671  CSSSandyBrown:= BlueGreenRedToBGRA(96, 164, 244);
     672  CSSGoldenrod:= BlueGreenRedToBGRA(32, 165, 218);
     673  CSSDarkGoldenrod:= BlueGreenRedToBGRA(11, 134, 184);
     674  CSSPeru:= BlueGreenRedToBGRA(63, 133, 205);
     675  CSSChocolate:= BlueGreenRedToBGRA(30, 105, 210);
     676  CSSSaddleBrown:= BlueGreenRedToBGRA(19, 69, 139);
     677  CSSSienna:= BlueGreenRedToBGRA(45, 82, 160);
     678  CSSBrown:= BlueGreenRedToBGRA(42, 42, 165);
     679  CSSMaroon:= BlueGreenRedToBGRA(0, 0, 128);
     680
     681  //White colors
     682  CSSWhite:= BlueGreenRedToBGRA(255, 255, 255);
     683  CSSSnow:= BlueGreenRedToBGRA(250, 250, 255);
     684  CSSHoneydew:= BlueGreenRedToBGRA(240, 255, 250);
     685  CSSMintCream:= BlueGreenRedToBGRA(250, 255, 245);
     686  CSSAzure:= BlueGreenRedToBGRA(255, 255, 240);
     687  CSSAliceBlue:= BlueGreenRedToBGRA(255, 248, 240);
     688  CSSGhostWhite:= BlueGreenRedToBGRA(255, 248, 248);
     689  CSSWhiteSmoke:= BlueGreenRedToBGRA(245, 245, 245);
     690  CSSSeashell:= BlueGreenRedToBGRA(255, 245, 238);
     691  CSSBeige:= BlueGreenRedToBGRA(220, 245, 245);
     692  CSSOldLace:= BlueGreenRedToBGRA(230, 245, 253);
     693  CSSFloralWhite:= BlueGreenRedToBGRA(240, 250, 255);
     694  CSSIvory:= BlueGreenRedToBGRA(240, 255, 255);
     695  CSSAntiqueWhite:= BlueGreenRedToBGRA(215, 235, 250);
     696  CSSLinen:= BlueGreenRedToBGRA(230, 240, 250);
     697  CSSLavenderBlush:= BlueGreenRedToBGRA(245, 240, 255);
     698  CSSMistyRose:= BlueGreenRedToBGRA(255, 228, 255);
     699
     700  //Gray colors
     701  CSSGainsboro:= BlueGreenRedToBGRA(220, 220, 220);
     702  CSSLightGray:= BlueGreenRedToBGRA(211, 211, 211);
     703  CSSSilver:= BlueGreenRedToBGRA(192, 192, 192);
     704  CSSDarkGray:= BlueGreenRedToBGRA(169, 169, 169);
     705  CSSGray:= BlueGreenRedToBGRA(128, 128, 128);
     706  CSSDimGray:= BlueGreenRedToBGRA(105, 105, 105);
     707  CSSLightSlateGray:= BlueGreenRedToBGRA(153, 136, 119);
     708  CSSSlateGray:= BlueGreenRedToBGRA(144, 128, 112);
     709  CSSDarkSlateGray:= BlueGreenRedToBGRA(79, 79, 47);
     710  CSSBlack:= BlueGreenRedToBGRA(0, 0, 0);
    202711
    203712  CSSColors := TBGRAColorList.Create;
     
    345854{$ENDIF}
    346855
     856{$IFDEF INCLUDE_FINAL}
     857{$UNDEF INCLUDE_FINAL}
     858  CSSColors.Free;
     859  VGAColors.Free;
     860{$ENDIF}
  • GraphicTest/Packages/bgrabitmap/face3d.inc

    r472 r494  
    88       ColorOverride: boolean;
    99       TexCoordOverride: boolean;
     10       ActualColor: TBGRAPixel;
     11       ActualTexCoord: TPointF;
    1012     end;
    1113
     
    1618    FVertices: packed array of TBGRAFaceVertexDescription;
    1719    FVertexCount: integer;
    18     FTexture: IBGRAScanner;
     20    FTexture, FActualTexture: IBGRAScanner;
    1921    FMaterial: IBGRAMaterial3D;
     22    FActualMaterial: TBGRAMaterial3D;
    2023    FMaterialName: string;
    2124    FParentTexture: boolean;
     
    3033    function GetVertexDescription(AIndex : integer): PBGRAFaceVertexDescription;
    3134    procedure SetCustomFlags(AValue: DWord);
     35    procedure ComputeActualVertexColor(AIndex: integer);
     36    procedure ComputeActualTexCoord(AMinIndex, AMaxIndex: integer);
     37    procedure UpdateTexture;
    3238  public
    3339    function GetObject3D: IBGRAObject3D;
    3440    constructor Create(AObject3D: IBGRAObject3D; AVertices: array of IBGRAVertex3D);
    3541    destructor Destroy; override;
     42    procedure ComputeVertexColors;
     43    procedure UpdateMaterial;
     44    procedure FlipFace;
    3645    function AddVertex(AVertex: IBGRAVertex3D): integer;
    3746    function GetParentTexture: boolean;
     
    8998    property LightThroughFactorOverride: boolean read GetLightThroughFactorOverride write SetLightThroughFactorOverride;
    9099    property Material: IBGRAMaterial3D read GetMaterial write SetMaterial;
     100    property ActualMaterial: TBGRAMaterial3D read FActualMaterial;
     101    property ActualTexture: IBGRAScanner read FActualTexture;
    91102    property VertexDescription[AIndex : integer]: PBGRAFaceVertexDescription read GetVertexDescription;
    92103    property CustomFlags: DWord read GetCustomFlags write SetCustomFlags;
     
    114125begin
    115126  FCustomFlags:= AValue;
     127end;
     128
     129procedure TBGRAFace3D.ComputeActualVertexColor(AIndex: integer);
     130begin
     131  with FVertices[AIndex] do
     132  begin
     133    if ColorOverride then
     134      ActualColor := Color
     135    else
     136    if Vertex.ParentColor then
     137      ActualColor := FObject3D.Color
     138    else
     139      ActualColor := Vertex.Color;
     140  end;
     141end;
     142
     143procedure TBGRAFace3D.ComputeActualTexCoord(AMinIndex, AMaxIndex: integer);
     144var
     145  i: Integer;
     146  zoom: TPointF;
     147  m: IBGRAMaterial3D;
     148begin
     149  m := ActualMaterial;
     150  if m <> nil then zoom := m.TextureZoom
     151  else zoom := PointF(1,1);
     152  for i := AMinIndex to AMaxIndex do
     153    with FVertices[i] do
     154    begin
     155      if TexCoordOverride then
     156        ActualTexCoord := TexCoord
     157      else
     158        ActualTexCoord := Vertex.TexCoord;
     159      ActualTexCoord.x *= zoom.x;
     160      ActualTexCoord.y *= zoom.y;
     161    end;
     162end;
     163
     164procedure TBGRAFace3D.UpdateTexture;
     165begin
     166  if FParentTexture then
     167  begin
     168    FActualTexture := nil;
     169    if FActualMaterial <> nil then
     170      FActualTexture := FActualMaterial.GetTexture;
     171    if FActualTexture = nil then
     172      FActualTexture := FObject3D.Texture
     173  end
     174  else
     175    FActualTexture := FTexture;
    116176end;
    117177
     
    131191  i: Integer;
    132192begin
    133   SetLength(FVertices, length(AVertices));
    134   for i:= 0 to high(AVertices) do
    135     AddVertex(AVertices[i]);
    136193  FObject3D := AObject3D;
    137194  FBiface := false;
     
    139196  FLightThroughFactor:= 0;
    140197  FLightThroughFactorOverride:= false;
     198
     199  UpdateMaterial;
     200
     201  SetLength(FVertices, length(AVertices));
     202  for i:= 0 to high(AVertices) do
     203    AddVertex(AVertices[i]);
    141204end;
    142205
    143206destructor TBGRAFace3D.Destroy;
    144207begin
     208  FMaterial := nil;
    145209  fillchar(FTexture,sizeof(FTexture),0);
     210  fillchar(FActualTexture,sizeof(FActualTexture),0);
    146211  inherited Destroy;
     212end;
     213
     214procedure TBGRAFace3D.ComputeVertexColors;
     215var
     216  i: Integer;
     217begin
     218  for i := 0 to FVertexCount-1 do
     219    ComputeActualVertexColor(i);
     220end;
     221
     222procedure TBGRAFace3D.UpdateMaterial;
     223begin
     224  if Material <> nil then
     225    FActualMaterial := TBGRAMaterial3D(Material.GetAsObject)
     226  else if FObject3D.Material <> nil then
     227    FActualMaterial := TBGRAMaterial3D(FObject3D.Material.GetAsObject)
     228  else if TBGRAScene3D(FObject3D.Scene).DefaultMaterial <> nil then
     229    FActualMaterial := TBGRAMaterial3D(TBGRAScene3D(FObject3D.Scene).DefaultMaterial.GetAsObject);
     230
     231  UpdateTexture;
     232
     233  ComputeActualTexCoord(0,FVertexCount-1);
     234end;
     235
     236procedure TBGRAFace3D.FlipFace;
     237var i: integer;
     238  temp: TBGRAFaceVertexDescription;
     239begin
     240  for i := 0 to (VertexCount div 2)-1 do
     241  begin
     242    temp := FVertices[i];
     243    FVertices[i] := FVertices[VertexCount-1-i];
     244    FVertices[VertexCount-1-i] := temp;
     245  end;
    147246end;
    148247
     
    161260    Normal := nil;
    162261  end;
     262  ComputeActualVertexColor(result);
     263  ComputeActualTexCoord(result,result);
    163264  inc(FVertexCount);
    164265end;
     
    186287    raise Exception.Create('Index out of bounds');
    187288  FVertices[AIndex].Vertex := AValue;
     289  ComputeActualVertexColor(AIndex);
    188290end;
    189291
     
    192294  if (AIndex < 0) or (AIndex >= FVertexCount) then
    193295    raise Exception.Create('Index out of bounds');
    194   result := FVertices[AIndex].Color;
     296  result := FVertices[AIndex].ActualColor;
    195297end;
    196298
     
    220322begin
    221323  FParentTexture := AValue;
     324  UpdateTexture;
    222325end;
    223326
     
    226329  FTexture := AValue;
    227330  FParentTexture := false;
     331  UpdateTexture;
    228332end;
    229333
     
    245349    ColorOverride := true;
    246350  end;
     351  ComputeActualVertexColor(AIndex);
    247352end;
    248353
     
    253358    raise Exception.Create('Index out of bounds');
    254359  FVertices[AIndex].ColorOverride := AValue;
     360  ComputeActualVertexColor(AIndex);
    255361end;
    256362
     
    275381  FVertices[AIndex].TexCoord := AValue;
    276382  FVertices[AIndex].TexCoordOverride := true;
     383  ComputeActualTexCoord(AIndex, AIndex);
    277384end;
    278385
     
    374481procedure TBGRAFace3D.SetMaterial(const AValue: IBGRAMaterial3D);
    375482begin
    376   FMaterial := AValue;
     483  if AValue <> FMaterial then
     484  begin
     485    FMaterial := AValue;
     486    UpdateMaterial;
     487  end;
    377488end;
    378489
     
    382493  begin
    383494    FMaterialName := AValue;
    384     FObject3D.Scene.UseMaterial(FMaterialName, self);
     495    TBGRAScene3D(FObject3D.Scene).UseMaterial(FMaterialName, self);
    385496  end;
    386497end;
  • GraphicTest/Packages/bgrabitmap/lightingclasses3d.inc

    r472 r494  
    11type
    2 
    3   { TBGRAMaterial3D }
    4 
    5   TBGRAMaterial3D = class(TInterfacedObject, IBGRAMaterial3D)
    6   private
    7     FName: string;
    8     FTexture: IBGRAScanner;
    9     FAutoSimpleColor,FAutoAmbiantColor,FAutoDiffuseColor,FAutoSpecularColor: boolean;
    10     FSimpleColorInt, FAmbiantColorInt, FDiffuseColorInt: TColorInt65536;
    11     FDiffuseLightness: integer;
    12     FTextureZoom: TPointF;
    13 
    14     FSpecularColorInt: TColorInt65536;
    15     FSpecularIndex: integer;
    16     FSpecularOn: boolean;
    17 
    18     FSaturationLowF: single;
    19     FSaturationHighF: single;
    20     FLightThroughFactor: single;
    21 
    22     //phong precalc
    23     FPowerTable: array of single;
    24     FPowerTableSize, FPowerTableExp2: integer;
    25     FPowerTableSizeF: single;
    26 
    27     procedure UpdateSpecular;
    28     procedure UpdateSimpleColor;
    29     procedure ComputePowerTable;
    30   public
    31     constructor Create;
    32     destructor Destroy; override;
    33 
    34     function GetAutoAmbiantColor: boolean;
    35     function GetAutoDiffuseColor: boolean;
    36     function GetAutoSpecularColor: boolean;
    37     function GetAutoSimpleColor: boolean;
    38     function GetAmbiantAlpha: byte;
    39     function GetAmbiantColor: TBGRAPixel;
    40     function GetAmbiantColorF: TColorF;
    41     function GetAmbiantColorInt: TColorInt65536;
    42     function GetDiffuseAlpha: byte;
    43     function GetDiffuseColor: TBGRAPixel;
    44     function GetDiffuseColorF: TColorF;
    45     function GetDiffuseColorInt: TColorInt65536;
    46     function GetLightThroughFactor: single;
    47     function GetSpecularColor: TBGRAPixel;
    48     function GetSpecularColorF: TColorF;
    49     function GetSpecularColorInt: TColorInt65536;
    50     function GetSpecularIndex: integer;
    51     function GetSaturationHigh: single;
    52     function GetSaturationLow: single;
    53     function GetSimpleAlpha: byte;
    54     function GetSimpleColor: TBGRAPixel;
    55     function GetSimpleColorF: TColorF;
    56     function GetSimpleColorInt: TColorInt65536;
    57     function GetTexture: IBGRAScanner;
    58     function GetTextureZoom: TPointF;
    59     procedure SetAutoAmbiantColor(const AValue: boolean);
    60     procedure SetAutoDiffuseColor(const AValue: boolean);
    61     procedure SetAutoSpecularColor(const AValue: boolean);
    62     procedure SetAmbiantAlpha(AValue: byte);
    63     procedure SetAmbiantColor(const AValue: TBGRAPixel);
    64     procedure SetAmbiantColorF(const AValue: TColorF);
    65     procedure SetAmbiantColorInt(const AValue: TColorInt65536);
    66     procedure SetDiffuseAlpha(AValue: byte);
    67     procedure SetDiffuseColor(const AValue: TBGRAPixel);
    68     procedure SetDiffuseColorF(const AValue: TColorF);
    69     procedure SetDiffuseColorInt(const AValue: TColorInt65536);
    70     procedure SetLightThroughFactor(const AValue: single);
    71     procedure SetSpecularColor(const AValue: TBGRAPixel);
    72     procedure SetSpecularColorF(const AValue: TColorF);
    73     procedure SetSpecularColorInt(const AValue: TColorInt65536);
    74     procedure SetSpecularIndex(const AValue: integer);
    75     procedure SetSaturationHigh(const AValue: single);
    76     procedure SetSaturationLow(const AValue: single);
    77     procedure SetSimpleAlpha(AValue: byte);
    78     procedure SetSimpleColor(AValue: TBGRAPixel);
    79     procedure SetSimpleColorF(AValue: TColorF);
    80     procedure SetSimpleColorInt(AValue: TColorInt65536);
    81     procedure SetTexture(AValue: IBGRAScanner);
    82     procedure SetTextureZoom(AValue: TPointF);
    83     function GetName: string;
    84     procedure SetName(const AValue: string);
    85 
    86     function GetSpecularOn: boolean;
    87     function GetAsObject: TObject;
    88     procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext; DiffuseIntensity, SpecularIntensity, SpecularCosine: single; const ALightColor: TColorInt65536);
    89     procedure ComputeDiffuseColor(Context: PSceneLightingContext; const DiffuseIntensity: single; const ALightColor: TColorInt65536);
    90     procedure ComputeDiffuseLightness(Context: PSceneLightingContext; DiffuseLightnessTerm32768: integer; ALightLightness: integer);
    91 
    92   end;
    93 
    94 { TBGRAMaterial3D }
    95 
    96 procedure TBGRAMaterial3D.UpdateSpecular;
    97 begin
    98   FAutoSpecularColor := (FSpecularColorInt.r = 65536) and (FSpecularColorInt.g = 65536) and (FSpecularColorInt.b = 65536) and (FSpecularColorInt.a = 65536);
    99   FSpecularOn := (FSpecularIndex > 0) and ((FSpecularColorInt.r <> 0) or (FSpecularColorInt.g <> 0) or (FSpecularColorInt.b <> 0) or
    100                                             FAutoSpecularColor);
    101 end;
    102 
    103 procedure TBGRAMaterial3D.UpdateSimpleColor;
    104 begin
    105   FSimpleColorInt := (FAmbiantColorInt+FDiffuseColorInt)*32768;
    106   FAutoSimpleColor := (FSimpleColorInt.r = 65536) and (FSimpleColorInt.g = 65536) and (FSimpleColorInt.b = 65536) and (FSimpleColorInt.a = 65536);
    107 end;
    108 
    109 procedure TBGRAMaterial3D.ComputePowerTable;
    110 var i: integer;
    111     Exponent: single;
    112 begin
    113   //exponent computed by squares
    114   Exponent := 1;
    115   FPowerTableExp2 := 0;
    116   While Exponent*FPowerTableSize/16 < FSpecularIndex do
    117   begin
    118     Exponent *= 2;
    119     Inc(FPowerTableExp2);
    120   end;
    121 
    122   //remaining exponent
    123   setlength(FPowerTable,FPowerTableSize+3);
    124   FPowerTable[0] := 0; //out of bound
    125   FPowerTable[1] := 0; //image of zero
    126   for i := 1 to FPowerTableSize do // ]0;1]
    127     FPowerTable[i+1] := Exp(ln(i/(FPowerTableSize-1))*FSpecularIndex/Exponent);
    128   FPowerTable[FPowerTableSize+2] := 1; //out of bound
    129 end;
    130 
    131 constructor TBGRAMaterial3D.Create;
    132 begin
    133   SetAmbiantColorInt(ColorInt65536(65536,65536,65536));
    134   SetDiffuseColorInt(ColorInt65536(65536,65536,65536));
    135   FSpecularIndex := 10;
    136   SetSpecularColorInt(ColorInt65536(0,0,0));
    137   FLightThroughFactor:= 0;
    138   SetSaturationLow(2);
    139   SetSaturationHigh(3);
    140 
    141   FTexture := nil;
    142   FTextureZoom := PointF(1,1);
    143 
    144   FPowerTableSize := 128;
    145   FPowerTableSizeF := FPowerTableSize;
    146   FPowerTable := nil;
    147 end;
    148 
    149 destructor TBGRAMaterial3D.Destroy;
    150 begin
    151   inherited Destroy;
    152 end;
    153 
    154 function TBGRAMaterial3D.GetAutoAmbiantColor: boolean;
    155 begin
    156   result := FAutoAmbiantColor;
    157 end;
    158 
    159 procedure TBGRAMaterial3D.SetDiffuseAlpha(AValue: byte);
    160 begin
    161   if AValue = 0 then
    162     FDiffuseColorInt.a := 0
    163   else
    164     FDiffuseColorInt.a := AValue*257+1;
    165   UpdateSimpleColor;
    166 end;
    167 
    168 function TBGRAMaterial3D.GetAutoDiffuseColor: boolean;
    169 begin
    170   result := FAutoDiffuseColor;
    171 end;
    172 
    173 function TBGRAMaterial3D.GetAutoSpecularColor: boolean;
    174 begin
    175   result := FAutoSpecularColor;
    176 end;
    177 
    178 function TBGRAMaterial3D.GetAutoSimpleColor: boolean;
    179 begin
    180   result := FAutoSimpleColor;
    181 end;
    182 
    183 function TBGRAMaterial3D.GetAmbiantAlpha: byte;
    184 var v: integer;
    185 begin
    186   if FAmbiantColorInt.a < 128 then
    187     result := 0
    188   else
    189   begin
    190     v := (FAmbiantColorInt.a-128) shr 8;
    191     if v > 255 then v := 255;
    192     result := v;
    193   end;
    194 end;
    195 
    196 function TBGRAMaterial3D.GetAmbiantColor: TBGRAPixel;
    197 begin
    198   result := ColorIntToBGRA(FAmbiantColorInt);
    199 end;
    200 
    201 function TBGRAMaterial3D.GetAmbiantColorF: TColorF;
    202 begin
    203   result := ColorInt65536ToColorF(FAmbiantColorInt);
    204 end;
    205 
    206 function TBGRAMaterial3D.GetAmbiantColorInt: TColorInt65536;
    207 begin
    208   result := FAmbiantColorInt;
    209 end;
    210 
    211 function TBGRAMaterial3D.GetDiffuseAlpha: byte;
    212 var v: integer;
    213 begin
    214   if FDiffuseColorInt.a < 128 then
    215     result := 0
    216   else
    217   begin
    218     v := (FDiffuseColorInt.a-128) shr 8;
    219     if v > 255 then v := 255;
    220     result := v;
    221   end;
    222 end;
    223 
    224 function TBGRAMaterial3D.GetDiffuseColor: TBGRAPixel;
    225 begin
    226   result := ColorIntToBGRA(FDiffuseColorInt);
    227 end;
    228 
    229 function TBGRAMaterial3D.GetDiffuseColorF: TColorF;
    230 begin
    231   result := ColorInt65536ToColorF(FDiffuseColorInt);
    232 end;
    233 
    234 function TBGRAMaterial3D.GetDiffuseColorInt: TColorInt65536;
    235 begin
    236   result := FDiffuseColorInt;
    237 end;
    238 
    239 function TBGRAMaterial3D.GetLightThroughFactor: single;
    240 begin
    241   result := FLightThroughFactor;
    242 end;
    243 
    244 function TBGRAMaterial3D.GetSpecularColor: TBGRAPixel;
    245 begin
    246   result := ColorIntToBGRA(FSpecularColorInt);
    247 end;
    248 
    249 function TBGRAMaterial3D.GetSpecularColorF: TColorF;
    250 begin
    251   result := ColorInt65536ToColorF(FSpecularColorInt);
    252 end;
    253 
    254 function TBGRAMaterial3D.GetSpecularColorInt: TColorInt65536;
    255 begin
    256   result := FSpecularColorInt;
    257 end;
    258 
    259 function TBGRAMaterial3D.GetSpecularIndex: integer;
    260 begin
    261   result := FSpecularIndex;
    262 end;
    263 
    264 function TBGRAMaterial3D.GetSaturationHigh: single;
    265 begin
    266   result := FSaturationHighF;
    267 end;
    268 
    269 function TBGRAMaterial3D.GetSaturationLow: single;
    270 begin
    271   result := FSaturationLowF;
    272 end;
    273 
    274 function TBGRAMaterial3D.GetSimpleAlpha: byte;
    275 begin
    276   result := (GetAmbiantAlpha + GetDiffuseAlpha) shr 1;
    277 end;
    278 
    279 function TBGRAMaterial3D.GetSimpleColor: TBGRAPixel;
    280 begin
    281   result := ColorIntToBGRA(GetSimpleColorInt);
    282 end;
    283 
    284 function TBGRAMaterial3D.GetSimpleColorF: TColorF;
    285 begin
    286   result := ColorInt65536ToColorF(GetSimpleColorInt);
    287 end;
    288 
    289 function TBGRAMaterial3D.GetSimpleColorInt: TColorInt65536;
    290 begin
    291   result := (GetAmbiantColorInt + GetDiffuseColorInt)*32768;
    292 end;
    293 
    294 function TBGRAMaterial3D.GetTexture: IBGRAScanner;
    295 begin
    296   result := FTexture;
    297 end;
    298 
    299 function TBGRAMaterial3D.GetTextureZoom: TPointF;
    300 begin
    301   result := FTextureZoom;
    302 end;
    303 
    304 procedure TBGRAMaterial3D.SetAutoAmbiantColor(const AValue: boolean);
    305 begin
    306   If AValue then
    307     SetAmbiantColorInt(ColorInt65536(65536,65536,65536));
    308 end;
    309 
    310 procedure TBGRAMaterial3D.SetAutoDiffuseColor(const AValue: boolean);
    311 begin
    312   If AValue then
    313     SetDiffuseColorInt(ColorInt65536(65536,65536,65536));
    314 end;
    315 
    316 procedure TBGRAMaterial3D.SetAutoSpecularColor(const AValue: boolean);
    317 begin
    318   If AValue then
    319     SetSpecularColorInt(ColorInt65536(65536,65536,65536));
    320 end;
    321 
    322 procedure TBGRAMaterial3D.SetAmbiantAlpha(AValue: byte);
    323 begin
    324   if AValue = 0 then
    325     FAmbiantColorInt.a := 0
    326   else
    327     FAmbiantColorInt.a := AValue*257+1;
    328   UpdateSimpleColor;
    329 end;
    330 
    331 procedure TBGRAMaterial3D.SetAmbiantColor(const AValue: TBGRAPixel);
    332 begin
    333   FAmbiantColorInt := BGRAToColorInt(AValue);
    334   FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
    335   UpdateSimpleColor;
    336 end;
    337 
    338 procedure TBGRAMaterial3D.SetAmbiantColorF(const AValue: TColorF);
    339 begin
    340   FAmbiantColorInt := ColorFToColorInt65536(AValue);
    341   FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
    342   UpdateSimpleColor;
    343 end;
    344 
    345 procedure TBGRAMaterial3D.SetAmbiantColorInt(const AValue: TColorInt65536);
    346 begin
    347   FAmbiantColorInt := AValue;
    348   FAutoAmbiantColor := (FAmbiantColorInt.r = 65536) and (FAmbiantColorInt.g = 65536) and (FAmbiantColorInt.b = 65536) and (FAmbiantColorInt.a = 65536);
    349   UpdateSimpleColor;
    350 end;
    351 
    352 procedure TBGRAMaterial3D.SetDiffuseColor(const AValue: TBGRAPixel);
    353 begin
    354   FDiffuseColorInt := BGRAToColorInt(AValue);
    355   FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
    356   FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
    357   UpdateSimpleColor;
    358 end;
    359 
    360 procedure TBGRAMaterial3D.SetDiffuseColorF(const AValue: TColorF);
    361 begin
    362   FDiffuseColorInt := ColorFToColorInt65536(AValue);
    363   FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
    364   FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
    365   UpdateSimpleColor;
    366 end;
    367 
    368 procedure TBGRAMaterial3D.SetDiffuseColorInt(const AValue: TColorInt65536);
    369 begin
    370   FDiffuseColorInt := AValue;
    371   FDiffuseLightness := (FDiffuseColorInt.r + FDiffuseColorInt.g + FDiffuseColorInt.b) div 6;
    372   FAutoDiffuseColor:= (FDiffuseColorInt.r = 65536) and (FDiffuseColorInt.g = 65536) and (FDiffuseColorInt.b = 65536);
    373   UpdateSimpleColor;
    374 end;
    375 
    376 procedure TBGRAMaterial3D.SetLightThroughFactor(const AValue: single);
    377 begin
    378   FLightThroughFactor:= AValue;
    379 end;
    380 
    381 procedure TBGRAMaterial3D.SetSpecularColor(const AValue: TBGRAPixel);
    382 begin
    383   FSpecularColorInt := BGRAToColorInt(AValue);
    384   UpdateSpecular;
    385 end;
    386 
    387 procedure TBGRAMaterial3D.SetSpecularColorF(const AValue: TColorF);
    388 begin
    389   FSpecularColorInt := ColorFToColorInt65536(AValue);
    390   UpdateSpecular;
    391 end;
    392 
    393 procedure TBGRAMaterial3D.SetSpecularColorInt(const AValue: TColorInt65536);
    394 begin
    395   FSpecularColorInt := AValue;
    396   UpdateSpecular;
    397 end;
    398 
    399 procedure TBGRAMaterial3D.SetSpecularIndex(const AValue: integer);
    400 begin
    401   FSpecularIndex := AValue;
    402   FPowerTable := nil;
    403   UpdateSpecular;
    404 end;
    405 
    406 procedure TBGRAMaterial3D.SetSaturationHigh(const AValue: single);
    407 begin
    408   FSaturationHighF:= AValue;
    409 end;
    410 
    411 procedure TBGRAMaterial3D.SetSaturationLow(const AValue: single);
    412 begin
    413   FSaturationLowF:= AValue;
    414 end;
    415 
    416 procedure TBGRAMaterial3D.SetSimpleAlpha(AValue: byte);
    417 begin
    418   SetAmbiantAlpha(AValue);
    419   SetDiffuseAlpha(AValue);
    420 end;
    421 
    422 procedure TBGRAMaterial3D.SetSimpleColor(AValue: TBGRAPixel);
    423 begin
    424   SetAmbiantColor(AValue);
    425   SetDiffuseColor(AValue);
    426 end;
    427 
    428 procedure TBGRAMaterial3D.SetSimpleColorF(AValue: TColorF);
    429 begin
    430   SetAmbiantColorF(AValue);
    431   SetDiffuseColorF(AValue);
    432 end;
    433 
    434 procedure TBGRAMaterial3D.SetSimpleColorInt(AValue: TColorInt65536);
    435 begin
    436   SetAmbiantColorInt(AValue);
    437   SetDiffuseColorInt(AValue);
    438 end;
    439 
    440 procedure TBGRAMaterial3D.SetTexture(AValue: IBGRAScanner);
    441 begin
    442   FTexture := AValue;
    443 end;
    444 
    445 procedure TBGRAMaterial3D.SetTextureZoom(AValue: TPointF);
    446 begin
    447   FTextureZoom := AValue;
    448 end;
    449 
    450 function TBGRAMaterial3D.GetName: string;
    451 begin
    452   result := FName;
    453 end;
    454 
    455 procedure TBGRAMaterial3D.SetName(const AValue: string);
    456 begin
    457   FName := AValue;
    458 end;
    459 
    460 function TBGRAMaterial3D.GetSpecularOn: boolean;
    461 begin
    462   result := FSpecularOn;
    463 end;
    464 
    465 function TBGRAMaterial3D.GetAsObject: TObject;
    466 begin
    467   result := self;
    468 end;
    469 
    470 procedure TBGRAMaterial3D.ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext; DiffuseIntensity, SpecularIntensity, SpecularCosine: single; const ALightColor: TColorInt65536);
    471 var
    472   NH,PowerTablePos: single; //keep first for asm
    473 
    474   NnH: single;
    475   PowerTableFPos: single;
    476   PowerTableIPos,i: integer;
    477 begin
    478   if SpecularCosine <= 0 then
    479     NnH := 0
    480   else
    481   if SpecularCosine >= 1 then
    482     NnH := 1 else
    483   begin
    484     NH := SpecularCosine;
    485     if FPowerTable = nil then ComputePowerTable;
    486     {$IFDEF CPUI386} {$asmmode intel}
    487     i := FPowerTableExp2;
    488     if i > 0 then
    489     begin
    490       PowerTablePos := FPowerTableSize;
    491       asm
    492         db $d9,$45,$f0  //flds NH
    493         mov ecx,i
    494       @loop:
    495         db $dc,$c8      //fmul st,st(0)
    496         dec ecx
    497         jnz @loop
    498         db $d8,$4d,$ec  //fmuls PowerTablePos
    499         db $d9,$5d,$ec  //fstps PowerTablePos
    500       end;
    501     end
    502     else
    503       PowerTablePos := NH*FPowerTableSize;
    504     {$ELSE}
    505     PowerTablePos := NH;
    506     for i := FPowerTableExp2-1 downto 0 do
    507       PowerTablePos := PowerTablePos*PowerTablePos;
    508     PowerTablePos *= FPowerTableSize;
    509     {$ENDIF}
    510     PowerTableIPos := round(PowerTablePos+0.5);
    511     PowerTableFPos := PowerTablePos-PowerTableIPos;
    512     NnH := FPowerTable[PowerTableIPos]*(1-PowerTableFPos)+FPowerTable[PowerTableIPos+1]*PowerTableFPos;
    513   end; //faster than NnH := exp(FSpecularIndex*ln(NH)); !
    514 
    515   if FAutoDiffuseColor then
    516     Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)
    517   else
    518     Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);
    519 
    520   if FAutoSpecularColor then
    521     Context^.specularColor += ALightColor*round(SpecularIntensity* NnH*65536)
    522   else
    523     Context^.specularColor += ALightColor*FSpecularColorInt*round(SpecularIntensity* NnH*65536);
    524 end;
    525 
    526 procedure TBGRAMaterial3D.ComputeDiffuseColor(Context: PSceneLightingContext;
    527   const DiffuseIntensity: single; const ALightColor: TColorInt65536);
    528 begin
    529   if FAutoDiffuseColor then
    530     Context^.diffuseColor += ALightColor*round(DiffuseIntensity*65536)
    531   else
    532     Context^.diffuseColor += ALightColor*FDiffuseColorInt*round(DiffuseIntensity*65536);
    533 end;
    534 
    535 procedure TBGRAMaterial3D.ComputeDiffuseLightness(
    536   Context: PSceneLightingContext; DiffuseLightnessTerm32768: integer; ALightLightness: integer);
    537 begin
    538   if FAutoDiffuseColor then
    539   begin
    540     if ALightLightness <> 32768 then
    541       Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,ALightLightness)
    542     else
    543       Context^.lightness += DiffuseLightnessTerm32768;
    544   end else
    545   begin
    546     if FDiffuseLightness <> 32768 then
    547       Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,CombineLightness(FDiffuseLightness,ALightLightness))
    548     else
    549       Context^.lightness += CombineLightness(DiffuseLightnessTerm32768,ALightLightness);
    550   end;
    551 end;
    552 
    553 type
    554 
    555   { TBGRALight3D }
    556 
    557   TBGRALight3D = class(TInterfacedObject,IBGRALight3D)
    558   protected
    559     FMinIntensity: single;
    560     FColorInt: TColorInt65536;
    561     FViewVector : TPoint3D_128;
    562     FLightness: integer;
    563   public
    564     constructor Create;
    565     destructor Destroy; override;
    566 
    567     procedure ComputeDiffuseLightness(Context: PSceneLightingContext); virtual; abstract;
    568     procedure ComputeDiffuseColor(Context: PSceneLightingContext); virtual; abstract;
    569     procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext); virtual; abstract;
    570 
    571     function GetLightnessF: single;
    572     function GetColor: TBGRAPixel;
    573     function GetColorF: TColorF;
    574     function GetColorInt: TColorInt65536;
    575     function GetAsObject: TObject;
    576     procedure SetColor(const AValue: TBGRAPixel);
    577     procedure SetColorF(const AValue: TColorF);
    578     procedure SetColorInt(const AValue: TColorInt65536);
    579     function GetColoredLight: boolean;
    580 
    581     function GetMinIntensity: single;
    582     procedure SetMinIntensity(const AValue: single);
    583     function IsDirectional: boolean; virtual; abstract;
    584   end;
    585 
    5862  { TBGRADirectionalLight3D }
    5873
     
    5917  public
    5928    constructor Create(ADirection: TPoint3D);
    593     function GetDirection: TPoint3D;
     9    function GetDirection: TPoint3D; override;
    59410    procedure SetDirection(const AValue: TPoint3D);
    59511
     
    60824  public
    60925    constructor Create(AVertex: IBGRAVertex3D; AIntensity: single);
    610     function GetIntensity: single;
     26    function GetIntensity: single; override;
    61127    procedure SetIntensity(const AValue: single);
    61228
    61329    function GetVertex: IBGRAVertex3D;
    61430    procedure SetVertex(const AValue: IBGRAVertex3D);
     31    function GetPosition: TPoint3D; override;
    61532
    61633    procedure ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext); override;
     
    61936    function IsDirectional: boolean; override;
    62037  end;
    621 
    622 { TBGRALight3D }
    623 
    624 constructor TBGRALight3D.Create;
    625 begin
    626   SetColorF(ColorF(1,1,1,1));
    627   FViewVector := Point3D_128(0,0,-1);
    628   FMinIntensity:= 0;
    629 end;
    630 
    631 destructor TBGRALight3D.Destroy;
    632 begin
    633   inherited Destroy;
    634 end;
    635 
    636 function TBGRALight3D.GetLightnessF: single;
    637 begin
    638   result := FLightness/32768;
    639 end;
    640 
    641 function TBGRALight3D.GetColor: TBGRAPixel;
    642 begin
    643   result := ColorIntToBGRA(FColorInt);
    644 end;
    645 
    646 function TBGRALight3D.GetColorF: TColorF;
    647 begin
    648   result := ColorInt65536ToColorF(FColorInt);
    649 end;
    650 
    651 function TBGRALight3D.GetColorInt: TColorInt65536;
    652 begin
    653   result := FColorInt;
    654 end;
    655 
    656 function TBGRALight3D.GetAsObject: TObject;
    657 begin
    658   result := self;
    659 end;
    660 
    661 procedure TBGRALight3D.SetColor(const AValue: TBGRAPixel);
    662 begin
    663   SetColorInt(BGRAToColorInt(AValue));
    664 end;
    665 
    666 procedure TBGRALight3D.SetColorF(const AValue: TColorF);
    667 begin
    668   SetColorInt(ColorFToColorInt65536(AValue));
    669 end;
    670 
    671 procedure TBGRALight3D.SetColorInt(const AValue: TColorInt65536);
    672 begin
    673   FColorInt := AValue;
    674   FLightness:= (AValue.r+AValue.g+AValue.b) div 6;
    675 end;
    676 
    677 function TBGRALight3D.GetColoredLight: boolean;
    678 begin
    679   result := (FColorInt.r <> FColorInt.g) or (FColorInt.g <> FColorInt.b);
    680 end;
    681 
    682 function TBGRALight3D.GetMinIntensity: single;
    683 begin
    684   result := FMinIntensity;
    685 end;
    686 
    687 procedure TBGRALight3D.SetMinIntensity(const AValue: single);
    688 begin
    689   FMinIntensity := AValue;
    690 end;
    69138
    69239{ TBGRAPointLight3D }
     
    71966end;
    72067
     68function TBGRAPointLight3D.GetPosition: TPoint3D;
     69begin
     70  Result:= FVertex.GetViewCoord;
     71end;
     72
    72173procedure TBGRAPointLight3D.ComputeDiffuseAndSpecularColor(Context: PSceneLightingContext);
    72274  {$DEFINE PARAM_POINTLIGHT}
     
    73587  else
    73688  begin
    737     intensity := (DotProduct3D_128(vect, Context^.basic.Normal))/(dist2*sqrt(dist2))*FIntensity;
     89    intensity := DotProduct3D_128(vect, Context^.basic.Normal)/(dist2*sqrt(dist2))*FIntensity;
    73890    if Context^.LightThrough and (intensity < 0) then intensity := -intensity*Context^.LightThroughFactor;
    73991    if intensity > 100 then intensity := 100;
     
    74395end;
    74496
    745 procedure TBGRAPointLight3D.ComputeDiffuseColor(Context: PSceneLightingContext
    746   );
     97procedure TBGRAPointLight3D.ComputeDiffuseColor(Context: PSceneLightingContext);
    74798var
    74899  vect: TPoint3D_128;
     
    755106  else
    756107  begin
    757     intensity := (DotProduct3D_128(vect, Context^.basic.Normal))/(dist2*sqrt(dist2))*FIntensity;
     108    intensity := DotProduct3D_128(vect, Context^.basic.Normal)/(dist2*sqrt(dist2))*FIntensity;
    758109    if Context^.LightThrough and (intensity < 0) then intensity := -intensity*Context^.LightThroughFactor;
    759110    if intensity > 100 then intensity := 100;
  • GraphicTest/Packages/bgrabitmap/object3d.inc

    r472 r494  
    1 
    21{ TBGRAObject3D }
    32
     
    1514  FLight := 1;
    1615  FTexture := nil;
    17   FMainPart := TBGRAPart3D.Create(nil);
     16  FMainPart := TBGRAPart3D.Create(self,nil);
    1817  FLightingNormal:= AScene.DefaultLightingNormal;
    1918  FParentLighting:= True;
    2019  FScene := AScene;
     20  FFaceColorsInvalidated := true;
     21  FMaterialInvalidated := false;
    2122end;
    2223
    2324destructor TBGRAObject3D.Destroy;
    2425begin
     26  FMaterial := nil;
    2527  fillchar(FTexture,sizeof(FTexture),0);
    2628  inherited Destroy;
     
    3436end;
    3537
     38procedure TBGRAObject3D.InvalidateColor;
     39begin
     40  FFaceColorsInvalidated := true;
     41end;
     42
     43procedure TBGRAObject3D.InvalidateMaterial;
     44begin
     45  FMaterialInvalidated := true;
     46end;
     47
    3648function TBGRAObject3D.GetColor: TBGRAPixel;
    3749begin
     
    5870  FColor := AValue;
    5971  FTexture := nil;
     72  InvalidateColor;
    6073end;
    6174
     
    6881begin
    6982  FTexture := AValue;
     83  InvalidateMaterial;
    7084end;
    7185
     
    7387begin
    7488  FMaterial := AValue;
     89  InvalidateMaterial;
    7590end;
    7691
     
    124139end;
    125140
    126 function TBGRAObject3D.GetScene: TBGRAScene3D;
     141function TBGRAObject3D.GetScene: TObject;
    127142begin
    128143  result := FScene;
     
    151166  for i := 0 to GetFaceCount-1 do
    152167    ACallback(GetFace(i));
     168end;
     169
     170procedure TBGRAObject3D.Update;
     171var
     172  i: Integer;
     173begin
     174  if FParentLighting and (FLightingNormal <> FScene.DefaultLightingNormal) then
     175    FLightingNormal := FScene.DefaultLightingNormal;
     176
     177  if FFaceColorsInvalidated then
     178  begin
     179    for i := 0 to FFaceCount-1 do
     180      FFaces[i].ComputeVertexColors;
     181    FFaceColorsInvalidated := false;
     182  end;
     183
     184  if FMaterialInvalidated then
     185  begin
     186    for i := 0 to FFaceCount-1 do
     187      FFaces[i].UpdateMaterial;
     188    FMaterialInvalidated := false;
     189  end;
    153190end;
    154191
  • GraphicTest/Packages/bgrabitmap/paletteformats.inc

    r472 r494  
    316316  function ReadInt16: int16;
    317317  begin
    318     AStream.Read({%H-}result, sizeof(result));
     318    {$PUSH}{$HINTS OFF}
     319    AStream.Read(result, sizeof(result));
     320    {$POP}
    319321    result := BEtoN(result);
    320322  end;
    321323  function ReadInt32: int32;
    322324  begin
    323     AStream.Read({%H-}result, sizeof(result));
     325    {$PUSH}{$HINTS OFF}
     326    AStream.Read(result, sizeof(result));
     327    {$POP}
    324328    result := BEtoN(result);
    325329  end;
     
    332336  function ReadSingle: single;
    333337  begin
    334     AStream.Read({%H-}Result, sizeof(result));
     338    {$PUSH}{$HINTS OFF}
     339    AStream.Read(Result, sizeof(result));
     340    {$POP}
    335341    DWord(Result) := BEtoN(DWord(Result));
    336342  end;
  • GraphicTest/Packages/bgrabitmap/part3d.inc

    r472 r494  
    1414    FCoordPool: TBGRACoordPool3D;
    1515    FNormalPool: TBGRANormalPool3D;
     16    FObject3D: TBGRAObject3D;
    1617  public
    17     constructor Create(AContainer: IBGRAPart3D);
     18    constructor Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D);
    1819    destructor Destroy; override;
    1920    procedure Clear(ARecursive: boolean);
     
    214215end;
    215216
    216 constructor TBGRAPart3D.Create(AContainer: IBGRAPart3D);
    217 begin
     217constructor TBGRAPart3D.Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D);
     218begin
     219  FObject3D := AObject3D;
    218220  FContainer := AContainer;
    219221  FMatrix := MatrixIdentity3D;
     
    229231  FVertexCount := 0;
    230232  if FCoordPool.UsedCapacity > 0 then
    231     raise Exception.Create('Coordinate pool still used');
     233    raise Exception.Create('Coordinate pool still used. Please set vertex references to nil before destroying the scene.');
    232234  FreeAndNil(FCoordPool);
    233235  if Assigned(FNormalPool) then
     
    258260function TBGRAPart3D.Add(x, y, z: single): IBGRAVertex3D;
    259261begin
    260   result := TBGRAVertex3D.Create(FCoordPool,Point3D(x,y,z));
     262  result := TBGRAVertex3D.Create(FObject3D,FCoordPool,Point3D(x,y,z));
    261263  Add(result);
    262264end;
     
    264266function TBGRAPart3D.Add(pt: TPoint3D): IBGRAVertex3D;
    265267begin
    266   result := TBGRAVertex3D.Create(FCoordPool,pt);
     268  result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
    267269  Add(result);
    268270end;
     
    270272function TBGRAPart3D.Add(pt: TPoint3D; normal: TPoint3D): IBGRAVertex3D;
    271273begin
    272   result := TBGRAVertex3D.Create(FCoordPool,pt);
     274  result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
    273275  result.CustomNormal := normal;
    274276  Add(result);
     
    277279function TBGRAPart3D.Add(pt: TPoint3D_128): IBGRAVertex3D;
    278280begin
    279   result := TBGRAVertex3D.Create(FCoordPool,pt);
     281  result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
    280282  Add(result);
    281283end;
     
    283285function TBGRAPart3D.Add(pt: TPoint3D_128; normal: TPoint3D_128): IBGRAVertex3D;
    284286begin
    285   result := TBGRAVertex3D.Create(FCoordPool,pt);
     287  result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
    286288  result.CustomNormal := Point3D(normal);
    287289  Add(result);
     
    312314  setlength(result, length(pts));
    313315  for i := 0 to high(pts) do
    314     result[i] := TBGRAVertex3D.Create(FCoordPool,pts[i]);
     316    result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]);
    315317  Add(result);
    316318end;
     
    323325  setlength(result, length(pts));
    324326  for i := 0 to high(pts) do
    325     result[i] := TBGRAVertex3D.Create(FCoordPool,pts[i]);
     327    result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]);
    326328  Add(result);
    327329end;
     
    490492end;
    491493
     494{$PUSH}{$OPTIMIZATION OFF} //avoids Internal error 2012090607
    492495procedure TBGRAPart3D.ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
    493496var
     
    501504    FParts[i].ComputeWithMatrix(Composed,AProjection);
    502505end;
     506{$POP}
    503507
    504508function TBGRAPart3D.ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF;
     
    550554  if FPartCount = length(FParts) then
    551555    setlength(FParts, FPartCount*2+1);
    552   result := TBGRAPart3D.Create(self);
     556  result := TBGRAPart3D.Create(FObject3D,self);
    553557  FParts[FPartCount] := result;
    554558  inc(FPartCount);
  • GraphicTest/Packages/bgrabitmap/phonglight.inc

    r472 r494  
    22var
    33  {%H-}dist2,LdotN,NdotH,lightEnergy,diffuse : single;
    4 const
    5   minus_05 = -0.5;
    64begin
    75  {$IFDEF BGRASSE_AVAILABLE}If UseSSE then
     
    4543  end;
    4644
    47   if LdotN < minus_05 then NdotH := 0 else
    48   if LdotN < 0 then
    49   begin
    50     NdotH := NdotH*(LdotN-minus_05);
    51     NdotH += NdotH;
    52   end;
    53 
    5445  {$IFDEF PARAM_POINTLIGHT}
    5546  if dist2 = 0 then
     
    6253  diffuse := LdotN;
    6354  {$ENDIF}
     55  if diffuse < FMinIntensity then diffuse:= FMinIntensity;
    6456
    6557  if Context^.LightThrough and (diffuse < 0) then diffuse := -diffuse*Context^.LightThroughFactor;
  • GraphicTest/Packages/bgrabitmap/polyaliaspersp.inc

    r472 r494  
    165165
    166166begin
    167   If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     167  If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit;
    168168
    169169  inter := polyInfo.CreateIntersectionArray;
     
    361361
    362362begin
    363   If not polyInfo.ComputeMinMax(minx,miny,maxx,maxy,bmp) then exit;
     363  If not BGRAShapeComputeMinMax(polyInfo,minx,miny,maxx,maxy,bmp) then exit;
    364364
    365365  inter := polyInfo.CreateIntersectionArray;
  • GraphicTest/Packages/bgrabitmap/readme.txt

    r472 r494  
    1 BGRABitmap - Drawing routines with alpha blending and antialiasing with Lazarus.
     1BGRABitmap - Drawing routines with transparency and antialiasing with Lazarus. Offers also various transforms.
    22
    3 These routines allow to manipulate 32bit images in BGRA format.
     3These routines allow to manipulate 32bit images in BGRA format or RGBA format (depending on the platform).
    44
    55This code is under modified LGPL (see COPYING.modifiedLGPL.txt). This means that you can link this library inside your programs for any purpose. Only the included part of the code must remain LGPL.
  • GraphicTest/Packages/bgrabitmap/unzipperext.pas

    r472 r494  
    3131implementation
    3232
    33 uses lazutf8classes;
     33uses BGRAUTF8;
    3434
    3535{ TUnzipperStreamUtf8 }
  • GraphicTest/Packages/bgrabitmap/vertex3d.inc

    r472 r494  
    11type
     2  { TBGRAObject3D }
     3
     4  TBGRAObject3D = class(TInterfacedObject,IBGRAObject3D)
     5  private
     6    FColor: TBGRAPixel;
     7    FLight: Single;
     8    FTexture: IBGRAScanner;
     9    FMainPart: IBGRAPart3D;
     10    FFaces: array of IBGRAFace3D;
     11    FFaceCount: integer;
     12    FLightingNormal : TLightingNormal3D;
     13    FParentLighting: boolean;
     14    FMaterial: IBGRAMaterial3D;
     15    FScene: TBGRAScene3D;
     16    FFaceColorsInvalidated,
     17    FMaterialInvalidated: boolean;
     18    procedure AddFace(AFace: IBGRAFace3D);
     19  public
     20    constructor Create(AScene: TBGRAScene3D);
     21    destructor Destroy; override;
     22    procedure Clear;
     23    procedure InvalidateColor;
     24    procedure InvalidateMaterial;
     25    function AddFace(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
     26    function AddFace(const AVertices: array of IBGRAVertex3D; ABiface: boolean): IBGRAFace3D;
     27    function AddFace(const AVertices: array of IBGRAVertex3D; ATexture: IBGRAScanner): IBGRAFace3D;
     28    function AddFace(const AVertices: array of IBGRAVertex3D; AColor: TBGRAPixel): IBGRAFace3D;
     29    function AddFace(const AVertices: array of IBGRAVertex3D; AColors: array of TBGRAPixel): IBGRAFace3D;
     30    function AddFaceReversed(const AVertices: array of IBGRAVertex3D): IBGRAFace3D;
     31    procedure ComputeWithMatrix(constref AMatrix: TMatrix3D; constref AProjection: TProjection3D);
     32    function GetColor: TBGRAPixel;
     33    function GetLight: Single;
     34    function GetTexture: IBGRAScanner;
     35    function GetMainPart: IBGRAPart3D;
     36    function GetLightingNormal: TLightingNormal3D;
     37    function GetParentLighting: boolean;
     38    function GetFace(AIndex: integer): IBGRAFace3D;
     39    function GetFaceCount: integer;
     40    function GetTotalVertexCount: integer;
     41    function GetTotalNormalCount: integer;
     42    function GetMaterial: IBGRAMaterial3D;
     43    procedure SetLightingNormal(const AValue: TLightingNormal3D);
     44    procedure SetParentLighting(const AValue: boolean);
     45    procedure SetColor(const AValue: TBGRAPixel);
     46    procedure SetLight(const AValue: Single);
     47    procedure SetTexture(const AValue: IBGRAScanner);
     48    procedure SetMaterial(const AValue: IBGRAMaterial3D);
     49    procedure RemoveUnusedVertices;
     50    procedure SeparatePart(APart: IBGRAPart3D);
     51    function GetScene: TObject;
     52    function GetRefCount: integer;
     53    procedure SetBiface(AValue : boolean);
     54    procedure ForEachVertex(ACallback: TVertex3DCallback);
     55    procedure ForEachFace(ACallback: TFace3DCallback);
     56    procedure Update;
     57  end;
     58
    259  { TBGRAVertex3D }
    360
     
    1168    FCoordPoolIndex: integer;
    1269    FCustomFlags: DWord;
     70    FObject3D: TBGRAObject3D;
    1371    function GetCoordData: PBGRACoordData3D;
    14     procedure Init(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
     72    procedure Init(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    1573  public
    16     constructor Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); overload;
    17     constructor Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); overload;
     74    constructor Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D); overload;
     75    constructor Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128); overload;
    1876    destructor Destroy; override;
    1977    function GetColor: TBGRAPixel;
     
    166224{ TBGRAVertex3D }
    167225
    168 procedure TBGRAVertex3D.Init(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    169 begin
     226procedure TBGRAVertex3D.Init(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
     227begin
     228  FObject3D := AObject3D;
    170229  FCoordPool := ACoordPool;
    171230  FCoordPoolIndex := FCoordPool.Add;
     
    209268end;
    210269
    211 constructor TBGRAVertex3D.Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D);
    212 begin
    213   Init(ACoordPool, Point3D_128(ASceneCoord));
    214 end;
    215 
    216 constructor TBGRAVertex3D.Create(ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
    217 begin
    218   Init(ACoordPool, ASceneCoord);
     270constructor TBGRAVertex3D.Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D);
     271begin
     272  Init(AObject3D, ACoordPool, Point3D_128(ASceneCoord));
     273end;
     274
     275constructor TBGRAVertex3D.Create(AObject3D: TBGRAObject3D; ACoordPool: TBGRACoordPool3D; ASceneCoord: TPoint3D_128);
     276begin
     277  Init(AObject3D, ACoordPool, ASceneCoord);
    219278end;
    220279
     
    284343  FColor := AValue;
    285344  FParentColor := false;
     345  FObject3D.InvalidateColor;
    286346end;
    287347
     
    339399begin
    340400  FParentColor := AValue;
     401  FObject3D.InvalidateColor;
    341402end;
    342403
Note: See TracChangeset for help on using the changeset viewer.