{=== TBGRACustomBitmap and IBGRAScanner ===}
{$IFDEF INCLUDE_SCANNER_INTERFACE}
{$UNDEF INCLUDE_SCANNER_INTERFACE}
type
  TBGRACustomBitmap = class;
{==== IBGRAScanner ====}
  {* Interface for a scanner. A scanner is like an image, but its content
     has no limit and it can be calculated on the fly. It is like a
     infinite readonly image.
   *
   * Note: it must not implement reference counting even if it is an interface
   *
   * ''TBGRACustomBitmap'' implements this interface and the content is repeated
     horizontally and vertically. There are also various classes
     in ''BGRAGradientScanner'' unit that generate gradients on the fly and
     in ''BGRATransform'' unit that provide geometrical transformations of images }
  IBGRAScanner = interface
    {** Move to the position (''X'',''Y'') for the next call to ''ScanNextPixel'' }
    procedure ScanMoveTo(X,Y: Integer);
    {** Scan the pixel at the current location and increments ''X'' }
    function ScanNextPixel: TBGRAPixel;
    {** Scan at any location using floating point coordinates }
    function ScanAt(X,Y: Single): TBGRAPixel;
    {** Scan at any location using integer coordinates }
    function ScanAtInteger(X,Y: integer): TBGRAPixel;
    {** Copy a row of pixels from ''X'' to ''X''+''count''-1 to a specified destination
        ''pdest''. ''mode'' indicates how to combine with existing data }
    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode);
    {** Returns True if the function ''ScanPutPixels'' is available. Otherwise
        you need to call ''ScanNextPixel'' and combine pixels for example
        with ''SetPixel'' }
    function IsScanPutPixelsDefined: boolean;
  end;

  {** A type of function of a scanner that returns the content at floating point coordinates }
  TScanAtFunction = function (X,Y: Single): TBGRAPixel of object;
  {** A type of function of a scanner that returns the content at integer coordinates }
  TScanAtIntegerFunction = function (X,Y: Integer): TBGRAPixel of object;
  {** A type of function of a scanner that returns the next pixel }
  TScanNextPixelFunction = function: TBGRAPixel of object;

  { TBGRACustomScanner }
  {* Base class for implementing ''IBGRAScanner'' interface }
  TBGRACustomScanner = class(IBGRAScanner)
  private
    FCurX,FCurY: integer;
  public
    function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual;
    procedure ScanMoveTo(X,Y: Integer); virtual;
    function ScanNextPixel: TBGRAPixel; virtual;
    function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
    procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
    function IsScanPutPixelsDefined: boolean; virtual;
  protected
    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};
    function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
    function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
  end;
{$ENDIF}

