// SPDX-License-Identifier: LGPL-3.0-linking-exception
{
 /**************************************************************************\
                                bgrabitmap.pas
                                --------------
                 Free easy-to-use memory bitmap 32-bit,
                 8-bit for each channel, transparency.
                 Channels can be in the following orders:
                 - B G R A (recommended for Windows, required for fpGUI)
                 - R G B A (recommended for Gtk and MacOS)

                 - Drawing primitives
                 - Resample
                 - Reference counter
                 - Drawing on LCL canvas
                 - Loading and saving images

                 Note : line order can change, so if you access
                 directly to bitmap data, check LineOrder value
                 or use Scanline to compute position.


       --> Include BGRABitmap and BGRABitmapTypes in the 'uses' clause.
}

unit BGRABitmap;

{$mode objfpc}{$H+}
{$i bgrabitmap.inc}

interface

{ Compiler directives are used to include the best version according
  to the platform }

uses
  BGRAClasses, BGRABitmapTypes, FPImage, SysUtils,
{$IFDEF BGRABITMAP_USE_FPGUI}
    BGRAfpGUIBitmap,
{$ELSE}
	{$IFDEF BGRABITMAP_USE_LCL}
	  {$IFDEF LCLwin32}
		BGRAWinBitmap,
	  {$ELSE}
		{$IFDEF LCLgtk}
		BGRAGtkBitmap,
		{$ELSE}
		  {$IFDEF LCLgtk2}
		BGRAGtkBitmap,
		  {$ELSE}
			{$IF defined(LCLqt) or defined(LCLqt5)}
		BGRAQtBitmap,
			{$ELSE}
              {$IFDEF DARWIN}
                BGRAMacBitmap,
              {$ELSE}
		BGRALCLBitmap,
              {$ENDIF}
			{$ENDIF}
		  {$ENDIF}
		{$ENDIF}
	  {$ENDIF}
	{$ELSE}
	  {$IFDEF BGRABITMAP_USE_MSEGUI}
            BGRAMSEguiBitmap,
          {$ELSE}
            BGRANoGuiBitmap,
          {$ENDIF}
	{$ENDIF}
{$ENDIF}
  BGRAGraphics;

type
{$IFDEF BGRABITMAP_USE_FPGUI}
  TBGRABitmap = class(TBGRAfpGUIBitmap)
{$ELSE}
    {$IFDEF BGRABITMAP_USE_LCL}
      {$IFDEF LCLwin32}
        TBGRABitmap = class(TBGRAWinBitmap)
      {$ELSE}
        {$IFDEF LCLgtk}
        TBGRABitmap = class(TBGRAGtkBitmap)
        {$ELSE}
          {$IFDEF LCLgtk2}

        { TBGRABitmap }

        TBGRABitmap = class(TBGRAGtkBitmap)
          {$ELSE}
            {$IF defined(LCLqt) or defined(LCLqt5)}
        TBGRABitmap = class(TBGRAQtBitmap)
            {$ELSE}
              {$IFDEF DARWIN}
        TBGRABitmap = class(TBGRAMacBitmap)
              {$ELSE}
        TBGRABitmap = class(TBGRALCLBitmap)
              {$ENDIF}
            {$ENDIF}
          {$ENDIF}
        {$ENDIF}
      {$ENDIF}
    {$ELSE}
      {$IFDEF BGRABITMAP_USE_MSEGUI}
        TBGRABitmap = class(TBGRAMSEguiBitmap)
      {$ELSE}
        TBGRABitmap = class(TBGRANoGUIBitmap)
      {$ENDIF}
    {$ENDIF}
{$ENDIF}
  public
    function NewBitmap: TBGRABitmap; overload; override;
    function NewBitmap(AWidth, AHeight: integer): TBGRABitmap; overload; override;
    function NewBitmap(AWidth, AHeight: integer; const Color: TBGRAPixel): TBGRABitmap; overload; override;
    function NewBitmap(AWidth, AHeight: integer; AColor: Pointer): TBGRABitmap; overload; override;
    function NewBitmap(Filename: string): TBGRABitmap; overload; override;
    function NewBitmap(Filename: string; AIsUtf8: boolean): TBGRABitmap; overload; override;
    function NewBitmap(Filename: string; AIsUtf8: boolean; AOptions: TBGRALoadingOptions): TBGRABitmap; overload; override;
    function NewBitmap(AFPImage: TFPCustomImage): TBGRABitmap; overload; override;
    function NewReference: TBGRABitmap; override;
    function GetUnique: TBGRABitmap; override;
    function Duplicate(DuplicateProperties: Boolean = False): TBGRABitmap; overload; override;
    function Duplicate(DuplicateProperties, DuplicateXorMask: Boolean) : TBGRABitmap; overload; override;
    function GetPart(const ARect: TRect): TBGRABitmap; override;
    function CreateBrushTexture(ABrushStyle: TBrushStyle; APatternColor, ABackgroundColor: TBGRAPixel;
                AWidth: integer = 8; AHeight: integer = 8; APenWidth: single = 1): TBGRABitmap; override;
    function Resample(newWidth, newHeight: integer;
      mode: TResampleMode = rmFineResample): TBGRABitmap; override;
    function RotateCW: TBGRABitmap; override;
    function RotateCCW: TBGRABitmap; override;
    function RotateUD: TBGRABitmap; override;
    function FilterSmartZoom3(Option: TMedianOption): TBGRABitmap; override;
    function FilterMedian(Option: TMedianOption): TBGRABitmap; override;
    function FilterSmooth: TBGRABitmap; override;
    function FilterSharpen(Amount: single = 1): TBGRABitmap; overload; override;
    function FilterSharpen(ABounds: TRect; Amount: single = 1): TBGRABitmap; overload; override;
    function FilterContour(AGammaCorrection: boolean = false): TBGRABitmap; override;
    function FilterPixelate(pixelSize: integer; useResample: boolean; filter: TResampleFilter = rfLinear): TBGRABitmap; override;
    function FilterBlurRadial(radius: single; blurType: TRadialBlurType): TBGRABitmap; overload; override;
    function FilterBlurRadial(const ABounds: TRect; radius: single; blurType: TRadialBlurType): TBGRABitmap; overload; override;
    function FilterBlurRadial(radiusX, radiusY: single; blurType: TRadialBlurType): TBGRABitmap; overload; override;
    function FilterBlurRadial(const ABounds: TRect; radiusX, radiusY: single; blurType: TRadialBlurType): TBGRABitmap; overload; override;
    function FilterBlurMotion(distance: single; angle: single; oriented: boolean): TBGRABitmap; overload; override;
    function FilterBlurMotion(const ABounds: TRect; distance: single; angle: single; oriented: boolean): TBGRABitmap; overload; override;
    function FilterCustomBlur(mask: TCustomUniversalBitmap): TBGRABitmap; overload; override;
    function FilterCustomBlur(const ABounds: TRect; mask: TCustomUniversalBitmap): TBGRABitmap; overload; override;
    function FilterEmboss(angle: single; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRABitmap; overload; override;
    function FilterEmboss(angle: single; ABounds: TRect; AStrength: integer= 64; AOptions: TEmbossOptions = []): TBGRABitmap; overload; override;
    function FilterEmbossHighlight(FillSelection: boolean): TBGRABitmap; overload; override;
    function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel): TBGRABitmap; overload; override;
    function FilterEmbossHighlight(FillSelection: boolean; BorderColor: TBGRAPixel; var Offset: TPoint): TBGRABitmap; overload; override;
    function FilterGrayscale: TBGRABitmap; overload; override;
    function FilterGrayscale(ABounds: TRect): TBGRABitmap; overload; override;
    function FilterNormalize(eachChannel: boolean = True): TBGRABitmap; overload; override;
    function FilterNormalize(ABounds: TRect; eachChannel: boolean = True): TBGRABitmap; overload; override;
    function FilterRotate(origin: TPointF; angle: single; correctBlur: boolean = false): TBGRABitmap; override;
    function FilterAffine(AMatrix: TAffineMatrix; correctBlur: boolean = false): TBGRABitmap; override;
    function FilterSphere: TBGRABitmap; override;
    function FilterTwirl(ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRABitmap; overload; override;
    function FilterTwirl(ABounds: TRect; ACenter: TPoint; ARadius: Single; ATurn: Single=1; AExponent: Single=3): TBGRABitmap; overload; override;
    function FilterCylinder: TBGRABitmap; override;
    function FilterPlane: TBGRABitmap; override;
  end;

// draw a bitmap from pure data
procedure BGRABitmapDraw(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
  VerticalFlip: boolean; AWidth, AHeight: integer; Opaque: boolean);
  
{ Replace the content of the variable Destination with the variable
  Temp and frees previous object contained in Destination.
  
  This function is useful as a shortcut for :
 
  var
    temp: TBGRABitmap;
  begin
    ...
    temp := someBmp.Filter... as TBGRABitmap;
    someBmp.Free;
    someBmp := temp;
  end;
  
  which becomes :
  
  begin
    ...
    BGRAReplace(someBmp, someBmp.Filter... );
  end;
}
procedure BGRAReplace(var Destination: TBGRABitmap; Temp: TObject);

implementation

uses BGRAReadBMP, BGRAReadBmpMioMap, BGRAReadGif,
  BGRAReadIco, BGRAReadJpeg, BGRAReadLzp, BGRAReadPCX,
  BGRAReadPng, BGRAWritePNG, BGRAReadPSD, BGRAReadTGA, BGRAReadXPM,
  BGRAWriteLzp, BGRAReadWebP, BGRAWriteWebP, BGRAReadAVIF, BGRAWriteAVIF;

var
  tempBmp: TBGRABitmap;

procedure BGRABitmapDraw(ACanvas: TCanvas; Rect: TRect; AData: Pointer;
  VerticalFlip: boolean; AWidth, AHeight: integer; Opaque: boolean);
var
  LineOrder: TRawImageLineOrder;
begin
  if tempBmp = nil then
    tempBmp := TBGRABitmap.Create;
  if VerticalFlip then
    LineOrder := riloBottomToTop
  else
    LineOrder := riloTopToBottom;
  if Opaque then
    tempBmp.DataDrawOpaque(ACanvas, Rect, AData, LineOrder, AWidth, AHeight)
  else
    tempBmp.DataDrawTransparent(ACanvas, Rect, AData, LineOrder, AWidth, AHeight);
end;

procedure BGRAReplace(var Destination: TBGRABitmap; Temp: TObject);
begin
  Destination.Free;
  Destination := Temp as TBGRABitmap;
end;

{ TBGRABitmap }

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

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

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

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

function TBGRABitmap.NewBitmap(Filename: string): TBGRABitmap;
begin
  Result:=inherited NewBitmap(Filename) as TBGRABitmap;
end;

function TBGRABitmap.NewBitmap(Filename: string; AIsUtf8: boolean): TBGRABitmap;
begin
  Result:=inherited NewBitmap(Filename, AIsUtf8) as TBGRABitmap;
end;

function TBGRABitmap.NewBitmap(Filename: string; AIsUtf8: boolean;
  AOptions: TBGRALoadingOptions): TBGRABitmap;
begin
  Result:=inherited NewBitmap(Filename, AIsUtf8, AOptions) as TBGRABitmap;
end;

function TBGRABitmap.NewBitmap(AFPImage: TFPCustomImage): TBGRABitmap;
begin
  Result:=inherited NewBitmap(AFPImage) as TBGRABitmap;
end;

function TBGRABitmap.NewReference: TBGRABitmap;
begin
  Result:=inherited NewReference as TBGRABitmap;
end;

function TBGRABitmap.GetUnique: TBGRABitmap;
begin
  Result:=inherited GetUnique as TBGRABitmap;
end;

function TBGRABitmap.Duplicate(DuplicateProperties: Boolean): TBGRABitmap;
begin
  Result:=inherited Duplicate(DuplicateProperties) as TBGRABitmap;
end;

function TBGRABitmap.Duplicate(DuplicateProperties, DuplicateXorMask: Boolean
  ): TBGRABitmap;
begin
  Result:=inherited Duplicate(DuplicateProperties, DuplicateXorMask) as TBGRABitmap;
end;

function TBGRABitmap.GetPart(const ARect: TRect): TBGRABitmap;
begin
  Result:=inherited GetPart(ARect) as TBGRABitmap;
end;

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

function TBGRABitmap.Resample(newWidth, newHeight: integer; mode: TResampleMode
  ): TBGRABitmap;
begin
  Result:=inherited Resample(newWidth, newHeight, mode) as TBGRABitmap;
end;

function TBGRABitmap.RotateCW: TBGRABitmap;
begin
  Result:=inherited RotateCW as TBGRABitmap;
end;

function TBGRABitmap.RotateCCW: TBGRABitmap;
begin
  Result:=inherited RotateCCW as TBGRABitmap;
end;

function TBGRABitmap.RotateUD: TBGRABitmap;
begin
  Result:=inherited RotateUD as TBGRABitmap;
end;

function TBGRABitmap.FilterSmartZoom3(Option: TMedianOption): TBGRABitmap;
begin
  Result:=inherited FilterSmartZoom3(Option) as TBGRABitmap;
end;

function TBGRABitmap.FilterMedian(Option: TMedianOption): TBGRABitmap;
begin
  Result:=inherited FilterMedian(Option) as TBGRABitmap;
end;

function TBGRABitmap.FilterSmooth: TBGRABitmap;
begin
  Result:=inherited FilterSmooth as TBGRABitmap;
end;

function TBGRABitmap.FilterSharpen(Amount: single): TBGRABitmap;
begin
  Result:=inherited FilterSharpen(Amount) as TBGRABitmap;
end;

function TBGRABitmap.FilterSharpen(ABounds: TRect; Amount: single): TBGRABitmap;
begin
  Result:=inherited FilterSharpen(ABounds, Amount) as TBGRABitmap;
end;

function TBGRABitmap.FilterContour(AGammaCorrection: boolean = false): TBGRABitmap;
begin
  Result:=inherited FilterContour(AGammaCorrection) as TBGRABitmap;
end;

function TBGRABitmap.FilterPixelate(pixelSize: integer; useResample: boolean;
  filter: TResampleFilter): TBGRABitmap;
begin
  Result:=inherited FilterPixelate(pixelSize, useResample, filter) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurRadial(radius: single; blurType: TRadialBlurType
  ): TBGRABitmap;
begin
  Result:=inherited FilterBlurRadial(radius, blurType) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurRadial(const ABounds: TRect; radius: single;
  blurType: TRadialBlurType): TBGRABitmap;
begin
  Result:=inherited FilterBlurRadial(ABounds, radius, blurType) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurRadial(radiusX, radiusY: single;
  blurType: TRadialBlurType): TBGRABitmap;
begin
  Result:=inherited FilterBlurRadial(radiusX, radiusY, blurType) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurRadial(const ABounds: TRect; radiusX, radiusY: single;
  blurType: TRadialBlurType): TBGRABitmap;
begin
  Result:=inherited FilterBlurRadial(ABounds, radiusX, radiusY, blurType) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurMotion(distance: single; angle: single;
  oriented: boolean): TBGRABitmap;
begin
  Result:=inherited FilterBlurMotion(distance, angle, oriented) as TBGRABitmap;
end;

function TBGRABitmap.FilterBlurMotion(const ABounds: TRect; distance: single;
  angle: single; oriented: boolean): TBGRABitmap;
begin
  Result:=inherited FilterBlurMotion(ABounds, distance, angle, oriented) as TBGRABitmap;
end;

function TBGRABitmap.FilterCustomBlur(mask: TCustomUniversalBitmap
  ): TBGRABitmap;
begin
  Result:=inherited FilterCustomBlur(mask) as TBGRABitmap;
end;

function TBGRABitmap.FilterCustomBlur(const ABounds: TRect;
  mask: TCustomUniversalBitmap): TBGRABitmap;
begin
  Result:=inherited FilterCustomBlur(ABounds, mask) as TBGRABitmap;
end;

function TBGRABitmap.FilterEmboss(angle: single; AStrength: integer;
  AOptions: TEmbossOptions): TBGRABitmap;
begin
  Result:=inherited FilterEmboss(angle, AStrength, AOptions) as TBGRABitmap;
end;

function TBGRABitmap.FilterEmboss(angle: single; ABounds: TRect;
  AStrength: integer; AOptions: TEmbossOptions): TBGRABitmap;
begin
  Result:=inherited FilterEmboss(angle, ABounds, AStrength, AOptions) as TBGRABitmap;
end;

function TBGRABitmap.FilterEmbossHighlight(FillSelection: boolean): TBGRABitmap;
begin
  Result:=inherited FilterEmbossHighlight(FillSelection) as TBGRABitmap;
end;

function TBGRABitmap.FilterEmbossHighlight(FillSelection: boolean;
  BorderColor: TBGRAPixel): TBGRABitmap;
begin
  Result:=inherited FilterEmbossHighlight(FillSelection, BorderColor) as TBGRABitmap;
end;

function TBGRABitmap.FilterEmbossHighlight(FillSelection: boolean;
  BorderColor: TBGRAPixel; var Offset: TPoint): TBGRABitmap;
begin
  Result:=inherited FilterEmbossHighlight(FillSelection, BorderColor, Offset) as TBGRABitmap;
end;

function TBGRABitmap.FilterGrayscale: TBGRABitmap;
begin
  Result:=inherited FilterGrayscale as TBGRABitmap;
end;

function TBGRABitmap.FilterGrayscale(ABounds: TRect): TBGRABitmap;
begin
  Result:=inherited FilterGrayscale(ABounds) as TBGRABitmap;
end;

function TBGRABitmap.FilterNormalize(eachChannel: boolean): TBGRABitmap;
begin
  Result:=inherited FilterNormalize(eachChannel) as TBGRABitmap;
end;

function TBGRABitmap.FilterNormalize(ABounds: TRect; eachChannel: boolean
  ): TBGRABitmap;
begin
  Result:=inherited FilterNormalize(ABounds, eachChannel) as TBGRABitmap;
end;

function TBGRABitmap.FilterRotate(origin: TPointF; angle: single;
  correctBlur: boolean): TBGRABitmap;
begin
  Result:=inherited FilterRotate(origin, angle, correctBlur) as TBGRABitmap;
end;

function TBGRABitmap.FilterAffine(AMatrix: TAffineMatrix; correctBlur: boolean
  ): TBGRABitmap;
begin
  Result:=inherited FilterAffine(AMatrix, correctBlur) as TBGRABitmap;
end;

function TBGRABitmap.FilterSphere: TBGRABitmap;
begin
  Result:=inherited FilterSphere as TBGRABitmap;
end;

function TBGRABitmap.FilterTwirl(ACenter: TPoint; ARadius: Single;
  ATurn: Single; AExponent: Single): TBGRABitmap;
begin
  Result:=inherited FilterTwirl(ACenter, ARadius, ATurn, AExponent) as TBGRABitmap;
end;

function TBGRABitmap.FilterTwirl(ABounds: TRect; ACenter: TPoint;
  ARadius: Single; ATurn: Single; AExponent: Single): TBGRABitmap;
begin
  Result:=inherited FilterTwirl(ABounds, ACenter, ARadius, ATurn, AExponent) as TBGRABitmap;
end;

function TBGRABitmap.FilterCylinder: TBGRABitmap;
begin
  Result:=inherited FilterCylinder as TBGRABitmap;
end;

function TBGRABitmap.FilterPlane: TBGRABitmap;
begin
  Result:=inherited FilterPlane as TBGRABitmap;
end;

initialization

  //this variable is assigned to access appropriate functions
  //depending on the platform
  BGRABitmapFactory := TBGRABitmap;

finalization

  tempBmp.Free;

end.

