// SPDX-License-Identifier: LGPL-3.0-linking-exception
{$IFDEF INCLUDE_INTERFACE}
{$UNDEF INCLUDE_INTERFACE}
type
{=== TBGRACustomBitmap ===}

  { TBGRACustomBitmap }
  {* @abstract(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 IBGRAScanner interface.
  }
  TBGRACustomBitmap = class(specialize TGenericUniversalBitmap<TBGRAPixel,TBGRAPixelColorspace>,IBGRAScanner)
  protected
    FXorMask: TBGRACustomBitmap;

    { accessors to properies }
     procedure SetXorMask(AValue: TBGRACustomBitmap);

     function GetAverageColor: TColor; virtual; abstract;
     function GetAveragePixel: TBGRAPixel; virtual; abstract;

     //FreePascal drawing routines
     {$IFDEF BGRABITMAP_USE_FPCANVAS}function GetCanvasFP: TFPImageCanvas; virtual; abstract;{$ENDIF}
     function GetCanvasDrawModeFP: TDrawMode; virtual; abstract;
     procedure SetCanvasDrawModeFP(const AValue: TDrawMode); virtual; abstract;

     //GUI bitmap object
     function GetBitmap: TBitmap; virtual; abstract;
     function GetCanvas: TCanvas; virtual; abstract;
     function GetCanvasOpacity: byte; virtual; abstract;
     procedure SetCanvasOpacity(AValue: byte); virtual; abstract;
     function GetCanvasAlphaCorrection: boolean; virtual; abstract;
     procedure SetCanvasAlphaCorrection(const AValue: boolean); virtual; abstract;

     procedure Init; override;
     function InternalNew: TBGRACustomBitmap; override;

     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
     {** 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;
     ScanMaskChannel: TChannel;

     {** Cursor position for mouse pointer }
     HotSpot: TPoint;

     { ** Free reference to xor mask }
     procedure DiscardXorMask; virtual;
     { ** Allocate xor mask }
     procedure NeedXorMask; virtual;
     {** Xor mask to be applied when image is drawn }
     property XorMask: TBGRACustomBitmap read FXorMask write SetXorMask;

     {** 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 }
     {$IFDEF BGRABITMAP_USE_FPCANVAS}property CanvasFP: TFPImageCanvas read GetCanvasFP;{$ENDIF}

     {** 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;

  protected {----------- pen style accessors ----------------}
     function GetPenJoinStyle: TPenJoinStyle; virtual; abstract;
     procedure SetPenJoinStyle(const AValue: TPenJoinStyle); virtual; abstract;
     function GetPenMiterLimit: single; virtual; abstract;
     procedure SetPenMiterLimit(const AValue: single); 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 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;

  public {----------- pen style ----------------}
     {** How to join segments. See [[BGRABitmap Types imported from Graphics|BGRAGraphics]] }
     property JoinStyle: TPenJoinStyle read GetPenJoinStyle Write SetPenJoinStyle;
     {** Limit for the extension of the segments when joining them
         with ''pjsMiter'' join style, expressed in multiples of the width
         of the pen }
     property JoinMiterLimit: single read GetPenMiterLimit Write SetPenMiterLimit;
     {** 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;

     {** 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;

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

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

  protected {-------------------font style accessors------------------------}
     function GetFontAntialias: Boolean;
     procedure SetFontAntialias(const AValue: Boolean);
     function GetFontRenderer: TBGRACustomFontRenderer; virtual; abstract;
     procedure SetFontRenderer(AValue: TBGRACustomFontRenderer); 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 GetFontVerticalAnchorOffset: single; virtual; abstract;
     function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;

     function GetFontRightToLeftFor(AText: string): boolean;

  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 base direction of the text (cf Unicode). By default, it is
         automatically determined by the first strongly oriented character.
         You can specify another base direction here however it is not taken
         into account by the LCL on Linux. }
     FontBidiMode: TFontBidiMode;

     {** 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;

     property FontVerticalAnchorOffset: single read GetFontVerticalAnchorOffset;

     {** 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(AFPImage: TFPCustomImage; ACopyProperties: Boolean=False); overload; virtual; abstract;
     constructor Create(ABitmap: TBitmap); overload; virtual; abstract;
     constructor Create(ABitmap: TBitmap; AUseTransparent: boolean); overload; virtual; abstract;
     constructor Create(AFilename: string); overload; virtual; abstract;
     constructor Create(AFilename: string; AIsUtf8Filename: boolean); overload; virtual; abstract;
     constructor Create(AFilename: string; AIsUtf8Filename: boolean; AOptions: TBGRALoadingOptions); overload; virtual; abstract;
     constructor Create(AStream: TStream); overload; virtual; abstract;

     function NewBitmap: TBGRACustomBitmap; overload; override;
     function NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap; overload; override;
     function NewBitmap(AWidth, AHeight: integer; const Color: TBGRAPixel): TBGRACustomBitmap; overload; override;
     function NewBitmap(AWidth, AHeight: integer; AColor: Pointer): TBGRACustomBitmap; overload; override;
     function NewBitmap(Filename: string): TBGRACustomBitmap; overload; virtual; abstract;
     function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRACustomBitmap; overload; virtual; abstract;
     function NewBitmap(Filename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions): TBGRACustomBitmap; overload; virtual; abstract;
     function NewBitmap(AFPImage: TFPCustomImage): TBGRACustomBitmap; overload; virtual; abstract;

     procedure LoadFromStream(AStream: TStream; AHandler: TFPCustomImageReader; AOptions: TBGRALoadingOptions); override;

     {==== Reference counting ====}

     {** Adds a reference (this reference count is not the same as
         the reference count of an interface, it changes only by
         explicit calls) }
     function NewReference: TBGRACustomBitmap; override;
     {** Returns an object with a reference count equal to 1. Duplicate
         this bitmap if necessary }
     function GetUnique: TBGRACustomBitmap; override;
     function Duplicate(DuplicateProperties: Boolean = False): TBGRACustomBitmap; overload; override;
     function Duplicate(DuplicateProperties, DuplicateXorMask: Boolean): TBGRACustomBitmap; overload; virtual;
     procedure CopyPropertiesTo(ABitmap: TCustomUniversalBitmap); override;
     function GetPart(const ARect: TRect; ACopyProperties: Boolean=False): TBGRACustomBitmap; override;

     function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
                 AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRACustomBitmap; override;

     {** Can only be called with an existing instance of ''TBGRACustomBitmap''.
         Sets the dimensions of an existing ''TBGRACustomBitmap'' instance. }
     procedure SetSize(AWidth, AHeight: integer); override;

     {==== Retrieve image from system ====}

     {** Gets the content of the specified device context }
     procedure LoadFromDevice(DC: HDC); overload; virtual; abstract;
     {** Gets the content from the specified rectangular area of a device context }
     procedure LoadFromDevice(DC: HDC; ARect: TRect); overload; virtual; abstract;
     {** 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]] }

     {==== Drawing functions ====}

     {Pixel functions}
     procedure SetPixel(x, y: int32or64; c: TColor); overload; virtual; abstract;
     procedure XorPixel(x, y: int32or64; const c: TBGRAPixel); overload; virtual; abstract;
     procedure DrawPixel(x, y: int32or64; const c: TBGRAPixel; ADrawMode: TDrawMode); overload; override;
     procedure FastBlendPixel(x, y: int32or64; const c: TBGRAPixel); virtual; abstract;
     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; overload; virtual; abstract;
     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; overload; virtual; abstract;
     function GetPixelCycle(x, y: single; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; overload; virtual; abstract;
     function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter = rfLinear): TBGRAPixel; overload; virtual; abstract;
     function GetPixelCycle256(x, y, fracX256,fracY256: int32or64; AResampleFilter: TResampleFilter; repeatX: boolean; repeatY: boolean): TBGRAPixel; overload; virtual; abstract;

     {Line primitives}
     procedure XorHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure DrawHorizLine(x, y, x2: int32or64; ec: TExpandedPixel); overload; virtual; abstract;
     procedure DrawHorizLine(x, y, x2: int32or64; texture: IBGRAScanner); overload;
     procedure FastBlendHorizLine(x, y, x2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure DrawHorizLineDiff(x, y, x2: int32or64; c, compare: TBGRAPixel; maxDiff: byte); virtual; abstract;
     procedure HorizLineDiff(x, y, x2: int32or64; const ABrush: TUniversalBrush; ACompare: TBGRAPixel; AMaxDiffW: word); virtual; abstract;

     procedure XorVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure DrawVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;
     procedure FastBlendVertLine(x, y, y2: int32or64; c: TBGRAPixel); virtual; abstract;

     {==== Rectangles, ellipses and path (floating point coordinates) ====}
     {* These functions use the current pen style/cap/join. The parameter ''w''
        specifies the width of the line and the base unit for dashes
      * The coordinates are pixel-centered, so that when filling a rectangle,
        if the supplied values are integers, the border will be half transparent.
        If you want the border to be completely filled, you can subtract/add
        0.5 to the coordinates to include the remaining thin border.
        See [[BGRABitmap tutorial 13|coordinate system]]. }

     {==== Multi-shape fill ====}

     {** Draws and fill a polyline using current pen style/cap/join in one go.
         The stroke is stricly over the fill even if partially transparent.
         ''fillcolor'' specifies a color to fill the polygon formed by the points }
     procedure DrawPolyLineAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); overload; virtual; abstract;
     {** Draws a filled polygon using current pen style/cap/join in one go.
         The stroke is stricly over the fill even if partially transparent.
         The polygon is always closed. You don't need to set the last point
         to be the same as the first point. }
     procedure DrawPolygonAntialias(const points: array of TPointF; c: TBGRAPixel; w: single; fillcolor: TBGRAPixel); overload; virtual; abstract;

     procedure EllipseAntialias(x, y, rx, ry: single; c: TBGRAPixel; w: single; back: TBGRAPixel); overload; virtual; abstract;
     procedure EllipseAntialias(AOrigin, AXAxis, AYAxis: TPointF; c: TBGRAPixel; w: single; back: TBGRAPixel); overload; virtual; abstract;

     procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); overload; virtual; abstract;

     procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillColor: TBGRAPixel); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillColor: TBGRAPixel); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeColor: TBGRAPixel; AWidth: single; AFillTexture: IBGRAScanner); overload; virtual; abstract;
     procedure DrawPath(APath: IBGRAPath; AMatrix: TAffineMatrix; AStrokeTexture: IBGRAScanner; AWidth: single; AFillTexture: IBGRAScanner); overload; virtual; abstract;

     {==== Gradient/textured polygons ====}

     procedure FillTriangleLinearColor(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); overload; virtual; abstract;
     procedure FillTriangleLinearColorAntialias(pt1,pt2,pt3: TPointF; c1,c2,c3: TBGRAPixel); overload; virtual; abstract;
     procedure FillTriangleLinearMapping(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF; TextureInterpolation: Boolean= True); overload; virtual; abstract;
     {$IFNDEF BGRABITMAP_CORE}{ Fills a triangle with linear mapping of a texture and lightness }
     procedure FillTriangleLinearMappingLightness(pt1,pt2,pt3: TPointF; texture: IBGRAScanner;
                 tex1, tex2, tex3: TPointF; light1,light2,light3: word;
                 TextureInterpolation: Boolean= True); overload; virtual; abstract; {$ENDIF}
     procedure FillTriangleLinearMappingAntialias(pt1,pt2,pt3: TPointF; texture: IBGRAScanner; tex1, tex2, tex3: TPointF); overload; virtual; abstract;

     procedure FillQuadLinearColor(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); overload; virtual; abstract;
     procedure FillQuadLinearColorAntialias(pt1,pt2,pt3,pt4: TPointF; c1,c2,c3,c4: TBGRAPixel); overload; virtual; abstract;
     procedure FillQuadLinearMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; TextureInterpolation: Boolean= True; ACulling: TFaceCulling = fcNone; ACropToPolygon: boolean = true); overload; virtual; abstract;
     {$IFNDEF BGRABITMAP_CORE}{ Fills a quadrilateral with linear mapping and lightness }
     procedure FillQuadLinearMappingLightness(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner;
                 tex1, tex2, tex3, tex4: TPointF; light1,light2,light3,light4: word;
                 TextureInterpolation: Boolean= True); overload; virtual; abstract;{$ENDIF}
     procedure FillQuadLinearMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACulling: TFaceCulling = fcNone); overload; virtual; abstract;
     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ADrawMode: TDrawMode = dmDrawWithTransparency); overload; virtual; abstract;
     procedure FillQuadPerspectiveMapping(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect; ADrawMode: TDrawMode = dmDrawWithTransparency); overload; virtual; abstract;
     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF); overload; virtual; abstract;
     procedure FillQuadPerspectiveMappingAntialias(pt1,pt2,pt3,pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF; ACleanBorders: TRect); overload; virtual; abstract;
     procedure FillQuadAffineMapping(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; ADrawMode: TDrawMode = dmDrawWithTransparency; AOpacity: byte = 255); virtual; abstract;
     procedure FillQuadAffineMappingAntialias(Orig,HAxis,VAxis: TPointF; AImage: TBGRACustomBitmap; APixelCenteredCoordinates: boolean = true; AOpacity: byte = 255); virtual; abstract;

     procedure FillEllipseLinearColorAntialias(x, y, rx, ry: single; outercolor, innercolor: TBGRAPixel); overload; virtual; abstract;
     procedure FillEllipseLinearColorAntialias(AOrigin, AXAxis, AYAxis: TPointF; outercolor, innercolor: TBGRAPixel); overload; virtual; abstract;

     {$IFNDEF BGRABITMAP_CORE}
     procedure FillPolyLinearColor(const points: array of TPointF; AColors: array of TBGRAPixel);  overload; virtual; abstract;
     procedure FillPolyLinearMapping(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean); overload; virtual; abstract;
     procedure FillPolyLinearMappingLightness(const points: array of TPointF; texture: IBGRAScanner; texCoords: array of TPointF; lightnesses: array of word; TextureInterpolation: Boolean); overload; virtual; abstract;
     procedure FillPolyPerspectiveMapping(const points: array of TPointF; const pointsZ: array of single; texture: IBGRAScanner; texCoords: array of TPointF; TextureInterpolation: Boolean; zbuffer: psingle = nil); overload; virtual; abstract;
     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); overload; virtual; abstract;
     {$ENDIF}

     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 RectangleAntialias(x, y, x2, y2: single; c: TBGRAPixel; w: single; back: TBGRAPixel); overload; virtual; abstract;
     procedure RectangleWithin(x1,y1,x2,y2: single; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel; APixelCenteredCoordinates: boolean = true); overload;
     procedure RectangleWithin(r: TRect; ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel); overload;

     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; c: TBGRAPixel; w: single; options: TRoundRectangleOptions = []); overload; virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; pencolor: TBGRAPixel; w: single; fillcolor: TBGRAPixel; options: TRoundRectangleOptions = []); overload; virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; penTexture: IBGRAScanner; w: single; fillTexture: IBGRAScanner; options: TRoundRectangleOptions = []); overload; virtual; abstract;
     procedure RoundRectAntialias(x,y,x2,y2,rx,ry: single; texture: IBGRAScanner; w: single; options: TRoundRectangleOptions = []); overload; virtual; abstract;

     {** Fills completely a rectangle, without any border, with the
         specified ''texture'' and with the specified ''mode'' and optional dithering algorithm }
     procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode; ditheringAlgorithm: TDitheringAlgorithm); overload; virtual;
     procedure FillRect(r: TRect; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm); overload; virtual;
     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; ditheringAlgorithm: TDitheringAlgorithm); overload; virtual;
     procedure FillRect(x, y, x2, y2: integer; texture: IBGRAScanner; mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm); overload; virtual; abstract;

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

     {$IFNDEF BGRABITMAP_CORE}
     { Draw text taking into account line endings, filling it with a solid color }
     procedure TextMultiline(x,y: single; const sUTF8: string; c: TBGRAPixel;
       AAlign: TBidiTextAlignment = btaLeftJustify; AVertAlign: TTextLayout = tlTop; AParagraphSpacing: single = 0); overload;
     { Draw text taking into account line endings, filling it with a texture }
     procedure TextMultiline(x,y: single; const sUTF8: string; ATexture: IBGRAScanner;
       AAlign: TBidiTextAlignment = btaLeftJustify; AVertAlign: TTextLayout = tlTop; AParagraphSpacing: single = 0); overload;
     { Draw text with line breaks, filling it with a solid color }
     procedure TextMultiline(ALeft,ATop,AWidth: single; const sUTF8: string; c: TBGRAPixel;
       AAlign: TBidiTextAlignment = btaNatural; AVertAlign: TTextLayout = tlTop; AParagraphSpacing: single = 0); overload; virtual; abstract;
     { Draw text with line breaks, filling it with a texture }
     procedure TextMultiline(ALeft,ATop,AWidth: single; const sUTF8: string; ATexture: IBGRAScanner;
       AAlign: TBidiTextAlignment = btaNatural; AVertAlign: TTextLayout = tlTop; AParagraphSpacing: single = 0); overload; virtual; abstract;
     {$ENDIF}

     function TextSize(const sUTF8: string): TSize; overload; virtual; abstract;
     function TextAffineBox(const sUTF8: string): TAffineBox; virtual; abstract;
     function TextSize(const sUTF8: string; AMaxWidth: integer): TSize; overload; virtual; abstract;
     function TextSize(const sUTF8: string; AMaxWidth: integer; ARightToLeft: boolean): TSize; overload; virtual; abstract;
     function TextFitInfo(const sUTF8: string; AMaxWidth: integer): integer; virtual; abstract;
     {$IFNDEF BGRABITMAP_CORE}{ Returns the total size of the string provided using the current font
                                and taking into account line breaks. }
     function TextSizeMultiline(const sUTF8: string; AMaxWidth: single = EmptySingle;
                 AParagraphSpacing: single = 0): TSize; virtual; abstract;{$ENDIF}

     { Draw the UTF8 encoded string, (x,y) being the top-left corner by default. 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; const sUTF8: string; c: TBGRAPixel; align: TAlignment); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; texture: IBGRAScanner; align: TAlignment); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; c: TBGRAPixel); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; c: TBGRAPixel; ARightToLeft: boolean); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; c: TColor); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; c: TColor; ARightToLeft: boolean); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; texture: IBGRAScanner); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; texture: IBGRAScanner; ARightToLeft: boolean); overload; virtual;
     procedure TextOut(x, y: single; const sUTF8: string; c: TBGRAPixel; align: TAlignment; ARightToLeft: boolean); overload; virtual; abstract;
     procedure TextOut(x, y: single; const sUTF8: string; texture: IBGRAScanner; align: TAlignment; ARightToLeft: boolean); overload; virtual; abstract;
     procedure TextOut(x, y: single; const sUTF8: string; AColor: TBGRAPixel; AAlign: TAlignment; ALetterSpacing: single); overload; virtual; abstract;
     procedure TextOut(x, y: single; const sUTF8: string; ATexture: IBGRAScanner; AAlign: TAlignment; ALetterSpacing: single); overload; virtual; abstract;

     { Overrides the font orientation with the parameter orientationTenthDegCCW }
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; c: TBGRAPixel); overload; virtual;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; c: TBGRAPixel; align: TAlignment); overload; virtual;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; c: TBGRAPixel; align: TAlignment; ARightToLeft: boolean); overload; virtual; abstract;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; texture: IBGRAScanner); overload; virtual;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; texture: IBGRAScanner; align: TAlignment); overload; virtual;
     procedure TextOutAngle(x, y: single; orientationTenthDegCCW: integer; const sUTF8: string; texture: IBGRAScanner; align: TAlignment; ARightToLeft: boolean); overload; virtual; abstract;

     { 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; const sUTF8: string; halign: TAlignment; valign: TTextLayout; c: TBGRAPixel); overload; virtual;
     procedure TextRect(ARect: TRect; const sUTF8: string; halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner); overload; virtual;

     //-------------------------- computing path ------------------------------------

     {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; overload; virtual; abstract;
     function ComputeBezierCurve(const curve: TQuadraticBezierCurve): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeBezierSpline(const spline: array of TCubicBezierCurve): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeBezierSpline(const spline: array of TQuadraticBezierCurve): ArrayOfTPointF; overload; virtual; abstract;

     {can be accessed via Pen property}
     function ComputeWidePolyline(const points: array of TPointF; w: single): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeWidePolyline(const points: array of TPointF; w: single; ClosedCap: boolean): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeWidePolygon(const points: array of TPointF; w: single): ArrayOfTPointF; virtual; abstract;

     function ComputeEllipse(x,y,rx,ry: single): ArrayOfTPointF; overload; deprecated;
     function ComputeEllipse(x,y,rx,ry,w: single): ArrayOfTPointF; overload; deprecated;
     function ComputeEllipseContour(x,y,rx,ry: single; quality: single = 1): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeEllipseContour(AOrigin, AXAxis, AYAxis: TPointF; quality: single = 1): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeEllipseBorder(x,y,rx,ry,w: single; quality: single = 1): ArrayOfTPointF; overload; virtual; abstract;
     function ComputeEllipseBorder(AOrigin, AXAxis, AYAxis: TPointF; w: single; quality: single = 1): ArrayOfTPointF; overload; 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; overload; virtual; abstract;
     function ComputeRoundRect(x1,y1,x2,y2,rx,ry: single; options: TRoundRectangleOptions; quality: single = 1): ArrayOfTPointF; overload; 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}

     // compatibility: take into account ClipRect
     procedure Fill(texture: IBGRAScanner); overload; virtual;
     procedure Fill(texture: IBGRAScanner; mode: TDrawMode); overload; override;

     procedure Fill(c: TBGRAPixel; start, Count: integer); overload; virtual; abstract;
     procedure DrawPixels(c: TBGRAPixel; start, Count: integer); virtual; abstract;
     procedure AlphaFill(alpha: byte; start, Count: integer); overload; virtual; abstract;
     procedure ReplaceColor(before, after: TColor); overload; virtual; abstract;
     procedure ReplaceColor(ARect: TRect; before, after: TColor); overload; virtual; abstract;
     procedure FloodFill(X, Y: integer; Color: TBGRAPixel;
       mode: TFloodfillMode; Tolerance: byte = 0); overload; virtual;
     procedure FloodFill(X, Y: integer; const Brush: TUniversalBrush;
       Progressive: boolean; ToleranceW: Word = $00ff); overload; virtual;
     procedure ParallelFloodFill(X, Y: integer; Dest: TCustomUniversalBitmap; Color: TBGRAPixel;
       mode: TFloodfillMode; Tolerance: byte = 0; DestOfsX: integer = 0; DestOfsY: integer = 0); overload; virtual; abstract;
     procedure ParallelFloodFill(X, Y: integer; Dest: TCustomUniversalBitmap; const Brush: TUniversalBrush;
       Progressive: boolean; ToleranceW: Word = $00ff; DestOfsX: integer = 0; DestOfsY: integer = 0); overload; 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;
       ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); overload; virtual; abstract;
     procedure GradientFill(x, y, x2, y2: integer; gradient: TBGRACustomGradient;
       gtype: TGradientType; o1, o2: TPointF; mode: TDrawMode;
       Sinus: Boolean=False;
       ditherAlgo: TDitheringAlgorithm = daNearestNeighbor); overload; virtual; abstract;

     {Canvas drawing functions}
     procedure DataDrawTransparent(ACanvas: TCanvas; Rect: TRect;
       AData: Pointer; ALineOrder: TRawImageLineOrder; AWidth, AHeight: integer); virtual; abstract;
     procedure DataDrawOpaque(ACanvas: TCanvas; ARect: 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); overload; virtual; abstract;
     procedure Draw(ACanvas: TCanvas; Rect: TRect; Opaque: boolean = True); overload; virtual; abstract;
     procedure DrawPart(ARect: TRect; ACanvas: TCanvas; x, y: integer; Opaque: boolean); overload; virtual;
     procedure DrawPart(ARect: TRect; ACanvas: TCanvas; ATargetRect: TRect; Opaque: boolean); overload; virtual;
     function GetPtrBitmap(Top,Bottom: Integer): TBGRACustomBitmap; virtual; abstract;
     function MakeBitmapCopy(BackgroundColor: TColor; AMasked: boolean = False): TBitmap; virtual; abstract;

     {BGRA bitmap functions}
     procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadePosition: byte; mode: TDrawMode = dmDrawWithTransparency); overload; virtual; abstract;
     procedure CrossFade(ARect: TRect; Source1, Source2: IBGRAScanner; AFadeMask: IBGRAScanner; mode: TDrawMode = dmDrawWithTransparency); overload; virtual; abstract;
     procedure PutImage(x, y: integer; Source: TBitmap; mode: TDrawMode; AOpacity: byte = 255); overload;
     procedure StretchPutImage(ARect: TRect; Source: TBitmap; mode: TDrawMode; AOpacity: byte = 255); overload;
     procedure StretchPutImage(ARect: TRect; Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255); overload; virtual; abstract;
     procedure StretchPutImageProportionally(ARect: TRect; AHorizAlign: TAlignment; AVertAlign: TTextLayout;
       Source: TBGRACustomBitmap; mode: TDrawMode; AOpacity: byte = 255; ACover: boolean = false);
     procedure PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap; AOpacity: byte = 255);
     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; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); overload;
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255); overload;
     procedure PutImageAffine(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false); overload;
     procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AOpacity: Byte=255; ACorrectBlur: Boolean = false; APixelCenteredCoords: boolean = true); overload;
     procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte=255; APixelCenteredCoords: boolean = true); overload;
     procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255; APixelCenteredCoords: boolean = true); overload;
     procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AOutputBounds: TRect; AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte=255; APixelCenteredCoords: boolean = true); overload; virtual; abstract;
     procedure PutImageAffine(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte=255; ACorrectBlur: Boolean = false; APixelCenteredCoords: boolean = true); overload;
     function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; Source: TBGRACustomBitmap): TRect; overload;
     function GetImageAffineBounds(Origin,HAxis,VAxis: TPointF; ASourceWidth, ASourceHeight: integer; const ASourceBounds: TRect; AClipOutput: boolean = true): TRect; overload;
     function GetImageAffineBounds(AMatrix: TAffineMatrix; Source: TBGRACustomBitmap; APixelCenteredCoords: boolean = true): TRect; overload;
     function GetImageAffineBounds(AMatrix: TAffineMatrix; ASourceBounds: TRect; AClipOutput: boolean = true; APixelCenteredCoords: boolean = true): TRect; overload; virtual; abstract;
     class function IsAffineRoughlyTranslation(AMatrix: TAffineMatrix; ASourceBounds: TRect): boolean; virtual; abstract;
     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 Blend(AColor: TBGRAPixel; AOperation: TBlendOperation; AIgnoreDestAlpha: boolean = false); virtual;
     procedure BlendOver(AColor: TBGRAPixel; AOperation: TBlendOperation; AOpacity: byte = 255; ALinearBlend: boolean = false; AIgnoreDestAlpha: boolean = false); virtual;
     procedure BlendRect(ADest: TRect; AColor: TBGRAPixel; AOperation: TBlendOperation; AIgnoreDestAlpha: boolean = false); overload;
     procedure BlendRect(ADest: TRect; AColor: TBGRAPixel; AOperation: TBlendOperation; AExcludeChannels: TChannels); overload; virtual; abstract;
     procedure BlendRectOver(ADest: TRect; AColor: TBGRAPixel; AOperation: TBlendOperation; AOpacity: byte = 255; ALinearBlend: boolean = false; AIgnoreDestAlpha: boolean = false); overload;
     procedure BlendRectOver(ADest: TRect; AColor: TBGRAPixel; AOperation: TBlendOperation; AOpacity: byte; ALinearBlend: boolean; AExcludeChannels: TChannels); overload; virtual; abstract;
     procedure BlendImage(x, y: integer; ASource: TBGRACustomBitmap; AOperation: TBlendOperation); overload; virtual; abstract;
     procedure BlendImage(ADest: TRect; ASource: IBGRAScanner; AOffsetX, AOffsetY: integer; AOperation: TBlendOperation); overload; virtual; abstract;
     procedure BlendImageOver(x, y: integer; ASource: TBGRACustomBitmap; AOperation: TBlendOperation; AOpacity: byte = 255; ALinearBlend: boolean = false); overload; virtual; abstract;
     procedure BlendImageOver(ADest: TRect; ASource: IBGRAScanner; AOffsetX, AOffsetY: integer; AOperation: TBlendOperation; AOpacity: byte = 255; ALinearBlend: boolean = false); overload; virtual; abstract;
     function Resample(newWidth, newHeight: integer;
       mode: TResampleMode = rmFineResample; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;

     //masks
     procedure FillMask(x,y: integer; AMask: TCustomUniversalBitmap; ATexture: IBGRAScanner; ADrawMode: TDrawMode); overload; override;
     procedure FillMask(x,y: integer; AMask: TCustomUniversalBitmap; ATexture: IBGRAScanner; ADrawMode: TDrawMode; AOpacity: byte); overload; virtual; abstract;
     procedure EraseMask(x,y: integer; AMask: TBGRACustomBitmap; alpha: byte=255); virtual; abstract;
     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; color: TBGRAPixel; ARGBOrder: boolean = true); overload; virtual; abstract;
     procedure FillClearTypeMask(x,y: integer; xThird: integer; AMask: TBGRACustomBitmap; texture: IBGRAScanner; ARGBOrder: boolean = true); overload; virtual; abstract;
     function GetMaskFromAlpha: TBGRACustomBitmap; virtual; abstract;
     function GetImageBoundsWithin(const ARect: TRect; Channel: TChannel = cAlpha; ANothingValue: Byte = 0): TRect; overload; override;
     function GetImageBoundsWithin(const ARect: TRect; Channels: TChannels; ANothingValue: Byte = 0): TRect; overload; override;

     {inplace filters}
     procedure Negative; virtual; abstract;
     procedure NegativeRect(ABounds: TRect); virtual; abstract;
     procedure LinearNegative; virtual; abstract;
     procedure LinearNegativeRect(ABounds: TRect); virtual; abstract;
     procedure InplaceGrayscale(AGammaCorrection: boolean = true); overload; virtual; abstract;
     procedure InplaceGrayscale(ABounds: TRect; AGammaCorrection: boolean = true); overload; virtual; abstract;
     procedure InplaceNormalize(AEachChannel: boolean = True); overload; virtual; abstract;
     procedure InplaceNormalize(ABounds: TRect; AEachChannel: boolean = True); overload; virtual; abstract;
     procedure ConvertToLinearRGB; virtual; abstract;
     procedure ConvertFromLinearRGB; virtual; abstract;
     procedure SwapRedBlue; overload; virtual; abstract;
     procedure SwapRedBlue(ARect: TRect); overload; virtual; abstract;
     procedure GrayscaleToAlpha; virtual; abstract;
     procedure AlphaToGrayscale; virtual; abstract;
     procedure VerticalFlip(ARect: TRect); overload; override;
     procedure HorizontalFlip(ARect: TRect); overload; override;
     procedure RotateUDInplace(ARect: TRect); overload; override;

     {Filters}
     function RotateCW(ACopyProperties: Boolean=False): TBGRACustomBitmap; override;
     function RotateCCW(ACopyProperties: Boolean=False): TBGRACustomBitmap; override;
     function RotateUD(ACopyProperties: Boolean=False): TBGRACustomBitmap; override;
     {$IFNDEF BGRABITMAP_CORE}
     function FilterSmartZoom3(Option: TMedianOption; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterMedian(Option: TMedianOption; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterSmooth(ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterSharpen(Amount: single = 1; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterSharpen(ABounds: TRect; Amount: single = 1; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterContour(AGammaCorrection: boolean = false; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterBlurRadial(radius: single; blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterBlurRadial(const ABounds: TRect; radius: single; blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterBlurRadial(radiusX, radiusY: single; blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterBlurRadial(const ABounds: TRect; radiusX, radiusY: single; blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterBlurMotion(distance: single; angle: single; oriented: boolean; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterBlurMotion(const ABounds: TRect; distance: single; angle: single; oriented: boolean; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterCustomBlur(mask: TCustomUniversalBitmap; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterCustomBlur(const ABounds: TRect; mask: TCustomUniversalBitmap; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; override;
     function FilterEmboss(angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterEmboss(angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterGrayscale(ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterGrayscale(ABounds: TRect; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterNormalize(eachChannel: boolean = True; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterNormalize(ABounds: TRect; eachChannel: boolean = True; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterAffine(AMatrix: TAffineMatrix; correctBlur: boolean = false; ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterSphere(ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3; ACopyProperties: Boolean=False): TBGRACustomBitmap; overload; virtual; abstract;
     function FilterCylinder(ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     function FilterPlane(ACopyProperties: Boolean=False): TBGRACustomBitmap; virtual; abstract;
     {$ENDIF}

     //IBGRAScanner
     function ScanAtIntegerExpanded(X,Y: integer): TExpandedPixel; override;
     function ScanNextExpandedPixel: TExpandedPixel; override;
     function ScanAtExpanded(X,Y: Single): TExpandedPixel; override;
     function ProvidesScanline(ARect: TRect): boolean; override;
     function GetScanlineAt(X, Y: integer): PBGRAPixel; override;
     procedure ScanNextMaskChunk(var ACount: integer; out AMask: PByteMask; out AStride: integer); override;
     function ScanAtIntegerMask(X,Y: integer): TByteMask; override;
     function ScanAtMask(X,Y: Single): TByteMask; override;
  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}
function InternalGetImageBoundsWithin(ASourceBitmap: TBGRACustomBitmap; ASourceTexture: IBGRAScanner;
  const ARect: TRect; Channels: TChannels; ANothingValue: Byte): TRect;
var
  minx, miny, maxx, maxy: integer;
  xb, xb2, yb: integer;
  p:      PLongWord;
  colorMask, colorZeros: LongWord;
  actualRect: TRect;
  pixelBuffer: TBGRAPixelBuffer;
begin
  pixelBuffer := nil;
  if ASourceBitmap <> nil then
  begin
    actualRect := TRect.Intersect(ARect, rect(0,0,ASourceBitmap.Width,ASourceBitmap.Height));
  end
  else if ASourceTexture <> nil then
  begin
    actualRect := ARect;
    AllocateBGRAPixelBuffer(pixelBuffer, ARect.Right-ARect.Left)
  end
  else
  begin
    result := EmptyRect;
    exit;
  end;
  maxx := actualRect.Left-1;
  maxy := actualRect.Top-1;
  minx := actualRect.Right;
  miny := actualRect.Bottom;
  colorMask := 0;
  colorZeros := 0;
  if cBlue in Channels then
  begin
    colorMask := colorMask or LongWord(BGRA(0,0,255,0));
    colorZeros:= colorZeros or LongWord(BGRA(0,0,ANothingValue,0));
  end;
  if cGreen in Channels then
  begin
    colorMask := colorMask or LongWord(BGRA(0,255,0,0));
    colorZeros:= colorZeros or LongWord(BGRA(0,ANothingValue,0,0));
  end;
  if cRed in Channels then
  begin
    colorMask := colorMask or LongWord(BGRA(255,0,0,0));
    colorZeros:= colorZeros or LongWord(BGRA(ANothingValue,0,0,0));
  end;
  if cAlpha in Channels then
  begin
    colorMask := colorMask or LongWord(BGRA(0,0,0,255));
    colorZeros:= colorZeros or LongWord(BGRA(0,0,0,ANothingValue));
  end;
  colorMask := NtoLE(colorMask);
  colorZeros := NtoLE(colorZeros);
  for yb := actualRect.Top to actualRect.Bottom-1 do
  begin
    if ASourceBitmap <> nil then
      p := PLongWord(ASourceBitmap.ScanLine[yb])+actualRect.Left
    else
    begin
      p := @pixelBuffer[0];
      ASourceTexture.ScanMoveTo(actualRect.Left,actualRect.Top);
      ASourceTexture.ScanPutPixels(PBGRAPixel(p),ARect.Right-ARect.Left, dmSet);
    end;
    for xb := actualRect.Left to actualRect.Right - 1 do
    begin
      if (p^ and colorMask) <> colorZeros then
      begin
        if xb < minx then
          minx := xb;
        if yb < miny then
          miny := yb;
        if xb > maxx then
          maxx := xb;
        if yb > maxy then
          maxy := yb;

        inc(p, actualRect.Right-1-xb);
        for xb2 := actualRect.Right-1 downto xb+1 do
        begin
          if (p^ and colorMask) <> colorZeros then
          begin
            if xb2 > maxx then
              maxx := xb2;
            break;
          end;
          dec(p);
        end;
        break;
      end;
      Inc(p);
    end;
  end;
  if minx > maxx then
  begin
    Result.left   := 0;
    Result.top    := 0;
    Result.right  := 0;
    Result.bottom := 0;
  end
  else
  begin
    Result.left   := minx;
    Result.top    := miny;
    Result.right  := maxx + 1;
    Result.bottom := maxy + 1;
  end;
end;

{ 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.SetXorMask(AValue: TBGRACustomBitmap);
begin
  if FXorMask=AValue then Exit;
  if (AValue.Width <> Width) or (AValue.Height <> Height) then
    raise exception.Create('Dimension mismatch');
  DiscardXorMask;
  FXorMask:=AValue;
end;

procedure TBGRACustomBitmap.Init;
begin
  inherited Init;
  ScanMaskChannel:= cGreen;
end;

function TBGRACustomBitmap.GetFontRightToLeftFor(AText: string): boolean;
begin
  case FontBidiMode of
    fbmAuto: result := IsRightToLeftUTF8(AText);
    fbmRightToLeft: result := true;
  else
    {fbmLeftToRight}
    result := false;
  end;
end;

function TBGRACustomBitmap.NewBitmap: TBGRACustomBitmap;
begin
  Result:=inherited NewBitmap as TBGRACustomBitmap;
end;

function TBGRACustomBitmap.NewBitmap(AWidth, AHeight: integer): TBGRACustomBitmap;
begin
  Result:=inherited NewBitmap(AWidth, AHeight) as TBGRACustomBitmap;
end;

function TBGRACustomBitmap.NewBitmap(AWidth, AHeight: integer;
  const Color: TBGRAPixel): TBGRACustomBitmap;
begin
  Result:=inherited NewBitmap(AWidth, AHeight, Color) as TBGRACustomBitmap;
end;

function TBGRACustomBitmap.NewBitmap(AWidth, AHeight: integer; AColor: Pointer
  ): TBGRACustomBitmap;
begin
  Result:=inherited NewBitmap(AWidth, AHeight, AColor) as TBGRACustomBitmap;
end;

function TBGRACustomBitmap.InternalNew: TBGRACustomBitmap;
begin
  Result:= BGRABitmapFactory.Create;
end;

procedure TBGRACustomBitmap.DiscardXorMask;
begin
  if Assigned(FXorMask) then
  begin
    if FXorMask is TBGRACustomBitmap then
    begin
      TBGRACustomBitmap(FXorMask).FreeReference;
      FXorMask := nil;
    end else
      FreeAndNil(FXorMask);
  end;
end;

procedure TBGRACustomBitmap.NeedXorMask;
begin
  if FXorMask = nil then
    FXorMask := BGRABitmapFactory.Create(Width,Height);
end;

function TBGRACustomBitmap.NewReference: TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited NewReference);
end;

function TBGRACustomBitmap.GetUnique: TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited GetUnique);
end;

function TBGRACustomBitmap.Duplicate(DuplicateProperties: Boolean): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited Duplicate(DuplicateProperties));
end;

function TBGRACustomBitmap.Duplicate(DuplicateProperties,
  DuplicateXorMask: Boolean): TBGRACustomBitmap;
begin
  result := Duplicate(DuplicateProperties);
  if DuplicateXorMask and Assigned(XorMask) then
    result.XorMask := FXorMask.Duplicate(True);
end;

procedure TBGRACustomBitmap.CopyPropertiesTo(ABitmap: TCustomUniversalBitmap);
var
  other: TBGRACustomBitmap;
begin
  inherited CopyPropertiesTo(ABitmap);
  if ABitmap is TBGRACustomBitmap then
  begin
    other := TBGRACustomBitmap(ABitmap);
    other.CanvasOpacity := CanvasOpacity;
    other.CanvasDrawModeFP := CanvasDrawModeFP;
    other.PenStyle := PenStyle;
    other.CustomPenStyle := CustomPenStyle;
    other.FontName := FontName;
    other.FontHeight := FontHeight;
    other.FontStyle := FontStyle;
    other.FontQuality := FontQuality;
    other.FontOrientation := FontOrientation;
    other.FontVerticalAnchor:= FontVerticalAnchor;
    other.FontBidiMode:= FontBidiMode;
    other.LineCap := LineCap;
    other.JoinStyle := JoinStyle;
    other.ResampleFilter := ResampleFilter;
    other.ScanInterpolationFilter:= ScanInterpolationFilter;
    other.HotSpot := HotSpot;
  end;
end;

function TBGRACustomBitmap.GetPart(const ARect: TRect; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited GetPart(ARect, ACopyProperties));
end;

function TBGRACustomBitmap.CreateBrushTexture(ABrushStyle: TBrushStyle;
  APatternColor, ABackgroundColor: TBGRAPixel; AWidth: integer;
  AHeight: integer; APenWidth: single): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited CreateBrushTexture(ABrushStyle, APatternColor, ABackgroundColor, AWidth,AHeight,APenWidth));
end;

procedure TBGRACustomBitmap.SetSize(AWidth, AHeight: integer);
begin
  if (AWidth <> Width) or (AHeight <> Height) then
  begin
    inherited SetSize(AWidth, AHeight);
    DiscardXorMask;
  end;
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;

procedure TBGRACustomBitmap.DrawPixel(x, y: int32or64; const 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(AStream: TStream;
  AHandler: TFPCustomImageReader; AOptions: TBGRALoadingOptions);
var
  OldDrawMode: TDrawMode;
begin
  { 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 }
  OldDrawMode := CanvasDrawModeFP;
  CanvasDrawModeFP := dmSet;
  DiscardXorMask;
  try
    inherited LoadFromStream(AStream, AHandler, AOptions);
  finally
    CanvasDrawModeFP := OldDrawMode;
  end;
end;

{ Look for a pixel considering the bitmap is repeated in both directions }
procedure TBGRACustomBitmap.DrawHorizLine(x, y, x2: int32or64;
  texture: IBGRAScanner);
begin
  HorizLine(x,y,x2,texture,dmDrawWithTransparency);
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.RectangleWithin(x1, y1, x2, y2: single;
  ABorderColor: TBGRAPixel; w: single; AFillColor: TBGRAPixel;
  APixelCenteredCoordinates: boolean);
begin
  if not APixelCenteredCoordinates then
  begin
    DecF(x1, 0.5);
    DecF(y1, 0.5);
    DecF(x2, 0.5);
    DecF(y2, 0.5);
  end;
  RectangleAntialias(x1+w*0.5,y1+w*0.5,x2-w*0.5,y2-w*0.5, ABorderColor, w, AFillColor);
end;

procedure TBGRACustomBitmap.RectangleWithin(r: TRect; ABorderColor: TBGRAPixel;
  w: single; AFillColor: TBGRAPixel);
begin
  RectangleWithin(r.left,r.top,r.right,r.bottom,ABorderColor,w,AFillColor,false);
end;

procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
  mode: TDrawMode; ditheringAlgorithm: TDitheringAlgorithm);
begin
  FillRect(r.Left,r.Top,r.Right,r.Bottom, texture, mode, ditheringAlgorithm);
end;

procedure TBGRACustomBitmap.FillRect(r: TRect; texture: IBGRAScanner;
  mode: TDrawMode; AScanOffset: TPoint; ditheringAlgorithm: TDitheringAlgorithm);
begin
  FillRect(r.Left,r.Top,r.Right,r.Bottom, texture, mode, AScanOffset, ditheringAlgorithm);
end;

procedure TBGRACustomBitmap.FillRect(x, y, x2, y2: integer;
  texture: IBGRAScanner; mode: TDrawMode;
  ditheringAlgorithm: TDitheringAlgorithm);
begin
  FillRect(x,y,x2,y2,texture,mode,Point(0,0),ditheringAlgorithm);
end;

procedure TBGRACustomBitmap.TextOutCurved(APath: IBGRAPath; const 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; const 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;

{$IFNDEF BGRABITMAP_CORE}procedure TBGRACustomBitmap.TextMultiline(x, y: single; const sUTF8: string;
  c: TBGRAPixel; AAlign: TBidiTextAlignment; AVertAlign: TTextLayout; AParagraphSpacing: single);
begin
  TextMultiline(x, y, EmptySingle, sUTF8, c, AAlign, AVertAlign, AParagraphSpacing);
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}procedure TBGRACustomBitmap.TextMultiline(x, y: single; const sUTF8: string;
  ATexture: IBGRAScanner; AAlign: TBidiTextAlignment; AVertAlign: TTextLayout; AParagraphSpacing: single);
begin
  TextMultiline(x, y, EmptySingle, sUTF8, ATexture, AAlign, AVertAlign, AParagraphSpacing);
end;{$ENDIF}

procedure TBGRACustomBitmap.TextOut(x, y: single; const sUTF8: string; c: TBGRAPixel;
  align: TAlignment);
begin
  TextOut(x,y,sUTF8,c,align, GetFontRightToLeftFor(sUTF8));
end;

procedure TBGRACustomBitmap.TextOut(x, y: single; const sUTF8: string;
  texture: IBGRAScanner; align: TAlignment);
begin
  TextOut(x,y,sUTF8,texture,align, GetFontRightToLeftFor(sUTF8));
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; const sUTF8: string; c: TBGRAPixel);
begin
  TextOut(x, y, sUTF8, c, taLeftJustify);
end;

procedure TBGRACustomBitmap.TextOut(x, y: single; const sUTF8: string; c: TBGRAPixel;
  ARightToLeft: boolean);
begin
  TextOut(x, y, sUTF8, c, taLeftJustify, ARightToLeft);
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; const sUTF8: string; c: TColor);
begin
  TextOut(x, y, sUTF8, ColorToBGRA(c));
end;

procedure TBGRACustomBitmap.TextOut(x, y: single; const sUTF8: string; c: TColor;
  ARightToLeft: boolean);
begin
  TextOut(x, y, sUTF8, ColorToBGRA(c), ARightToLeft);
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; const sUTF8: string;
  texture: IBGRAScanner);
begin
  TextOut(x, y, sUTF8, texture, taLeftJustify);
end;

procedure TBGRACustomBitmap.TextOut(x, y: single; const sUTF8: string;
  texture: IBGRAScanner; ARightToLeft: boolean);
begin
  TextOut(x, y, sUTF8, texture, taLeftJustify, ARightToLeft);
end;

procedure TBGRACustomBitmap.TextOutAngle(x, y: single;
  orientationTenthDegCCW: integer; const sUTF8: string; c: TBGRAPixel);
begin
  TextOutAngle(x,y, orientationTenthDegCCW, sUTF8,c,taLeftJustify);
end;

procedure TBGRACustomBitmap.TextOutAngle(x, y: single;
  orientationTenthDegCCW: integer; const sUTF8: string; c: TBGRAPixel;
  align: TAlignment);
begin
  TextOutAngle(x,y, orientationTenthDegCCW, sUTF8,c,align, GetFontRightToLeftFor(sUTF8));
end;

procedure TBGRACustomBitmap.TextOutAngle(x, y: single;
  orientationTenthDegCCW: integer; const sUTF8: string; texture: IBGRAScanner);
begin
  TextOutAngle(x,y, orientationTenthDegCCW, sUTF8,texture,taLeftJustify);
end;

procedure TBGRACustomBitmap.TextOutAngle(x, y: single;
  orientationTenthDegCCW: integer; const sUTF8: string; texture: IBGRAScanner;
  align: TAlignment);
begin
  TextOutAngle(x,y, orientationTenthDegCCW, sUTF8,texture,align, GetFontRightToLeftFor(sUTF8));
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; const sUTF8: string;
  halign: TAlignment; valign: TTextLayout; c: TBGRAPixel);
var
  style: TTextStyle;
  sUTF8bidi: String;
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;
  style.RightToLeft := GetFontRightToLeftFor(sUTF8);
  if FontBidiMode = fbmAuto then
    sUTF8bidi := AddParagraphBidiUTF8(sUTF8, style.RightToLeft)
    else sUTF8bidi := sUTF8;
  TextRect(ARect, ARect.Left, ARect.Top, sUTF8bidi, 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; const sUTF8: string;
  halign: TAlignment; valign: TTextLayout; texture: IBGRAScanner);
var
  style: TTextStyle;
  sUTF8bidi: String;
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;
  style.RightToLeft := GetFontRightToLeftFor(sUTF8);
  if FontBidiMode = fbmAuto then
    sUTF8bidi := AddParagraphBidiUTF8(sUTF8, style.RightToLeft)
    else sUTF8bidi := sUTF8;
  TextRect(ARect,ARect.Left,ARect.Top,sUTF8bidi,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.Fill(texture: IBGRAScanner);
begin
  FillRect(ClipRect, texture, dmSet);
end;

procedure TBGRACustomBitmap.Fill(texture: IBGRAScanner; mode: TDrawMode);
begin
  FillRect(ClipRect, texture, mode);
end;

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

procedure TBGRACustomBitmap.FloodFill(X, Y: integer;
  const Brush: TUniversalBrush; Progressive: boolean; ToleranceW: Word);
begin
  ParallelFloodFill(X,Y, Self, Brush, Progressive, ToleranceW);
end;

procedure TBGRACustomBitmap.DrawPart(ARect: TRect; ACanvas: TCanvas; x,
  y: integer; Opaque: boolean);
var
  partial: TBGRACustomBitmap;
begin
  if (ARect.Left = 0) and (ARect.Top = 0) and (ARect.Right = Width) and (ARect.Bottom = Height) then
    Draw(ACanvas, x,y, Opaque)
  else
  begin
    partial := GetPart(ARect);
    if partial <> nil then
    begin
      partial.Draw(ACanvas, x, y, Opaque);
      partial.Free;
    end;
  end;
end;

procedure TBGRACustomBitmap.DrawPart(ARect: TRect; ACanvas: TCanvas;
  ATargetRect: TRect; Opaque: boolean);
var
  partial: TBGRACustomBitmap;
begin
  if (ARect.Left = 0) and (ARect.Top = 0) and (ARect.Right = Width) and (ARect.Bottom = Height) then
    Draw(ACanvas, ATargetRect, Opaque)
  else
  begin
    partial := GetPart(ARect);
    if partial <> nil then
    begin
      partial.Draw(ACanvas, ATargetRect, Opaque);
      partial.Free;
    end;
  end;
end;

procedure TBGRACustomBitmap.PutImage(x, y: integer; Source: TBitmap;
  mode: TDrawMode; AOpacity: byte);
var bgra: TBGRACustomBitmap;
begin
  bgra := BGRABitmapFactory.create(Source);
  PutImage(x,y, bgra, mode, AOpacity);
  bgra.free;
end;

procedure TBGRACustomBitmap.StretchPutImage(ARect: TRect; Source: TBitmap;
  mode: TDrawMode; AOpacity: byte);
var bgra: TBGRACustomBitmap;
begin
  bgra := BGRABitmapFactory.create(Source);
  StretchPutImage(ARect, bgra, mode, AOpacity);
  bgra.free;
end;

procedure TBGRACustomBitmap.StretchPutImageProportionally(ARect: TRect;
  AHorizAlign: TAlignment; AVertAlign: TTextLayout; Source: TBGRACustomBitmap;
  mode: TDrawMode; AOpacity: byte; ACover: boolean);
var
  ratio: single;
  imgRect: TRect;
begin
  if (Source.Width = 0) or (Source.Height = 0) then exit;
  if (ARect.Width <= 0) or (ARect.Height <= 0) then exit;

  if ACover then
    ratio := max(ARect.Width/Source.Width, ARect.Height/Source.Height)
  else
    ratio := min(ARect.Width/Source.Width, ARect.Height/Source.Height);
  imgRect := RectWithSize(ARect.Left,ARect.Top, round(Source.Width*ratio), round(Source.Height*ratio));
  case AHorizAlign of
    taCenter: imgRect.Offset((ARect.Width-imgRect.Width) div 2, 0);
    taRightJustify: imgRect.Offset(ARect.Width-imgRect.Width, 0);
  end;
  case AVertAlign of
    tlCenter: imgRect.Offset(0,(ARect.Height-imgRect.Height) div 2);
    tlBottom: imgRect.Offset(0,ARect.Height-imgRect.Height);
  end;
  StretchPutImage(imgRect, Source, mode, AOpacity);
end;

procedure TBGRACustomBitmap.PutImageSubpixel(x, y: single; Source: TBGRACustomBitmap; AOpacity: byte);
begin
  PutImageAngle(x,y,source,0,0,0,AOpacity);
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);
  newClip.Intersect(oldClip);
  if newClip.IsEmpty 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);
begin
  if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit;
  PutImageAffine(Origin,HAxis,VAxis,Source,GetImageAffineBounds(Origin,HAxis,VAxis,Source),AResampleFilter,dmDrawWithTransparency,AOpacity);
end;

procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter;
  AMode: TDrawMode; AOpacity: Byte);
begin
  if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit;
  PutImageAffine(Origin,HAxis,VAxis,Source,GetImageAffineBounds(Origin,HAxis,VAxis,Source),AResampleFilter,AMode,AOpacity);
end;

procedure TBGRACustomBitmap.PutImageAffine(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap; AOutputBounds: TRect;
  AResampleFilter: TResampleFilter; AMode: TDrawMode; AOpacity: Byte);
var m: TAffineMatrix; w,h: integer;
begin
  if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit;
  if Source.Width < 2 then w := 2 else w := Source.Width; //avoid actual size of zero
  if Source.Height < 2 then h := 2 else h := Source.Height;
  m[1,1] := (HAxis.x-Origin.x)/(w-1); m[1,2] := (VAxis.x-Origin.x)/(h-1); m[1,3] := Origin.x;
  m[2,1] := (HAxis.y-Origin.y)/(w-1); m[2,2] := (VAxis.y-Origin.y)/(h-1); m[2,3] := Origin.y;
  PutImageAffine(m,Source,AOutputBounds,AResampleFilter,AMode,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;

procedure TBGRACustomBitmap.PutImageAffine(AMatrix: TAffineMatrix;
  Source: TBGRACustomBitmap; AOpacity: Byte; ACorrectBlur: Boolean; APixelCenteredCoords: boolean);
begin
  if ACorrectBlur then
    PutImageAffine(AMatrix,Source,rfCosine,AOpacity,APixelCenteredCoords)
  else
    PutImageAffine(AMatrix,Source,rfLinear,AOpacity,APixelCenteredCoords);
end;

procedure TBGRACustomBitmap.PutImageAffine(AMatrix: TAffineMatrix;
  Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter; AOpacity: Byte; APixelCenteredCoords: boolean);
begin
  PutImageAffine(AMatrix, Source, AResampleFilter, dmDrawWithTransparency, AOpacity, APixelCenteredCoords);
end;

procedure TBGRACustomBitmap.PutImageAffine(AMatrix: TAffineMatrix;
  Source: TBGRACustomBitmap; AResampleFilter: TResampleFilter;
  AMode: TDrawMode; AOpacity: Byte; APixelCenteredCoords: boolean);
begin
  if (Source = nil) or (Source.Width = 0) or (Source.Height = 0) or (AOpacity = 0) then exit;
  PutImageAffine(AMatrix, Source, GetImageAffineBounds(AMatrix,Source),
                 AResampleFilter,AMode,AOpacity,APixelCenteredCoords);
end;

procedure TBGRACustomBitmap.PutImageAffine(AMatrix: TAffineMatrix;
  Source: TBGRACustomBitmap; AOutputBounds: TRect; AOpacity: Byte;
  ACorrectBlur: Boolean; APixelCenteredCoords: boolean);
begin
  if ACorrectBlur then
    PutImageAffine(AMatrix,Source,AOutputBounds,rfCosine,dmDrawWithTransparency,AOpacity,APixelCenteredCoords)
  else
    PutImageAffine(AMatrix,Source,AOutputBounds,rfLinear,dmDrawWithTransparency,AOpacity,APixelCenteredCoords);
end;

{ Returns the area that contains the affine transformed image }
function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
  Source: TBGRACustomBitmap): TRect;
begin
  if Source = nil then
    result := EmptyRect
  else
    result := GetImageAffineBounds(Origin,HAxis,VAxis,Source.Width,Source.Height,Source.GetImageBounds);
end;

function TBGRACustomBitmap.GetImageAffineBounds(Origin, HAxis, VAxis: TPointF;
  ASourceWidth, ASourceHeight: integer; const ASourceBounds: TRect; AClipOutput: boolean): TRect;
var m: TAffineMatrix;
begin
  if (ASourceWidth = 0) or (ASourceHeight = 0) then
    result := EmptyRect
  else
  begin
    if ASourceWidth < 2 then ASourceWidth := 2;   //avoid division by zero by supposing a pixel size of 2
    if ASourceHeight < 2 then ASourceHeight := 2; //i.e. an actual size of 1 (cf pixel centered coordinates)
    m[1,1] := (HAxis.x-Origin.x)/(ASourceWidth-1); m[1,2] := (VAxis.x-Origin.x)/(ASourceHeight-1); m[1,3] := Origin.x;
    m[2,1] := (HAxis.y-Origin.y)/(ASourceWidth-1); m[2,2] := (VAxis.y-Origin.y)/(ASourceHeight-1); m[2,3] := Origin.y;
    result := GetImageAffineBounds(m, ASourceBounds, AClipOutput);
  end;
end;

function TBGRACustomBitmap.GetImageAffineBounds(AMatrix: TAffineMatrix;
  Source: TBGRACustomBitmap; APixelCenteredCoords: boolean): TRect;
begin
  result := GetImageAffineBounds(AMatrix, Source.GetImageBounds, true, APixelCenteredCoords);
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
    DecF(relX, imageCenterX);
    DecF(relY, imageCenterY);
    result.x := relX*cosa - relY*sina+ x;
    result.y := relY*cosa + relX*sina+ y;
    if ARestoreOffsetAfterRotation then
      result.Offset(imageCenterX,imageCenterY);
  end;

begin
  cosa := cos(-angle*Pi/180);
  sina := -sin(-angle*Pi/180);
  Origin := Coord(0,0);
  if w < 2 then w := 2; //when pixel size is 1, actual size is zero, so avoid that
  if h < 2 then h := 2;
  HAxis := Coord(w-1,0);
  VAxis := Coord(0,h-1);
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
    DecF(relX, imageCenterX);
    DecF(relY, imageCenterY);
    result.x := relX*cosa - relY*sina + x;
    result.y := relY*cosa + relX*sina + y;
    if ARestoreOffsetAfterRotation then
      result.Offset(imageCenterX,imageCenterY);
  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.Blend(AColor: TBGRAPixel;
  AOperation: TBlendOperation; AIgnoreDestAlpha: boolean);
begin
  BlendRect(ClipRect, AColor, AOperation, AIgnoreDestAlpha);
end;

procedure TBGRACustomBitmap.BlendOver(AColor: TBGRAPixel;
  AOperation: TBlendOperation; AOpacity: byte; ALinearBlend: boolean; AIgnoreDestAlpha: boolean);
begin
  BlendRectOver(ClipRect, AColor, AOperation, AOpacity, ALinearBlend, AIgnoreDestAlpha);
end;

procedure TBGRACustomBitmap.BlendRect(ADest: TRect; AColor: TBGRAPixel;
  AOperation: TBlendOperation; AIgnoreDestAlpha: boolean);
begin
  if AIgnoreDestAlpha then
    BlendRect(ADest, AColor, AOperation, [cAlpha])
    else BlendRect(ADest, AColor, AOperation, []);
end;

procedure TBGRACustomBitmap.BlendRectOver(ADest: TRect; AColor: TBGRAPixel;
  AOperation: TBlendOperation; AOpacity: byte; ALinearBlend: boolean;
  AIgnoreDestAlpha: boolean);
begin
  if AIgnoreDestAlpha then
    BlendRectOver(ADest, AColor, AOperation, AOpacity, ALinearBlend,[cAlpha])
    else BlendRectOver(ADest, AColor, AOperation, AOpacity, ALinearBlend, []);
end;

procedure TBGRACustomBitmap.FillMask(x, y: integer;
  AMask: TCustomUniversalBitmap; ATexture: IBGRAScanner; ADrawMode: TDrawMode);
begin
  FillMask(x, y, AMask, ATexture, ADrawMode, 255);
end;

procedure TBGRACustomBitmap.VerticalFlip(ARect: TRect);
begin
  inherited VerticalFlip(ARect);
  if Assigned(XorMask) then XorMask.VerticalFlip(ARect);
end;

procedure TBGRACustomBitmap.HorizontalFlip(ARect: TRect);
begin
  inherited HorizontalFlip(ARect);
  if Assigned(XorMask) then XorMask.HorizontalFlip(ARect);
end;

procedure TBGRACustomBitmap.RotateUDInplace(ARect: TRect);
begin
  inherited RotateUDInplace(ARect);
  if Assigned(XorMask) then XorMask.RotateUDInplace(ARect);
end;

function TBGRACustomBitmap.RotateCW(ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(Inherited RotateCW(ACopyProperties));
  if Assigned(XorMask) then result.FXorMask := self.XorMask.RotateCW(ACopyProperties);
end;

function TBGRACustomBitmap.RotateCCW(ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(Inherited RotateCCW(ACopyProperties));
  if Assigned(XorMask) then result.FXorMask := self.XorMask.RotateCCW(ACopyProperties);
end;

function TBGRACustomBitmap.RotateUD(ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(Inherited RotateUD(ACopyProperties));
  if Assigned(XorMask) then result.FXorMask := self.XorMask.RotateUD(ACopyProperties);
end;

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurRadial(radius: single;
  blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurRadial(radius, blurType, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurRadial(const ABounds: TRect; radius: single;
  blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurRadial(ABounds, radius, blurType, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurRadial(radiusX, radiusY: single;
  blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurRadial(radiusX,radiusY, blurType, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurRadial(const ABounds: TRect; radiusX,
  radiusY: single; blurType: TRadialBlurType; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurRadial(ABounds, radiusX,radiusY, blurType, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurMotion(distance: single; angle: single;
  oriented: boolean; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurMotion(distance, angle, oriented, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterBlurMotion(const ABounds: TRect; distance: single;
  angle: single; oriented: boolean; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterBlurMotion(ABounds, distance, angle, oriented, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterCustomBlur(mask: TCustomUniversalBitmap; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterCustomBlur(mask, ACopyProperties));
end;{$ENDIF}

{$IFNDEF BGRABITMAP_CORE}function TBGRACustomBitmap.FilterCustomBlur(const ABounds: TRect;
  mask: TCustomUniversalBitmap; ACopyProperties: Boolean=False): TBGRACustomBitmap;
begin
  result := TBGRACustomBitmap(inherited FilterCustomBlur(ABounds,mask, ACopyProperties));
end;{$ENDIF}

function TBGRACustomBitmap.GetImageBoundsWithin(const ARect: TRect;
  Channel: TChannel; ANothingValue: Byte): TRect;
begin
  result := InternalGetImageBoundsWithin(self,nil,ARect,[Channel],ANothingValue);
end;

function TBGRACustomBitmap.GetImageBoundsWithin(const ARect: TRect;
  Channels: TChannels; ANothingValue: Byte): TRect;
begin
  result := InternalGetImageBoundsWithin(self,nil,ARect,Channels,ANothingValue);
end;

function TBGRACustomBitmap.ScanAtIntegerExpanded(X, Y: integer): TExpandedPixel;
begin
  result := GammaExpansion(ScanAtInteger(X,Y));
end;

function TBGRACustomBitmap.ScanNextExpandedPixel: TExpandedPixel;
begin
  result := GammaExpansion(ScanNextPixel);
end;

function TBGRACustomBitmap.ScanAtExpanded(X, Y: Single): TExpandedPixel;
begin
  result := GammaExpansion(ScanAt(X,Y));
end;

function TBGRACustomBitmap.ProvidesScanline(ARect: TRect): boolean;
begin
  result := (ARect.Left+ScanOffset.x >= 0) and (ARect.Top+ScanOffset.y >= 0) and
      (ARect.Right+ScanOffset.x <= Width) and (ARect.Bottom+ScanOffset.y <= Height);
end;

function TBGRACustomBitmap.GetScanlineAt(X, Y: integer): PBGRAPixel;
begin
  result := ScanLine[y+ScanOffset.y]+x+ScanOffset.x;
end;

procedure TBGRACustomBitmap.ScanNextMaskChunk(var ACount: integer; out AMask: PByteMask; out AStride: integer);
var
  PPixels: Pointer;
begin
  ScanNextCustomChunk(ACount, PPixels);
  AMask := (PByteMask(PPixels)+TBGRAPixel_ChannelByteOffset[ScanMaskChannel]);
  AStride := sizeof(TBGRAPixel);
end;

function TBGRACustomBitmap.ScanAtIntegerMask(X,Y: integer): TByteMask;
var
  c: TBGRAPixel;
begin
  c := ScanAtInteger(X,Y);
  Byte(result) := (PByte(@c)+TBGRAPixel_ChannelByteOffset[ScanMaskChannel])^;
end;

function TBGRACustomBitmap.ScanAtMask(X,Y: Single): TByteMask;
var
  c: TBGRAPixel;
begin
  c := ScanAt(X,Y);
  Byte(result) := (PByte(@c)+TBGRAPixel_ChannelByteOffset[ScanMaskChannel])^;
end;

{$ENDIF}