{$IFDEF INCLUDE_INTERFACE}
{$UNDEF INCLUDE_INTERFACE}
type
{==== TBGRACustomBitmap ====}

  { TBGRACustomBitmap }
  {* This is the base class for ''TBGRABitmap''. It is the direct parent of
     ''TBGRADefaultBitmap'' class, which is the parent of the diverse
     implementations. A bitmap can be used as a scanner using the interface
     ''IBGRAScanner'' }
  TBGRACustomBitmap = class(TFPCustomImage,IBGRAScanner)
  private
    function GetFontAntialias: Boolean;
    procedure SetFontAntialias(const AValue: Boolean);
  protected
     { accessors to properies }
     function GetArrowEndRepeat: integer; virtual; abstract;
     function GetArrowStartRepeat: integer; virtual; abstract;
     procedure SetArrowEndRepeat(AValue: integer); virtual; abstract;
     procedure SetArrowStartRepeat(AValue: integer); virtual; abstract;
     function GetArrowEndOffset: single; virtual; abstract;
     function GetArrowStartOffset: single; virtual; abstract;
     procedure SetArrowEndOffset(AValue: single); virtual; abstract;
     procedure SetArrowStartOffset(AValue: single); virtual; abstract;
     function GetArrowEndSize: TPointF; virtual; abstract;
     function GetArrowStartSize: TPointF; virtual; abstract;
     procedure SetArrowEndSize(AValue: TPointF); virtual; abstract;
     procedure SetArrowStartSize(AValue: TPointF); virtual; abstract;
     function GetLineCap: TPenEndCap; virtual; abstract;
     procedure SetLineCap(AValue: TPenEndCap); virtual; abstract;
     function GetFontRenderer: TBGRACustomFontRenderer; virtual; abstract;
     procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); virtual; abstract;
     function GetHeight: integer; virtual; abstract;
     function GetWidth: integer; virtual; abstract;
     function GetDataPtr: PBGRAPixel; virtual; abstract;
     function GetNbPixels: integer; virtual; abstract;
     function CheckEmpty: boolean; virtual; abstract;
     function GetHasTransparentPixels: boolean; virtual; abstract;
     function GetAverageColor: TColor; virtual; abstract;
     function GetAveragePixel: TBGRAPixel; virtual; abstract;
     procedure SetCanvasOpacity(AValue: byte); virtual; abstract;
     function GetScanLine(y: integer): PBGRAPixel; virtual; abstract;
     function GetRefCount: integer; virtual; abstract;
     function GetBitmap: TBitmap; virtual; abstract;
     function GetLineOrder: TRawImageLineOrder; virtual; abstract;
     function GetCanvasFP: TFPImageCanvas; virtual; abstract;
     function GetCanvasDrawModeFP: TDrawMode; virtual; abstract;
     procedure SetCanvasDrawModeFP(const AValue: TDrawMode); virtual; abstract;
     function GetCanvas: TCanvas; virtual; abstract;
     function GetCanvasOpacity: byte; virtual; abstract;
     function GetCanvasAlphaCorrection: boolean; virtual; abstract;
     procedure SetCanvasAlphaCorrection(const AValue: boolean); virtual; abstract;
     function GetFontHeight: integer; virtual; abstract;
     procedure SetFontHeight(AHeight: integer); virtual; abstract;
     function GetFontFullHeight: integer; virtual; abstract;
     procedure SetFontFullHeight(AHeight: integer); virtual; abstract;
     function GetPenStyle: TPenStyle; virtual; abstract;
     procedure SetPenStyle(const AValue: TPenStyle); virtual; abstract;
     function GetCustomPenStyle: TBGRAPenStyle; virtual; abstract;
     procedure SetCustomPenStyle(const AValue: TBGRAPenStyle); virtual; abstract;
     function GetClipRect: TRect; virtual; abstract;
     procedure SetClipRect(const AValue: TRect); virtual; abstract;
     function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
     procedure ClearTransparentPixels; virtual; abstract;
     procedure SetArrowStart(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;
     procedure SetArrowEnd(AStyle: TBGRAArrowStyle; ATipStyle: TPenJoinStyle = pjsMiter; ARelativePenWidth: single = 1; ATriangleBackOffset: single = 0); virtual; abstract;

     procedure InternalArc(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions; ADrawChord: boolean = false; ATexture: IBGRAScanner = nil); overload;
     procedure InternalArc(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions; ADrawChord: boolean = false; ATexture: IBGRAScanner = nil); overload; virtual; abstract;
     procedure InternalArcInRect(r: TRect; StartAngleRad,EndAngleRad: Single; ABorderColor : TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions; ADrawChord: boolean = false; ATexture: IBGRAScanner = nil);
     procedure InternalFillArcInRect(r: TRect; StartAngleRad,EndAngleRad: Single; AFillColor: TBGRAPixel; AOptions: TArcOptions; ATexture: IBGRAScanner = nil);

  public
     {** User defined caption. It does not appear on the image }
     Caption:   string;

     {** Method to use when filling polygons (winding or alternate).
         See [[BGRABitmap Types imported from Graphics|BGRAGraphics]] }
     FillMode:  TFillMode;

     {** Specifies if linear antialiasing must be used when drawing
         antialiased shapes }
     LinearAntialiasing: boolean;

     {** Resample filter is used when resizing the bitmap. See [[BGRABitmap Miscellaneous types#Images and resampling|resampling types]] }
     ResampleFilter : TResampleFilter;

     {** Scan interpolation filter is used when the bitmap is used
         as a scanner (interface ''IBGRAScanner'') }
     ScanInterpolationFilter: TResampleFilter;
     {** Offset to apply when the image is scanned using ''IBGRAScanner'' interface }
     ScanOffset: TPoint;

     {** Width of the image in pixels }
     property Width: integer Read GetWidth;
     {** Height of the image in pixels }
     property Height: integer Read GetHeight;

     {** Clipping rectangle for all drawing functions }
     property ClipRect: TRect read GetClipRect write SetClipRect;

     {** Total number of pixels. It is always true that ''NbPixels'' = ''Width'' * ''Height'' }
     property NbPixels: integer Read GetNbPixels;

     {** Returns the address of the left-most pixel of any line.
         The parameter y ranges from 0 to Height-1 }
     property ScanLine[y: integer]: PBGRAPixel Read GetScanLine;

     {** Indicates the order in which lines are stored in memory.
         If it is equal to ''riloTopToBottom'', the first line is the top line.
         If it is equal to ''riloBottomToTop'', the first line is the bottom line.
         See [[BGRABitmap Miscellaneous types|miscellaneous types]] }
     property LineOrder: TRawImageLineOrder Read GetLineOrder;

     {** Provides a pointer to the first pixel in memory.
         Depending on the ''LineOrder'' property, this can be the top-left pixel or the bottom-left pixel.
         There is no padding between scanlines, so the start of the next line is at the address ''Data'' + ''Width''.
         See [[BGRABitmap tutorial 4]] }
     property Data: PBGRAPixel Read GetDataPtr;

     {** Number of references to this image. It is increased by the function
         ''NewReference'' and decreased by the function ''FreeReference'' }
     property RefCount: integer Read GetRefCount;

     {** Returns True if the bitmap only contains transparent pixels or has a size of zero }
     property Empty: boolean Read CheckEmpty;

     {** Returns True if there are transparent or semitransparent pixels,
         and so if the image would be stored with an alpha channel }
     property HasTransparentPixels: boolean Read GetHasTransparentPixels;

     {** Average color of the image }
     property AverageColor: TColor Read GetAverageColor;
     {** Average color (including alpha) of the image }
     property AveragePixel: TBGRAPixel Read GetAveragePixel;

     {** Canvas compatible with FreePascal }
     property CanvasFP: TFPImageCanvas read GetCanvasFP;
     {** Draw mode to used when image is access using FreePascal functions
         and ''Colors'' property }
     property CanvasDrawModeFP: TDrawMode read GetCanvasDrawModeFP write SetCanvasDrawModeFP;

     {** Bitmap in a format compatible with the current GUI.
         Don't forget to call ''InvalidateBitmap'' before using it
         if you changed something with direct pixel access (''Scanline''
         and ''Data'') }
     property Bitmap: TBitmap Read GetBitmap;
     {** Canvas provided by the GUI }
     property Canvas: TCanvas Read GetCanvas;
     {** Opacity to apply to changes made using GUI functions, provided
         ''CanvasAlphaCorrection'' is set to ''True'' }
     property CanvasOpacity: byte Read GetCanvasOpacity Write SetCanvasOpacity;
     {** Specifies if the alpha values must be corrected after GUI access
         to the bitmap }
     property CanvasAlphaCorrection: boolean Read GetCanvasAlphaCorrection Write SetCanvasAlphaCorrection;

  public {----------- pen style ----------------}
     {** How to join segments. See [[BGRABitmap Types imported from Graphics|BGRAGraphics]] }
     JoinStyle: TPenJoinStyle;
     {** Limit for the extension of the segments when joining them
         with ''pjsMiter'' join style, expressed in multiples of the width
         of the pen }
     JoinMiterLimit: single;

     {** Pen style. See [[BGRABitmap Types imported from Graphics|BGRAGraphics]] }
     property PenStyle: TPenStyle read GetPenStyle Write SetPenStyle;
     {** Custom pen style. See [[BGRABitmap Geometry types|geometric types]] }
     property CustomPenStyle: TBGRAPenStyle read GetCustomPenStyle write SetCustomPenStyle;
     {** How to draw the ends of a line }
     property LineCap: TPenEndCap read GetLineCap write SetLineCap;

     {** Size of arrows at the start of the line }
     property ArrowStartSize: TPointF read GetArrowStartSize write SetArrowStartSize;
     {** Size of arrows at the end of the line }
     property ArrowEndSize: TPointF read GetArrowEndSize write SetArrowEndSize;
     {** Offset of the arrow from the start of the line }
     property ArrowStartOffset: single read GetArrowStartOffset write SetArrowStartOffset;
     {** Offset of the arrow from the end of the line }
     property ArrowEndOffset: single read GetArrowEndOffset write SetArrowEndOffset;
     {** Number of times to repeat the starting arrow }
     property ArrowStartRepeat: integer read GetArrowStartRepeat write SetArrowStartRepeat;
     {** Number of times to repeat the ending arrow }
     property ArrowEndRepeat: integer read GetArrowEndRepeat write SetArrowEndRepeat;

  public {-------------------font style------------------------}
     {** Specifies the font to use. Unless the font renderer accept otherwise,
         the name is in human readable form, like 'Arial', 'Times New Roman', ... }
     FontName: string;
     {** Specifies the set of styles to be applied to the font.
         These can be ''fsBold'', ''fsItalic'', ''fsStrikeOut'', ''fsUnderline''.
         So the value [''fsBold'',''fsItalic''] means that the font must be bold and italic.
         See [[BGRABitmap Miscellaneous types|miscellaneous types]] }
     FontStyle: TFontStyles;

     {** Specifies the quality of rendering. Default value is ''fqSystem''.
         See [[BGRABitmap Miscellaneous types|miscellaneous types]] }
     FontQuality : TBGRAFontQuality;

     {** Specifies the rotation of the text, for functions that support text rotation.
         It is expressed in tenth of degrees, positive values going counter-clockwise. }
     FontOrientation: integer;

     {** Specifies how the font is vertically aligned relative to the start coordinate.
         See [[BGRABitmap Miscellaneous types|miscellaneous types]]}
     FontVerticalAnchor: TFontVerticalAnchor;

     {** Specifies the height of the font in pixels without taking into account
         additional line spacing. A negative value means that it is the
         full height instead (see below) }
     property FontHeight: integer Read GetFontHeight Write SetFontHeight;

     {** Specifies the height of the font in pixels, taking into account the
         additional line spacing defined for the font }
     property FontFullHeight: integer read GetFontFullHeight write SetFontFullHeight;

     {** Simplified property to specify the quality (see ''FontQuality'') }
     property FontAntialias: Boolean read GetFontAntialias write SetFontAntialias;
     {** Returns measurement for the current font in pixels }
     property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;

     {** Specifies the font renderer. When working with the LCL,
         by default it is an instance of ''TLCLFontRenderer'' of
         unit ''BGRAText''. Other renderers are provided in ''BGRATextFX''
         unit and ''BGRAVectorize'' unit. Additionally, ''BGRAFreeType''
         provides a renderer independent from the LCL.
       *
       * Once you assign a renderer, it will automatically be freed when
         the bitmap is freed. The renderers may provide additional styling
         for the font, not accessible with the properties in this class
       *
       * See [[BGRABitmap tutorial Font rendering|font rendering]]}
     property FontRenderer: TBGRACustomFontRenderer read GetFontRenderer write SetFontRenderer;

  public
     constructor Create; virtual; abstract; overload;
     constructor Create(AFPImage: TFPCustomImage); virtual; abstract; overload;
     constructor Create(ABitmap: TBitmap); virtual; abstract; overload;
     constructor Create(AWidth, AHeight: integer; Color: TColor); virtual; abstract; overload;
     constructor Create(AWidth, AHeight: integer; Color: TBGRAPixel); virtual; abstract; overload;
     constructor Create(AFilename: string); virtual; abstract; overload;
     constructor Create(AFilename: string; AIsUtf8Filename: boolean); virtual; abstract; overload;
     constructor Create(AStream: TStream); virtual; abstract; overload;

     function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; virtual; abstract; overload;
     function NewBitmap(AWidth, AHeight: integer; Color: TBGRAPixel): TBGRACustomBitmap; virtual; abstract; overload;
     function NewBitmap(Filename: string): TBGRACustomBitmap; virtual; abstract; overload;
     function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; virtual; abstract; overload;
     function NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap; virtual; abstract; overload;

     //there are UTF8 functions that are different from standard function as those
     //depend on TFPCustomImage that does not clearly handle UTF8
  {==== Load and save files ====}
     {** Load image from a file. ''filename'' is an ANSI string }
     procedure LoadFromFile(const filename: string); virtual;
     {** Load image from a file with the specified image reader. ''filename'' is an ANSI string }
     procedure LoadFromFile(const filename:String; Handler:TFPCustomImageReader); virtual;
     {** Load image from a file. ''filename'' is an UTF8 string }
     procedure LoadFromFileUTF8(const filenameUTF8: string); virtual;
     {** Load image from a file with the specified image reader. ''filename'' is an UTF8 string }
     procedure LoadFromFileUTF8(const filenameUTF8: string; AHandler: TFPCustomImageReader); virtual;
     {** Load image from a stream. Format is detected automatically }
     procedure LoadFromStream(Str: TStream); virtual; overload;
     {** Load image from a stream. The specified image reader is used }
     procedure LoadFromStream(Str: TStream; Handler: TFPCustomImageReader); virtual; overload;

     {** Save image to a file. The format is guessed from the file extension. ''filename'' is an ANSI string }
     procedure SaveToFile(const filename: string); virtual; overload;
     {** Save image to a file with the specified image writer. ''filename'' is an ANSI string }
     procedure SaveToFile(const filename: string; Handler:TFPCustomImageWriter); virtual; overload;
     {** Save image to a file. The format is guessed from the file extension. ''filename'' is an ANSI string }
     procedure SaveToFileUTF8(const filenameUTF8: string); virtual; overload;
     {** Save image to a file with the specified image writer. ''filename'' is an UTF8 string }
     procedure SaveToFileUTF8(const filenameUTF8: string; Handler:TFPCustomImageWriter); virtual; overload;

     {** Save image to a stream with the specified image writer }{inherited
     procedure SaveToStream (Str:TStream; Handler:TFPCustomImageWriter);
    }{** Save image to a stream in the specified image format }
     procedure SaveToStreamAs(Str: TStream; AFormat: TBGRAImageFormat); virtual;
     {** Save image to a stream in PNG format }
     procedure SaveToStreamAsPng(Str: TStream); virtual;

     procedure Serialize(AStream: TStream); virtual; abstract;
     procedure Deserialize(AStream: TStream); virtual; abstract;

     {** Gets the content of the specified device context }
     procedure LoadFromDevice(DC: System.THandle); virtual; abstract; overload;
     {** Gets the content from the specified rectangular area of a device context }
     procedure LoadFromDevice(DC: System.THandle; ARect: TRect); virtual; abstract; overload;
     {** Fills the content with a screenshot of the primary monitor }
     procedure TakeScreenshotOfPrimaryMonitor; virtual; abstract;
     {** Fills the content with a screenshot of the specified rectangular area of the desktop
         (it can be from any screen) }
     procedure TakeScreenshot(ARect: TRect); virtual; abstract;
     {** For more methods, see derived class [[TBGRABitmap class|TBGRABitmap]] }

     {Pixel functions}
     procedure SetPixel(x, y: int32or64; c: TColor); virtual; abstract; overload;
     procedure XorPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
     procedure SetPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
     procedure DrawPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract; overload;
     procedure DrawPixel(x, y: int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
     procedure DrawPixel(x, y: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
     procedure FastBlendPixel(x, y: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure ErasePixel(x, y: int32or64; alpha: byte); virtual; abstract;
     procedure AlphaPixel(x, y: int32or64; alpha: byte); virtual; abstract;
     function GetPixel(x, y: int32or64): TBGRAPixel; virtual; abstract; overload;
     function GetPixel256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract;
     function GetPixel(x, y: single; AResampleFilter: TResampleFilter = rfLinear; smoothBorder: boolean = true): TBGRAPixel; virtual; abstract; overload;
     function GetPixelCycle(x, y: int32or64): TBGRAPixel; virtual; overload;
     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;
     function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; virtual; abstract; overload;
     function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; virtual; abstract; overload;

     {Line primitives}
     procedure SetHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure DrawHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract; overload;
     procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); virtual; abstract; overload;
     procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload;
     procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure HorizLine(x,y,x2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode); overload;
     procedure HorizLine(x,y,x2: Int32or64; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
     procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; maxDiff: byte); virtual; abstract;
     procedure AlphaHorizLine(x, y, x2: int32or64; alpha: byte); virtual; abstract;

     procedure SetVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure AlphaVertLine(x, y, y2: int32or64; alpha: byte); virtual; abstract;
     procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure VertLine(x,y,y2: Int32or64; c: TBGRAPixel; ADrawMode: TDrawMode);

     {Shapes}
     procedure DrawPath(APath: IBGRAPath; c: TBGRAPixel; w: single); virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; texture: IBGRAScanner; w: single); virtual; abstract;

     procedure ArrowStartAsNone;
     procedure ArrowStartAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
     procedure ArrowStartAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
     procedure ArrowStartAsTail;

     procedure ArrowEndAsNone;
     procedure ArrowEndAsClassic(AFlipped: boolean = false; ACut: boolean = false; ARelativePenWidth: single = 1);
     procedure ArrowEndAsTriangle(ABackOffset: single = 0; ARounded: boolean = false; AHollow: boolean = false; AHollowPenWidth: single = 0.5);
     procedure ArrowEndAsTail;

     procedure DrawLine(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode= dmDrawWithTransparency); virtual; abstract;
     procedure DrawLineAntialias(x1, y1, x2, y2: integer; c: TBGRAPixel; DrawLastPixel: boolean); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: integer; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean; var DashPos: integer); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: single; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
     procedure DrawLineAntialias(x1, y1, x2, y2: single; texture: IBGRAScanner; w: single; Closed: boolean); virtual; abstract; overload;

     procedure DrawPolyLine(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode=dmDrawWithTransparency);
     procedure DrawPolyLineAntialias(const points: array of TPoint; c: TBGRAPixel; DrawLastPixel: boolean); virtual; overload;
     procedure DrawPolyLineAntialias(const points: array of TPoint; c1, c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean); virtual; overload;
     procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
     procedure DrawPolyLineAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
     procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; Closed: boolean); virtual; abstract; overload;
     procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;
     procedure DrawPolyLineAntialiasAutocycle(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
     procedure DrawPolygon(const points: array of TPoint; c: TBGRAPixel; ADrawMode: TDrawMode=dmDrawWithTransparency);
     procedure DrawPolygonAntialias(const points: array of TPoint; c: TBGRAPixel); overload;
     procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single); virtual; abstract; overload;
     procedure DrawPolygonAntialias(const points: array of TPointF; texture: IBGRAScanner; w: single); virtual; abstract; overload;
     procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); virtual; abstract; overload;

     procedure EraseLine(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract;
     procedure EraseLineAntialias(x1, y1, x2, y2: integer; alpha: byte; DrawLastPixel: boolean); virtual; abstract; overload;
     procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single); virtual; abstract; overload;
     procedure EraseLineAntialias(x1, y1, x2, y2: single; alpha: byte; w: single; Closed: boolean); virtual; abstract; overload;
     procedure ErasePolyLine(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
     procedure ErasePolyLineAntialias(const points: array of TPoint; alpha: byte; DrawLastPixel: boolean); overload;
     procedure ErasePolyLineAntialias(const points: array of TPointF; alpha: byte; w: single); virtual; abstract; overload;
     procedure ErasePolygonOutline(const points: array of TPoint; alpha: byte);
     procedure ErasePolygonOutlineAntialias(const points: array of TPoint; alpha: byte);

     procedure FillPath(APath: IBGRAPath; c: TBGRAPixel); virtual; abstract;
     procedure FillPath(APath: IBGRAPath; texture: IBGRAScanner); virtual; abstract;
     procedure ErasePath(APath: IBGRAPath; alpha: byte); virtual; abstract;

     procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
     procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); virtual; abstract; overload;
     procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); virtual; abstract; overload;
     procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; light1,light2,light3: word; TextureInterpolation: Boolean= True); virtual; abstract; overload;
     procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); virtual; abstract; overload;

     procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
     procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); virtual; abstract; overload;
     procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True);  virtual; abstract; overload;
     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;
     procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;
     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); virtual; abstract; overload;
     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); virtual; abstract; overload;

     procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel);  virtual; abstract; overload;
     procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); virtual; abstract; overload;
     procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); virtual; abstract; overload;
     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;
     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;

     procedure FillPoly(const points: array of TPointF; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
     procedure FillPoly(const points: array of TPointF; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
     procedure FillPolyAntialias(const points: array of TPointF; c: TBGRAPixel); virtual; abstract;
     procedure FillPolyAntialias(const points: array of TPointF; texture: IBGRAScanner); virtual; abstract;
     procedure ErasePoly(const points: array of TPointF; alpha: byte); virtual; abstract;
     procedure ErasePolyAntialias(const points: array of TPointF; alpha: byte); virtual; abstract;

     procedure FillShape(shape: TBGRACustomFillInfo; c: TBGRAPixel; drawmode: TDrawMode); virtual; abstract;
     procedure FillShape(shape: TBGRACustomFillInfo; texture: IBGRAScanner; drawmode: TDrawMode); virtual; abstract;
     procedure FillShapeAntialias(shape: TBGRACustomFillInfo; c: TBGRAPixel); virtual; abstract;
     procedure FillShapeAntialias(shape: TBGRACustomFillInfo; texture: IBGRAScanner); virtual; abstract;
     procedure EraseShape(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;
     procedure EraseShapeAntialias(shape: TBGRACustomFillInfo; alpha: byte); virtual; abstract;

     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single); virtual; abstract;
     procedure EllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner; w: single); virtual; abstract;
     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract;
     procedure FillEllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel); virtual; abstract;
     procedure FillEllipseAntialias(x, y, rx, ry: single; texture: IBGRAScanner); virtual; abstract;
     procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); virtual; abstract;
     procedure EraseEllipseAntialias(x, y, rx, ry: single; alpha: byte); virtual; abstract;

     procedure Arc(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; AColor: TBGRAPixel; w: single; ADrawChord: boolean; AFillColor: TBGRAPixel); overload;
     procedure Arc(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; AColor: TBGRAPixel; w: single; ADrawChord: boolean; AFillColor: TBGRAPixel); overload;
     procedure FillChord(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; AFillColor: TBGRAPixel); overload;
     procedure FillChord(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; AFillColor: TBGRAPixel); overload;
     procedure FillChord(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; texture: IBGRAScanner); overload;
     procedure FillChord(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; texture: IBGRAScanner); overload;
     procedure FillChordInRect(const ARect: TRect; StartAngleRad,EndAngleRad: Single; AFillColor: TBGRAPixel); overload;
     procedure FillChordInRect(const ARect: TRect; StartAngleRad,EndAngleRad: Single; texture: IBGRAScanner); overload;

     procedure Pie(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; AColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel); overload;
     procedure Pie(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; AColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel); overload;
     procedure FillPie(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; AFillColor: TBGRAPixel); overload;
     procedure FillPie(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; AFillColor: TBGRAPixel); overload;
     procedure FillPie(cx,cy,rx,ry: single; const StartPoint,EndPoint: TPointF; texture: IBGRAScanner); overload;
     procedure FillPie(cx,cy,rx,ry: single; StartAngleRad,EndAngleRad: Single; texture: IBGRAScanner); overload;
     procedure FillPieInRect(const ARect: TRect; StartAngleRad,EndAngleRad: Single; AFillColor: TBGRAPixel); overload;
     procedure FillPieInRect(const ARect: TRect; StartAngleRad,EndAngleRad: Single; texture: IBGRAScanner); overload;

     procedure Rectangle(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
     procedure Rectangle(x, y, x2, y2: integer; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
     procedure Rectangle(x, y, x2, y2: integer; c: TColor); virtual; overload;
     procedure Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
     procedure Rectangle(r: TRect; BorderColor, FillColor: TBGRAPixel; mode: TDrawMode); virtual;overload;
     procedure Rectangle(r: TRect; c: TColor); virtual; overload;
     procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single); virtual; overload;
     procedure RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); virtual; abstract; overload;
     procedure RectangleAntialias(x, y, x2, y2: single; texture: IBGRAScanner; w: single); virtual; abstract; overload;

     procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor, FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
     procedure RoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; abstract; overload;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure FillRoundRect(X1, Y1, X2, Y2: integer; DX, DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;
     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure FillRoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; options: TRoundRectangleOptions = []); virtual; abstract;
     procedure EraseRoundRectAntialias(x,y,x2,y2,rx,ry: single; alpha: byte; options: TRoundRectangleOptions = []); virtual; abstract;

     procedure EllipseInRect(r: TRect; BorderColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
     procedure EllipseInRect(r: TRect; BorderColor,FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual; overload;
     procedure FillEllipseInRect(r: TRect; FillColor: TBGRAPixel; ADrawMode: TDrawMode = dmDrawWithTransparency); virtual;

     procedure FillRect(r: TRect; c: TColor); virtual; overload;
     procedure FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode); virtual; overload;
     procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode); virtual; overload;
     procedure FillRect(x, y, x2, y2: integer; c: TColor); virtual; overload;
     procedure FillRect(x, y, x2, y2: integer; c: TBGRAPixel; mode: TDrawMode); virtual; abstract; overload;
     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
     procedure FillRectAntialias(x, y, x2, y2: single; c: TBGRAPixel); virtual; abstract;
     procedure FillRectAntialias(x, y, x2, y2: single; texture: IBGRAScanner); virtual; abstract;
     procedure EraseRectAntialias(x, y, x2, y2: single; alpha: byte); virtual; abstract;
     procedure AlphaFillRect(x, y, x2, y2: integer; alpha: byte); virtual; abstract;

     procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract; overload;
     procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract; overload;
     procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); virtual; abstract; overload;
     procedure TextOutCurved(ACursor: TBGRACustomPathCursor; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); virtual; abstract; overload;
     procedure TextOutCurved(APath: IBGRAPath; sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); virtual; overload;
     procedure TextOutCurved(APath: IBGRAPath; sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); virtual; overload;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); virtual; abstract;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); virtual; abstract;
     procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); virtual; abstract; overload;
     procedure TextRect(ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); virtual; abstract; overload;
     function TextSize(sUTF8: string): TSize; virtual; abstract;

     { Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c or texture is used to fill the text.
       The value of FontOrientation is taken into account, so that the text may be rotated. }
     procedure TextOut(x, y: single; sUTF8: string; c: TBGRAPixel); virtual; overload;
     procedure TextOut(x, y: single; sUTF8: string; c: TColor); virtual; overload;
     procedure TextOut(x, y: single; sUTF8: string; texture: IBGRAScanner); virtual; overload;

     { Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
       The position depends on the specified horizontal alignment halign and vertical alignement valign.
       The color c or texture is used to fill the text. No rotation is applied. }
     procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); virtual; overload;
     procedure TextRect(ARect: TRect; sUTF8: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); virtual; overload;

     {Spline}
     function ComputeClosedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
     function ComputeOpenedSpline(const APoints: array of TPointF; AStyle: TSplineStyle): ArrayOfTPointF; virtual; abstract;
     function ComputeBezierCurve(const curve: TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
     function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;
     function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; virtual; abstract;
     function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; virtual; abstract;

     function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;
     function ComputeWidePolyline(const points: array of TPointF; w: single; Closed: boolean): ArrayOfTPointF; virtual; abstract;
     function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;

     function ComputeEllipse(x,y,rx,ry: single): ArrayOfTPointF; deprecated;
     function ComputeEllipse(x,y,rx,ry,w: single): ArrayOfTPointF; deprecated;
     function ComputeEllipseContour(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputeEllipseBorder(x,y,rx,ry,w: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputeArc65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputeArcRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputePie65536(x,y,rx,ry: single; start65536,end65536: word; quality: single = 1): ArrayOfTPointF; virtual; abstract;
     function ComputePieRad(x,y,rx,ry: single; startRad,endRad: single; quality: single = 1): ArrayOfTPointF; virtual; abstract;

     {Filling}
     procedure FillTransparent; virtual;
     procedure NoClip; virtual; abstract;
     procedure ApplyGlobalOpacity(alpha: byte); virtual; abstract; overload;
     procedure ApplyGlobalOpacity(ARect: TRect; alpha: byte); virtual; abstract; overload;
     procedure Fill(c: TColor); virtual; overload;
     procedure Fill(c: TBGRAPixel); virtual; overload;
     procedure Fill(texture: IBGRAScanner; mode: TDrawMode); virtual; abstract; overload;
     procedure Fill(texture: IBGRAScanner); virtual; abstract; overload;
     procedure Fill(c: TBGRAPixel; start, Count: integer); virtual; abstract; overload;
     procedure DrawPixels(c: TBGRAPixel; start, Count: integer); virtual; abstract;
     procedure AlphaFill(alpha: byte); virtual; overload;
     procedure AlphaFill(alpha: byte; start, Count: integer); virtual; abstract; overload;
     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel); virtual; overload;
     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner); virtual; overload;
     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ADrawMode: TDrawMode); virtual; abstract; overload;
     procedure FillMask(x,y: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ADrawMode: TDrawMode); virtual; abstract; overload;
     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); virtual; abstract; overload;
     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); virtual; abstract; overload;
     procedure ReplaceColor(before, after: TColor); virtual; abstract; overload;
     procedure ReplaceColor(before, after: TBGRAPixel); virtual; abstract; overload;
     procedure ReplaceColor(ARect: TRect; before, after: TColor); virtual; abstract; overload;
     procedure ReplaceColor(ARect: TRect; before, after: TBGRAPixel); virtual; abstract; overload;
     procedure ReplaceTransparent(after: TBGRAPixel); virtual; abstract; overload;
     procedure ReplaceTransparent(ABounds: TRect; after: TBGRAPixel); virtual; abstract; overload;
     procedure FloodFill(X, Y: integer; Color: TBGRAPixel;
       mode: TFloodfillMode; Tolerance: byte = 0); virtual;
     procedure ParallelFloodFill(X, Y: integer; Dest: TBGRACustomBitmap; Color: TBGRAPixel;
       mode: TFloodfillMode; Tolerance: byte = 0); virtual; abstract;
     procedure GradientFill(x, y, x2, y2: integer; c1, c2: TBGRAPixel;
       gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
       gammaColorCorrection: boolean = True; Sinus: Boolean=False); virtual; abstract;
     procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
       gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
       Sinus: Boolean=False); virtual; abstract;
     function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
                AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; virtual; abstract;

     {Canvas drawing functions}
     procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
       AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
     procedure DataDrawOpaque(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
       ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
     procedure GetImageFromCanvas(CanvasSource: TCanvas; x, y: integer); virtual; abstract;
     procedure Draw(ACanvas: TCanvas; x, y: integer; Opaque: boolean = True); virtual; abstract;
     procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); virtual; abstract;
     procedure DrawPart(ARect: TRect; ACanvas: TCanvas; x, y: integer; Opaque: boolean); virtual;
     function GetPart(ARect: TRect): TBGRACustomBitmap; virtual; abstract;
     function GetPtrBitmap(Top,Bottom: Integer): TBGRACustomBitmap; virtual; abstract;
     procedure InvalidateBitmap; virtual; abstract;         //call if you modify with Scanline
     procedure LoadFromBitmapIfNeeded; virtual; abstract;   //call to ensure that bitmap data is up to date

     {BGRA bitmap functions}
     procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
     procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); virtual; abstract;
     procedure PutImage(x, y: integer; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
     procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); virtual; abstract;
     procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
     procedure PutImagePart(x,y: integer; Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte = 255);
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte=255); overload;
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); virtual; abstract; overload;
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
     function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap): TRect;
     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;
     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;
     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;
     procedure PutImageAngle(x,y: single; Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter; imageCenterX: single = 0; imageCenterY: single = 0; AOpacity: Byte=255; ARestoreOffsetAfterRotation: boolean = false); overload;
     procedure ComputeImageAngleAxes(x,y,w,h,angle: single; imageCenterX,imageCenterY: single; ARestoreOffsetAfterRotation: boolean;
       out Origin,HAxis,VAxis: TPointF);
     function GetImageAngleBounds(x,y: single; Source: TBGRACustomBitmap; angle: single; imageCenterX: single = 0; imageCenterY: single = 0; ARestoreOffsetAfterRotation: boolean = false): TRect;
     procedure BlendImage(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation); virtual; abstract;
     procedure BlendImageOver(x, y: integer; Source: TBGRACustomBitmap; operation: TBlendOperation; AOpacity: byte = 255;
         ALinearBlend: boolean = false); virtual; abstract;
     function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; virtual; abstract;
     function Equals(comp: TBGRACustomBitmap): boolean; virtual; abstract;
     function Equals(comp: TBGRAPixel): boolean; virtual; abstract;
     function Resample(newWidth, newHeight: integer;
       mode: TResampleMode = rmFineResample): TBGRACustomBitmap; virtual; abstract;
     procedure VerticalFlip; virtual; overload;
     procedure VerticalFlip(ARect: TRect); virtual; abstract; overload;
     procedure HorizontalFlip; virtual; overload;
     procedure HorizontalFlip(ARect: TRect); virtual; abstract; overload;
     function RotateCW: TBGRACustomBitmap; virtual; abstract;
     function RotateCCW: TBGRACustomBitmap; virtual; abstract;
     procedure Negative; virtual; abstract;
     procedure NegativeRect(ABounds: TRect); virtual; abstract;
     procedure LinearNegative; virtual; abstract;
     procedure LinearNegativeRect(ABounds: TRect); virtual; abstract;
     procedure InplaceGrayscale; virtual; abstract;
     procedure InplaceGrayscale(ABounds: TRect); virtual; abstract;
     procedure ConvertToLinearRGB; virtual; abstract;
     procedure ConvertFromLinearRGB; virtual; abstract;
     procedure SwapRedBlue; virtual; abstract;
     procedure GrayscaleToAlpha; virtual; abstract;
     procedure AlphaToGrayscale; virtual; abstract;
     procedure ApplyMask(mask: TBGRACustomBitmap); overload;
     procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect); overload;
     procedure ApplyMask(mask: TBGRACustomBitmap; ARect: TRect; AMaskRectTopLeft: TPoint); virtual; abstract; overload;
     function GetImageBounds(Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; virtual; abstract;
     function GetImageBounds(Channels: TChannels; ANothingValue: Byte = 0): TRect; virtual; abstract;
     function GetDifferenceBounds(ABitmap: TBGRACustomBitmap): TRect; virtual; abstract;
     function MakeBitmapCopy(BackgroundColor: TColor): TBitmap; virtual; abstract;

     {Filters}
     function FilterSmartZoom3(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
     function FilterMedian(Option: TMedianOption): TBGRACustomBitmap; virtual; abstract;
     function FilterSmooth: TBGRACustomBitmap; virtual; abstract;
     function FilterSharpen(Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
     function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRACustomBitmap; virtual; abstract;
     function FilterContour: TBGRACustomBitmap; virtual; abstract;
     function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRACustomBitmap; virtual; abstract;
     function FilterBlurRadial(radius: integer;
       blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
     function FilterBlurRadial(ABounds: TRect; radius: integer;
       blurType: TRadialBlurType): TBGRACustomBitmap; virtual; abstract;
     function FilterBlurMotion(distance: integer; angle: single;
       oriented: boolean): TBGRACustomBitmap; virtual; abstract;
     function FilterBlurMotion(ABounds: TRect; distance: integer; angle: single;
       oriented: boolean): TBGRACustomBitmap; virtual; abstract;
     function FilterCustomBlur(mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
     function FilterCustomBlur(ABounds: TRect; mask: TBGRACustomBitmap): TBGRACustomBitmap; virtual; abstract;
     function FilterEmboss(angle: single): TBGRACustomBitmap; virtual; abstract;
     function FilterEmboss(angle: single; ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean): TBGRACustomBitmap; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRACustomBitmap; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRACustomBitmap; virtual; abstract;
     function FilterGrayscale: TBGRACustomBitmap; virtual; abstract;
     function FilterGrayscale(ABounds: TRect): TBGRACustomBitmap; virtual; abstract;
     function FilterNormalize(eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
     function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRACustomBitmap; virtual; abstract;
     function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRACustomBitmap; virtual; abstract;
     function FilterSphere: TBGRACustomBitmap; virtual; abstract;
     function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
     function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRACustomBitmap; virtual; abstract;
     function FilterCylinder: TBGRACustomBitmap; virtual; abstract;
     function FilterPlane: TBGRACustomBitmap; virtual; abstract;

     //IBGRAScanner
     function ScanAtInteger(X,Y: integer): TBGRAPixel; virtual; abstract;
     procedure ScanMoveTo(X,Y: Integer); virtual; abstract;
     function ScanNextPixel: TBGRAPixel; virtual; abstract;
     function ScanAt(X,Y: Single): TBGRAPixel; virtual; abstract;
     procedure ScanPutPixels(pdest: PBGRAPixel; count: integer; mode: TDrawMode); virtual;
     function IsScanPutPixelsDefined: boolean; virtual;

  protected
     //interface
     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};
     function _AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
     function _Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};

  end;

type
  TBGRABitmapAny = class of TBGRACustomBitmap;  //used to create instances of the same type (see NewBitmap)

var
  BGRABitmapFactory : TBGRABitmapAny;
{$ENDIF}

{$IFDEF INCLUDE_IMPLEMENTATION}
{$UNDEF INCLUDE_IMPLEMENTATION}
{ TBGRACustomScanner }
{ The abstract class record the position so that a derived class
  need only to redefine ScanAt }

function TBGRACustomScanner.ScanAtInteger(X, Y: integer): TBGRAPixel;
begin
  result := ScanAt(X,Y);
end;

procedure TBGRACustomScanner.ScanMoveTo(X, Y: Integer);
begin
  FCurX := X;
  FCurY := Y;
end;

{ Call ScanAt to determine pixel value }
function TBGRACustomScanner.ScanNextPixel: TBGRAPixel;
begin
  result := ScanAt(FCurX,FCurY);
  Inc(FCurX);
end;

{$hints off}
procedure TBGRACustomScanner.ScanPutPixels(pdest: PBGRAPixel; count: integer;
  mode: TDrawMode);
begin
  //do nothing
end;
{$hints on}

function TBGRACustomScanner.IsScanPutPixelsDefined: boolean;
begin
  result := false;
end;

{ Interface gateway }
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};
begin
  if GetInterface(iid, obj) then
    Result := S_OK
  else
    Result := longint(E_NOINTERFACE);
end;

{ There is no automatic reference counting, but it is compulsory to define these functions }
function TBGRACustomScanner._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
begin
  result := 0;
end;

function TBGRACustomScanner._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
begin
  result := 0;
end;

{********************** End of TBGRACustomScanner **************************}

{ TBGRACustomBitmap }

function TBGRACustomBitmap.GetFontAntialias: Boolean;
begin
  result := FontQuality <> fqSystem;
end;

procedure TBGRACustomBitmap.SetFontAntialias(const AValue: Boolean);
begin
  if AValue and not FontAntialias then
    FontQuality := fqFineAntialiasing
  else if not AValue and (FontQuality <> fqSystem) then
    FontQuality := fqSystem;
end;

procedure TBGRACustomBitmap.InternalArc(cx, cy, rx, ry: single;
  const StartPoint, EndPoint: TPointF; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions;
  ADrawChord: boolean; ATexture: IBGRAScanner);
var angle1,angle2: single;
begin
  if (rx = 0) or (ry = 0) then exit;
  angle1 := arctan2(-(StartPoint.y-cy)/ry,(StartPoint.x-cx)/rx);
  angle2 := arctan2(-(EndPoint.y-cy)/ry,(EndPoint.x-cx)/rx);
  if angle1 = angle2 then angle2 := angle1+2*Pi;
  InternalArc(cx,cy,rx,ry, angle1,angle2,
              ABorderColor,w,AFillColor, AOptions, ADrawChord, ATexture);
end;

procedure TBGRACustomBitmap.InternalArcInRect(r: TRect; StartAngleRad,
  EndAngleRad: Single; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; AOptions: TArcOptions;
  ADrawChord: boolean; ATexture: IBGRAScanner);
var
  temp: LongInt;
begin
  if r.right = r.left then exit;
  if r.bottom = r.top then exit;
  if r.right < r.left then
  begin
    temp := r.left;
    r.left := r.right;
    r.right := temp;
  end;
  if r.Bottom < r.Top then
  begin
    temp := r.Top;
    r.Top := r.Bottom;
    r.Bottom := temp;
  end;
  InternalArc((r.left+r.right-1)/2,(r.top+r.bottom-1)/2,
             (r.right-r.left-1)/2,(r.bottom-r.top-1)/2,
             StartAngleRad,EndAngleRad,
             ABorderColor,w,AFillColor,
             AOptions, ADrawChord, ATexture);
end;

procedure TBGRACustomBitmap.InternalFillArcInRect(r: TRect; StartAngleRad,
  EndAngleRad: Single; AFillColor: TBGRAPixel; AOptions: TArcOptions;
  ATexture: IBGRAScanner);
var
  temp: LongInt;
begin
  if r.right = r.left then exit;
  if r.bottom = r.top then exit;
  if r.right < r.left then
  begin
    temp := r.left;
    r.left := r.right;
    r.right := temp;
  end;
  if r.Bottom < r.Top then
  begin
    temp := r.Top;
    r.Top := r.Bottom;
    r.Bottom := temp;
  end;
  InternalArc((r.left+r.right-1)/2,(r.top+r.bottom-1)/2,
             (r.right-r.left)/2,(r.bottom-r.top)/2,
             StartAngleRad,EndAngleRad,
             BGRAPixelTransparent,0,AFillColor,
             AOptions, False, ATexture);
end;

{ These declaration make sure that these methods are virtual }
procedure TBGRACustomBitmap.LoadFromFile(const filename: string);
begin
  LoadFromFileUTF8(SysToUtf8(filename));
end;

procedure TBGRACustomBitmap.LoadFromFile(const filename: String;
  Handler: TFPCustomImageReader);
begin
  LoadFromFileUTF8(SysToUtf8(filename),Handler);
end;

procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string);
var
  Stream: TStream;
  format: TBGRAImageFormat;
  reader: TFPCustomImageReader;
begin
  stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
  try
    format := DetectFileFormat(Stream, ExtractFileExt(filenameUTF8));
    reader := CreateBGRAImageReader(format);
    try
      LoadFromStream(stream, reader);
    finally
      reader.Free;
    end;
  finally
    ClearTransparentPixels;
    stream.Free;
  end;
end;

procedure TBGRACustomBitmap.LoadFromFileUTF8(const filenameUTF8: string;
  AHandler: TFPCustomImageReader);
var
  Stream: TStream;
begin
  stream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(stream, AHandler);
  finally
    ClearTransparentPixels;
    stream.Free;
  end;
end;

procedure TBGRACustomBitmap.SaveToFile(const filename: string);
begin
  SaveToFileUTF8(SysToUtf8(filename));
end;

procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string);
var
  writer: TFPCustomImageWriter;
  format: TBGRAImageFormat;
begin
  format := SuggestImageFormat(filenameUTF8);
  if (format = ifXPixMap) and (Width*Height > 32768) then //xpm is slow so avoid big images
    raise exception.Create('Image is too big to be saved as XPM');
  writer := CreateBGRAImageWriter(Format, HasTransparentPixels);
  try
    SaveToFileUTF8(filenameUTF8, writer);
  finally
    writer.free;
  end;
end;

procedure TBGRACustomBitmap.SaveToFile(const filename: string;
  Handler: TFPCustomImageWriter);
begin
  SaveToFileUTF8(SysToUtf8(filename),Handler);
end;

procedure TBGRACustomBitmap.SaveToFileUTF8(const filenameUTF8: string;
  Handler: TFPCustomImageWriter);
var
  stream: TFileStreamUTF8;
begin
   stream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
   try
     SaveToStream(stream, Handler);
   finally
     stream.Free;
   end;
end;

procedure TBGRACustomBitmap.SaveToStreamAsPng(Str: TStream);
var writer: TFPCustomImageWriter;
begin
  writer := CreateBGRAImageWriter(ifPNG, HasTransparentPixels);
  SaveToStream(Str,writer);
  writer.Free;
end;

procedure TBGRACustomBitmap.SaveToStreamAs(Str: TStream;
  AFormat: TBGRAImageFormat);
var handler: TFPCustomImageWriter;
begin
  handler := CreateBGRAImageWriter(AFormat, HasTransparentPixels);
  try
    SaveToStream(Str, handler)
  finally
    handler.Free;
  end;
end;

procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; c: TBGRAPixel;
  ADrawMode: TDrawMode);
begin
  case ADrawMode of
  dmSet: SetPixel(x,y,c);
  dmSetExceptTransparent: if c.alpha = 255 then SetPixel(x,y,c);
  dmLinearBlend: FastBlendPixel(x,y,c);
  dmDrawWithTransparency: DrawPixel(x,y,c);
  dmXor: XorPixel(x,y,c);
  end;
end;

procedure TBGRACustomBitmap.LoadFromStream(Str: TStream);
var
  format: TBGRAImageFormat;
  reader: TFPCustomImageReader;
begin
  format := DetectFileFormat(Str);
  reader := CreateBGRAImageReader(format);
  try
    LoadFromStream(Str,reader);
  finally
    reader.Free;
  end;
end;

{ LoadFromStream uses TFPCustomImage routine, which uses
  Colors property to access pixels. That's why the
  FP drawing mode is temporarily changed to load
  bitmaps properly }
procedure TBGRACustomBitmap.LoadFromStream(Str: TStream;
  Handler: TFPCustomImageReader);
var
  OldDrawMode: TDrawMode;
begin
  OldDrawMode := CanvasDrawModeFP;
  CanvasDrawModeFP := dmSet;
  try
    inherited LoadFromStream(Str, Handler);
  finally
    CanvasDrawModeFP := OldDrawMode;
  end;
end;

{ Look for a pixel considering the bitmap is repeated in both directions }
function TBGRACustomBitmap.GetPixelCycle(x, y: int32or64): TBGRAPixel;
begin
  if (Width = 0) or (Height = 0) then
    Result := BGRAPixelTransparent
  else
    Result := (Scanline[PositiveMod(y,Height)] + PositiveMod(x,Width))^;
end;

procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64;
  texture: IBGRAScanner);
begin
  HorizLine(x,y,x2,texture,dmDrawWithTransparency);
end;

procedure TBGRACustomBitmap.HorizLine(x, y, x2: Int32or64; c: TBGRAPixel;
  ADrawMode: TDrawMode);
begin
  case ADrawMode of
    dmSet: SetHorizLine(x,y,x2,c);
    dmSetExceptTransparent: if c.alpha = 255 then SetHorizLine(x,y,x2,c);
    dmXor: XorHorizLine(x,y,x2,c);
    dmLinearBlend: FastBlendHorizLine(x,y,x2,c);
    dmDrawWithTransparency: DrawHorizLine(x,y,x2,c);
  end;
end;

procedure TBGRACustomBitmap.VertLine(x, y, y2: Int32or64; c: TBGRAPixel;
  ADrawMode: TDrawMode);
begin
  case ADrawMode of
    dmSet: SetVertLine(x,y,y2,c);
    dmSetExceptTransparent: if c.alpha = 255 then SetVertLine(x,y,y2,c);
    dmXor: XorVertLine(x,y,y2,c);
    dmLinearBlend: FastBlendVertLine(x,y,y2,c);
    dmDrawWithTransparency: DrawVertLine(x,y,y2,c);
  end;
end;

procedure TBGRACustomBitmap.ArrowStartAsNone;
begin
  SetArrowStart(asNone);
end;

procedure TBGRACustomBitmap.ArrowStartAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
var join: TPenJoinStyle;
begin
  if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
  if ACut then
  begin
    if AFlipped then
      SetArrowStart(asFlippedCut,join,ARelativePenWidth)
    else
      SetArrowStart(asCut,join,ARelativePenWidth)
  end
  else
  begin
    if AFlipped then
      SetArrowStart(asFlipped,join,ARelativePenWidth)
    else
      SetArrowStart(asNormal,join,ARelativePenWidth)
  end;
end;

procedure TBGRACustomBitmap.ArrowStartAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
  AHollowPenWidth: single);
var join: TPenJoinStyle;
begin
  if ARounded then join := pjsRound else join := pjsMiter;
  if AHollow then
    SetArrowStart(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
  else
    SetArrowStart(asTriangle, join,1,ABackOffset);
end;

procedure TBGRACustomBitmap.ArrowStartAsTail;
begin
  SetArrowStart(asTail);
end;

procedure TBGRACustomBitmap.ArrowEndAsNone;
begin
  SetArrowEnd(asNone);
end;

procedure TBGRACustomBitmap.ArrowEndAsClassic(AFlipped: boolean; ACut: boolean; ARelativePenWidth: single);
var join: TPenJoinStyle;
begin
  if (LineCap = pecRound) and not ACut then join := pjsRound else join := pjsMiter;
  if ACut then
  begin
    if AFlipped then
      SetArrowEnd(asFlippedCut,join,ARelativePenWidth)
    else
      SetArrowEnd(asCut,join,ARelativePenWidth)
  end
  else
  begin
    if AFlipped then
      SetArrowEnd(asFlipped,join,ARelativePenWidth)
    else
      SetArrowEnd(asNormal,join,ARelativePenWidth)
  end;
end;

procedure TBGRACustomBitmap.ArrowEndAsTriangle(ABackOffset: single; ARounded: boolean; AHollow: boolean;
  AHollowPenWidth: single);
var join: TPenJoinStyle;
begin
  if ARounded then join := pjsRound else join := pjsMiter;
  if AHollow then
    SetArrowEnd(asHollowTriangle, join,AHollowPenWidth, ABackOffset)
  else
    SetArrowEnd(asTriangle, join,1, ABackOffset);
end;

procedure TBGRACustomBitmap.ArrowEndAsTail;
begin
  SetArrowEnd(asTail);
end;

procedure TBGRACustomBitmap.DrawPolyLine(const points: array of TPoint;
  c: TBGRAPixel; DrawLastPixel: boolean; ADrawMode: TDrawMode);
var i: integer;
begin
   if length(points) = 1 then
   begin
     if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c,ADrawMode);
   end
   else
     for i := 0 to high(points)-1 do
       DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1),ADrawMode);
end;

{ Pixel polylines are constructed by concatenation }
procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint;
  c: TBGRAPixel; DrawLastPixel: boolean);
var i: integer;
begin
   if length(points) = 1 then
   begin
     if DrawLastPixel then DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
   end
   else
     for i := 0 to high(points)-1 do
       DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,DrawLastPixel and (i=high(points)-1));
end;

procedure TBGRACustomBitmap.DrawPolyLineAntialias(const points: array of TPoint; c1,
  c2: TBGRAPixel; dashLen: integer; DrawLastPixel: boolean);
var i: integer;
  DashPos: integer;
begin
   DashPos := 0;
   if length(points) = 1 then
   begin
     if DrawLastPixel then DrawPixel(points[0].x,points[0].y,c1);
   end
   else
     for i := 0 to high(points)-1 do
       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);
end;

procedure TBGRACustomBitmap.DrawPolygon(const points: array of TPoint;
  c: TBGRAPixel; ADrawMode: TDrawMode);
var i: integer;
begin
   if length(points) = 1 then
   begin
     DrawPixel(points[0].x,points[0].y,c,ADrawMode);
   end
   else
   begin
     for i := 0 to high(points)-1 do
       DrawLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false,ADrawMode);
     DrawLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false,ADrawMode);
   end;
end;

procedure TBGRACustomBitmap.DrawPolygonAntialias(const points: array of TPoint;
  c: TBGRAPixel);
var i: integer;
begin
   if length(points) = 1 then
   begin
     DrawLineAntialias(points[0].x,points[0].y,points[0].x,points[0].y,c,true);
   end
   else
   begin
     for i := 0 to high(points)-1 do
       DrawLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,c,false);
     DrawLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,c,false);
   end;
end;

procedure TBGRACustomBitmap.ErasePolyLine(const points: array of TPoint; alpha: byte;
  DrawLastPixel: boolean);
var i: integer;
begin
   if length(points) = 1 then
   begin
     if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
   end
   else
     for i := 0 to high(points)-1 do
       EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
end;

procedure TBGRACustomBitmap.ErasePolyLineAntialias(
  const points: array of TPoint; alpha: byte; DrawLastPixel: boolean);
var i: integer;
begin
   if length(points) = 1 then
   begin
     if DrawLastPixel then ErasePixel(points[0].x,points[0].y,alpha);
   end
   else
     for i := 0 to high(points)-1 do
       EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,DrawLastPixel and (i=high(points)-1));
end;

procedure TBGRACustomBitmap.ErasePolygonOutline(const points: array of TPoint;
  alpha: byte);
var i: integer;
begin
   if length(points) = 1 then
   begin
     ErasePixel(points[0].x,points[0].y,alpha);
   end
   else
   begin
     for i := 0 to high(points)-1 do
       EraseLine(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
     EraseLine(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
   end;
end;

procedure TBGRACustomBitmap.ErasePolygonOutlineAntialias(
  const points: array of TPoint; alpha: byte);
var i: integer;
begin
   if length(points) = 1 then
   begin
     ErasePixel(points[0].x,points[0].y,alpha);
   end
   else
   begin
     for i := 0 to high(points)-1 do
       EraseLineAntialias(points[i].x,points[i].Y,points[i+1].x,points[i+1].y,alpha,false);
     EraseLineAntialias(points[high(points)].x,points[high(points)].Y,points[0].x,points[0].y,alpha,false);
   end;
end;

procedure TBGRACustomBitmap.Arc(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; AColor: TBGRAPixel; w: single; ADrawChord: boolean;
  AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,AColor,w,AFillColor,[aoFillPath],ADrawChord);
end;

procedure TBGRACustomBitmap.Arc(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; AColor: TBGRAPixel; w: single; ADrawChord: boolean;
  AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,AColor,w,AFillColor,[aoFillPath],ADrawChord);
end;

procedure TBGRACustomBitmap.FillChord(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,BGRAPixelTransparent,0,AFillColor,[aoFillPath]);
end;

procedure TBGRACustomBitmap.FillChord(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,BGRAPixelTransparent,0,AFillColor,[aoFillPath]);
end;

procedure TBGRACustomBitmap.FillChord(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; texture: IBGRAScanner);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,BGRAPixelTransparent,0,BGRAWhite,[aoFillPath],False,texture);
end;

procedure TBGRACustomBitmap.FillChord(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; texture: IBGRAScanner);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,BGRAPixelTransparent,0,BGRAWhite,[aoFillPath],False,texture);
end;

procedure TBGRACustomBitmap.FillChordInRect(const ARect: TRect; StartAngleRad,
  EndAngleRad: Single; AFillColor: TBGRAPixel);
begin
  InternalFillArcInRect(ARect,StartAngleRad,EndAngleRad,AFillColor,[aoFillPath]);
end;

procedure TBGRACustomBitmap.FillChordInRect(const ARect: TRect; StartAngleRad,
  EndAngleRad: Single; texture: IBGRAScanner);
begin
  InternalFillArcInRect(ARect,StartAngleRad,EndAngleRad,BGRAWhite,[aoFillPath],texture);
end;

procedure TBGRACustomBitmap.Pie(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; AColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,AColor,w,AFillColor,[aoFillPath,aoPie]);
end;

procedure TBGRACustomBitmap.Pie(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; AColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,AColor,w,AFillColor,[aoFillPath,aoPie]);
end;

procedure TBGRACustomBitmap.FillPie(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,BGRAPixelTransparent,0,AFillColor,[aoFillPath,aoPie]);
end;

procedure TBGRACustomBitmap.FillPie(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; AFillColor: TBGRAPixel);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,BGRAPixelTransparent,0,AFillColor,[aoFillPath,aoPie]);
end;

procedure TBGRACustomBitmap.FillPie(cx, cy, rx, ry: single; const StartPoint,
  EndPoint: TPointF; texture: IBGRAScanner);
begin
  InternalArc(cx,cy,rx,ry,StartPoint,EndPoint,BGRAPixelTransparent,0,BGRAWhite,[aoFillPath,aoPie],False,texture);
end;

procedure TBGRACustomBitmap.FillPie(cx, cy, rx, ry: single; StartAngleRad,
  EndAngleRad: Single; texture: IBGRAScanner);
begin
  InternalArc(cx,cy,rx,ry,StartAngleRad,EndAngleRad,BGRAPixelTransparent,0,BGRAWhite,[aoFillPath,aoPie],False,texture);
end;

procedure TBGRACustomBitmap.FillPieInRect(const ARect: TRect; StartAngleRad,
  EndAngleRad: Single; AFillColor: TBGRAPixel);
begin
  InternalFillArcInRect(ARect,StartAngleRad,EndAngleRad,AFillColor,[aoFillPath,aoPie]);
end;

procedure TBGRACustomBitmap.FillPieInRect(const ARect: TRect; StartAngleRad,
  EndAngleRad: Single; texture: IBGRAScanner);
begin
  InternalFillArcInRect(ARect,StartAngleRad,EndAngleRad,BGRAWhite,[aoFillPath,aoPie],texture);
end;

{ Following functions are defined for convenience }
procedure TBGRACustomBitmap.Rectangle(x, y, x2, y2: integer; c: TColor);
begin
  Rectangle(x, y, x2, y2, ColorToBGRA(c), dmSet);
end;

procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TBGRAPixel; mode: TDrawMode
  );
begin
  Rectangle(r.left, r.top, r.right, r.bottom, c, mode);
end;

procedure TBGRACustomBitmap.Rectangle(r: TRect; BorderColor,
  FillColor: TBGRAPixel; mode: TDrawMode);
begin
  Rectangle(r.left, r.top, r.right, r.bottom, BorderColor, FillColor, mode);
end;

procedure TBGRACustomBitmap.Rectangle(r: TRect; c: TColor);
begin
  Rectangle(r.left, r.top, r.right, r.bottom, c);
end;

procedure TBGRACustomBitmap.RectangleAntialias(x, y, x2, y2: single;
  c: TBGRAPixel; w: single);
begin
  RectangleAntialias(x, y, x2, y2, c, w, BGRAPixelTransparent);
end;

procedure TBGRACustomBitmap.FillRoundRect(X1, Y1, X2, Y2: integer; DX,
  DY: integer; FillColor: TBGRAPixel; ADrawMode: TDrawMode);
begin
  RoundRect(X1,Y1,X2,Y2,DX,DY,FillColor,FillColor,ADrawMode);
end;

procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor: TBGRAPixel;
  ADrawMode: TDrawMode);
begin
  RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,ADrawMode);
end;

procedure TBGRACustomBitmap.EllipseInRect(r: TRect; BorderColor,
  FillColor: TBGRAPixel; ADrawMode: TDrawMode);
begin
  RoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),BorderColor,FillColor,ADrawMode);
end;

procedure TBGRACustomBitmap.FillEllipseInRect(r: TRect; FillColor: TBGRAPixel;
  ADrawMode: TDrawMode);
begin
  FillRoundRect(r.left,r.top,r.right,r.bottom,abs(r.right-r.left),abs(r.bottom-r.top),FillColor,ADrawMode);
end;

procedure TBGRACustomBitmap.FillRect(r: TRect; c: TColor);
begin
  FillRect(r.Left, r.top, r.right, r.bottom, c);
end;

procedure TBGRACustomBitmap.FillRect(r: TRect; c: TBGRAPixel; mode: TDrawMode);
begin
  FillRect(r.Left, r.top, r.right, r.bottom, c, mode);
end;

procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
  mode: TDrawMode);
begin
  FillRect(r.Left, r.top, r.right, r.bottom, texture, mode);
end;

procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer; c: TColor);
begin
  FillRect(x, y, x2, y2, ColorToBGRA(c), dmSet);
end;

procedure TBGRACustomBitmap.TextOutCurved(APath: IBGRAPath; sUTF8: string;
  AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single);
var cursor: TBGRACustomPathCursor;
begin
  cursor := APath.getCursor;
  if cursor = nil then exit;
  case AAlign of
    taCenter: cursor.Position := cursor.PathLength*0.5;
    taRightJustify: cursor.Position:= cursor.PathLength;
  end;
  TextOutCurved(cursor, sUTF8, AColor, AAlign, ALetterSpacing);
  cursor.free;
end;

procedure TBGRACustomBitmap.TextOutCurved(APath: IBGRAPath; sUTF8: string;
  ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single);
var cursor: TBGRACustomPathCursor;
begin
  cursor := APath.getCursor;
  if cursor = nil then exit;
  case AAlign of
    taCenter: cursor.Position := cursor.PathLength*0.5;
    taRightJustify: cursor.Position:= cursor.PathLength;
  end;
  TextOutCurved(cursor, sUTF8, ATexture, AAlign, ALetterSpacing);
  cursor.free;
end;

{ Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
  The value of FontOrientation is taken into account, so that the text may be rotated. }
procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TBGRAPixel);
begin
  TextOut(x, y, sUTF8, c, taLeftJustify);
end;

{ Draw the UTF8 encoded string, (x,y) being the top-left corner. The color c is used to fill the text.
  The value of FontOrientation is taken into account, so that the text may be rotated. }
procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string; c: TColor);
begin
  TextOut(x, y, sUTF8, ColorToBGRA(c));
end;

{ Draw the UTF8 encoded string, (x,y) being the top-left corner. The texture is used to fill the text.
  The value of FontOrientation is taken into account, so that the text may be rotated. }
procedure TBGRACustomBitmap.TextOut(x, y: single; sUTF8: string;
  texture: IBGRAScanner);
begin
  TextOut(x, y, sUTF8, texture, taLeftJustify);
end;

{ Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
  The position depends on the specified horizontal alignment halign and vertical alignement valign.
  The color c is used to fill the text. No rotation is applied. }
procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
  halign: TAlignment; valign: TTextLayout; c: TBGRAPixel);
var
  style: TTextStyle;
begin
  {$hints off}
  FillChar(style,sizeof(style),0);
  {$hints on}
  style.Alignment := halign;
  style.Layout := valign;
  style.Wordbreak := true;
  style.ShowPrefix := false;
  style.Clipping := false;
  TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,c);
end;

{ Draw the UTF8 encoded string in the rectangle ARect. Text is wrapped if necessary.
  The position depends on the specified horizontal alignment halign and vertical alignement valign.
  The texture is used to fill the text. No rotation is applied. }
procedure TBGRACustomBitmap.TextRect(ARect: TRect; sUTF8: string;
  halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner);
var
  style: TTextStyle;
begin
  {$hints off}
  FillChar(style,sizeof(style),0);
  {$hints on}
  style.Alignment := halign;
  style.Layout := valign;
  style.Wordbreak := true;
  style.ShowPrefix := false;
  style.Clipping := false;
  TextRect(ARect,ARect.Left,ARect.Top,sUTF8,style,texture);
end;

function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry: single): ArrayOfTPointF;
begin
  result := ComputeEllipseContour(x,y,rx,ry);
end;

function TBGRACustomBitmap.ComputeEllipse(x, y, rx, ry, w: single
  ): ArrayOfTPointF;
begin
  result := ComputeEllipseBorder(x,y,rx,ry,w);
end;

procedure TBGRACustomBitmap.FillTransparent;
begin
  Fill(BGRAPixelTransparent);
end;

procedure TBGRACustomBitmap.Fill(c: TColor);
begin
  Fill(ColorToBGRA(c));
end;

procedure TBGRACustomBitmap.Fill(c: TBGRAPixel);
begin
  Fill(c, 0, NbPixels);
end;

procedure TBGRACustomBitmap.AlphaFill(alpha: byte);
begin
  AlphaFill(alpha, 0, NbPixels);
end;

procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
  color: TBGRAPixel);
begin
  FillMask(x,y, AMask, color, dmDrawWithTransparency);
end;

procedure TBGRACustomBitmap.FillMask(x, y: integer; AMask: TBGRACustomBitmap;
  texture: IBGRAScanner);
begin
  FillMask(x,y, AMask, texture, dmDrawWithTransparency);
end;

procedure TBGRACustomBitmap.FloodFill(X, Y: integer; Color: TBGRAPixel;
  mode: TFloodfillMode; Tolerance: byte);
begin
  ParallelFloodFill(X,Y,Self,Color,mode,Tolerance);
end;

procedure TBGRACustomBitmap.DrawPart(ARect: TRect; ACanvas: TCanvas; x,
  y: integer; Opaque: boolean);
var
  partial: TBGRACustomBitmap;
begin
  partial := GetPart(ARect);
  if partial <> nil then
  begin
    partial.Draw(ACanvas, x, y, Opaque);
    partial.Free;
  end;
end;

procedure TBGRACustomBitmap.PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap);
begin
  PutImageAngle(x,y,source,0);
end;

procedure TBGRACustomBitmap.PutImagePart(x, y: integer;
  Source: TBGRACustomBitmap; SourceRect: TRect; mode: TDrawMode; AOpacity: byte);
var w,h,sourcex,sourcey,nx,ny,xb,yb,destx,desty: integer;
    oldClip,newClip: TRect;
begin
  if (Source = nil) or (AOpacity = 0) then exit;
  w := SourceRect.Right-SourceRect.Left;
  h := SourceRect.Bottom-SourceRect.Top;
  if (w <= 0) or (h <= 0) or (Source.Width = 0) or (Source.Height = 0) then exit;
  sourcex := PositiveMod(SourceRect.Left, Source.Width);
  sourcey := PositiveMod(SourceRect.Top, Source.Height);
  nx := (sourceX+w + Source.Width-1) div Source.Width;
  ny := (sourceY+h + Source.Height-1) div Source.Height;

  oldClip := ClipRect;
  newClip := rect(x,y,x+w,y+h);
  if not IntersectRect(newClip,newClip,oldClip) then exit;

  ClipRect := newClip;

  desty := y-sourcey;
  for yb := 0 to ny-1 do
  begin
    destx := x-sourcex;
    for xb := 0 to nx-1 do
    begin
      self.PutImage(destx,desty,Source,mode,AOpacity);
      inc(destx,Source.Width);
    end;
    inc(desty,Source.Height);
  end;

  ClipRect := oldClip;
end;

procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean);
begin
  if ACorrectBlur then
    PutImageAffine(Origin,HAxis,VAxis,Source,rfCosine,AOpacity)
  else
    PutImageAffine(Origin,HAxis,VAxis,Source,rfLinear,AOpacity);
end;

procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte);
var outputBounds: TRect;
begin
  if (Source = nil) or (AOpacity = 0) then exit;
  if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
     (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
     (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
  begin
    PutImage(round(origin.x),round(origin.y),Source,dmDrawWithTransparency,AOpacity);
    exit;
  end;
  outputBounds := GetImageAffineBounds(Origin,HAxis,VAxis,Source);
  PutImageAffine(Origin,HAxis,VAxis,Source,outputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
end;

procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte;
  ACorrectBlur: Boolean);
begin
  if ACorrectBlur then
    PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfCosine,dmDrawWithTransparency, AOpacity)
  else
    PutImageAffine(Origin,HAxis,VAxis,Source,AOutputBounds,rfLinear,dmDrawWithTransparency,AOpacity);
end;

{ Returns the area that contains the affine transformed image }
function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap): TRect;
var minx,miny,maxx,maxy: integer;
    vx,vy,pt1: TPointF;
    sourceBounds: TRect;

  //include specified point in the bounds
  procedure Include(pt: TPointF);
  begin
    if floor(pt.X) < minx then minx := floor(pt.X);
    if floor(pt.Y) < miny then miny := floor(pt.Y);
    if ceil(pt.X) > maxx then maxx := ceil(pt.X);
    if ceil(pt.Y) > maxy then maxy := ceil(pt.Y);
  end;

begin
  result := EmptyRect;
  if (Source = nil) then exit;
  sourceBounds := source.GetImageBounds;
  if IsRectEmpty(sourceBounds) then exit;

  if (abs(Origin.x-round(Origin.x))<1e-6) and (abs(Origin.y-round(Origin.Y))<1e-6) and
     (abs(HAxis.x-(Origin.x+Source.Width))<1e-6) and (abs(HAxis.y-origin.y)<1e-6) and
     (abs(VAxis.x-Origin.x)<1e-6) and (abs(VAxis.y-(Origin.y+Source.Height))<1e-6) then
  begin
    result := sourceBounds;
    OffsetRect(result,round(origin.x),round(origin.y));
    IntersectRect(result,result,ClipRect);
    exit;
  end;

  { Compute bounds }
  vx := (HAxis-Origin)*(1/source.Width);
  vy := (VAxis-Origin)*(1/source.Height);
  pt1 := Origin+vx*sourceBounds.Left+vy*sourceBounds.Top;
  minx := floor(pt1.X);
  miny := floor(pt1.Y);
  maxx := ceil(pt1.X);
  maxy := ceil(pt1.Y);
  Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Top);
  Include(Origin+vx*sourceBounds.Right+vy*sourceBounds.Bottom);
  Include(Origin+vx*sourceBounds.Left+vy*sourceBounds.Bottom);

  result := rect(minx,miny,maxx+1,maxy+1);
  IntersectRect(result,result,ClipRect);
end;

procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
  Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
  imageCenterX: single; imageCenterY: single; AOpacity: Byte;
  ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
begin
  if ACorrectBlur then
    PutImageAngle(x,y,Source,angle,AOutputBounds,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
  else
    PutImageAngle(x,y,Source,angle,AOutputBounds,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
end;

procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
  Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
  imageCenterY: single; AOpacity: Byte; ARestoreOffsetAfterRotation: boolean; ACorrectBlur: Boolean);
begin
  if ACorrectBlur then
    PutImageAngle(x,y,Source,angle,rfCosine,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation)
  else
    PutImageAngle(x,y,Source,angle,rfLinear,imageCenterX,imageCenterY,AOpacity,ARestoreOffsetAfterRotation);
end;

procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
  Source: TBGRACustomBitmap; angle: single; AOutputBounds: TRect;
  AResampleFilter: TResampleFilter; imageCenterX: single; imageCenterY: single; AOpacity: Byte;
  ARestoreOffsetAfterRotation: boolean);
var
  Origin,HAxis,VAxis: TPointF;
begin
  if (source = nil) or (AOpacity=0) then exit;
  ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
     Origin,HAxis,VAxis);
  PutImageAffine(Origin,HAxis,VAxis,source,AOutputBounds,AResampleFilter,dmDrawWithTransparency,AOpacity);
end;

procedure TBGRACustomBitmap.PutImageAngle(x, y: single;
  Source: TBGRACustomBitmap; angle: single; AResampleFilter: TResampleFilter;
  imageCenterX: single; imageCenterY: single; AOpacity: Byte;
  ARestoreOffsetAfterRotation: boolean);
var
  Origin,HAxis,VAxis: TPointF;
begin
  if (source = nil) or (AOpacity=0) then exit;
  ComputeImageAngleAxes(x,y,source.Width,source.Height,angle,imageCenterX,imageCenterY,ARestoreOffsetAfterRotation,
     Origin,HAxis,VAxis);
  PutImageAffine(Origin,HAxis,VAxis,source,AResampleFilter,AOpacity);
end;

procedure TBGRACustomBitmap.ComputeImageAngleAxes(x, y, w, h,
  angle: single; imageCenterX, imageCenterY: single;
  ARestoreOffsetAfterRotation: boolean; out Origin, HAxis, VAxis: TPointF);
var
  cosa,sina: single;

  { Compute rotated coordinates }
  function Coord(relX,relY: single): TPointF;
  begin
    relX -= imageCenterX;
    relY -= imageCenterY;
    result.x := relX*cosa-relY*sina+x;
    result.y := relY*cosa+relX*sina+y;
    if ARestoreOffsetAfterRotation then
    begin
      result.x += imageCenterX;
      result.y += imageCenterY;
    end;
  end;

begin
  cosa := cos(-angle*Pi/180);
  sina := -sin(-angle*Pi/180);
  Origin := Coord(0,0);
  HAxis := Coord(w,0);
  VAxis := Coord(0,h);
end;

function TBGRACustomBitmap.GetImageAngleBounds(x, y: single;
  Source: TBGRACustomBitmap; angle: single; imageCenterX: single;
  imageCenterY: single; ARestoreOffsetAfterRotation: boolean): TRect;
var
  cosa,sina: single;

  { Compute rotated coordinates }
  function Coord(relX,relY: single): TPointF;
  begin
    relX -= imageCenterX;
    relY -= imageCenterY;
    result.x := relX*cosa-relY*sina+x;
    result.y := relY*cosa+relX*sina+y;
    if ARestoreOffsetAfterRotation then
    begin
      result.x += imageCenterX;
      result.y += imageCenterY;
    end;
  end;

begin
  if (source = nil) then
  begin
    result := EmptyRect;
    exit;
  end;
  cosa := cos(-angle*Pi/180);
  sina := -sin(-angle*Pi/180);
  result := GetImageAffineBounds(Coord(0,0),Coord(source.Width,0),Coord(0,source.Height),source);
end;

procedure TBGRACustomBitmap.VerticalFlip;
begin
  VerticalFlip(rect(0,0,Width,Height));
end;

procedure TBGRACustomBitmap.HorizontalFlip;
begin
  HorizontalFlip(rect(0,0,Width,Height));
end;

procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap);
begin
  ApplyMask(mask, Rect(0,0,Width,Height), Point(0,0));
end;

procedure TBGRACustomBitmap.ApplyMask(mask: TBGRACustomBitmap; ARect: TRect);
begin
  ApplyMask(mask, ARect, ARect.TopLeft);
end;

{ Interface gateway }
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};
begin
  if GetInterface(iid, obj) then
    Result := S_OK
  else
    Result := longint(E_NOINTERFACE);
end;

{ There is no automatic reference counting, but it is compulsory to define these functions }
function TBGRACustomBitmap._AddRef: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
begin
  result := 0;
end;

function TBGRACustomBitmap._Release: Integer; {$IF (not defined(WINDOWS)) AND (FPC_FULLVERSION>=20501)}cdecl{$ELSE}stdcall{$IFEND};
begin
  result := 0;
end;

{$hints off}
procedure TBGRACustomBitmap.ScanPutPixels(pdest: PBGRAPixel; count: integer;
  mode: TDrawMode);
begin
  //do nothing
end;
{$hints on}

function TBGRACustomBitmap.IsScanPutPixelsDefined: boolean;
begin
  result := False;
end;
{$ENDIF}
