{%mainunit orca_scene2d.pas}

{*************************************************************************
                Copyright (c) PilotLogic Software House

  Package pl_ORCA
  This file is part of CodeTyphon Studio (https://www.pilotlogic.com/)

 ***** BEGIN LICENSE BLOCK *****
 * Version: LGPL-2.1 with link exception (Modified LGPL)
 *
 * The contents of this file are subject to the 
 * GNU LIBRARY GENERAL PUBLIC LICENSE Version 2.1 (the "License")
 * with the following modification:
 * 
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * ***** END LICENSE BLOCK *****
 
***************************************************************************}


type

//========== TD2FilterGtk ====================

  TD2FilterGtk=class(TD2Filter)
  published
    class function GetFileTypes:string;  override;
    class function GetImageSize(const AFileName:string):TD2Point;  override;
    function LoadFromFile(const AFileName:string; const Rotate:single; var Bitmap:TD2Bitmap):boolean;  override;
    function LoadThumbnailFromFile(const AFileName:string; const AFitWidth,AFitHeight:single; const UseEmbedded:boolean;var Bitmap:TD2Bitmap):boolean;  override;
    function SaveToFile(const AFileName:string; var Bitmap:TD2Bitmap; const Params:string=''):boolean;  override;
    function LoadFromStream(const AStream:TStream; var Bitmap:TD2Bitmap):boolean;  override;
    function SaveToStream(const AStream:TStream; var Bitmap:TD2Bitmap; const Format:string;
                          const Params:string=''):boolean;  override;
  end;

//============== TD2CanvasCairo ==================

  TD2CanvasCairo=class(TD2Canvas)
  private
    fpCairoPattern:Pcairo_pattern_t;
    fpCairoSurface:Pcairo_surface_t;
    fpCairoSurfaceT:Pcairo_surface_t;
    fpCairoT:Pcairo_t;
    fpPangoLayout:PPangoLayout;
  protected
    procedure ApplyStroke(ARect:TD2Rect; const AOpacity:single);
    procedure FontChanged(Sender:TObject);  override;
    procedure UpdateBitmap(ABitmap:TD2Bitmap);
    procedure DoDestroyBitmap(Sender:TObject);
    procedure ApplyFill(ARect:TD2Rect; const AOpacity:single);
    procedure DeApplyFill;
  public
    constructor Create(const AWidth,AHeight:integer);  override;
    constructor CreateFromBitmap(const ABitmap:TD2Bitmap);  override;
    class function GetBitmapScanline(Bitmap:TD2Bitmap; y:integer):PD2ColorArray;  override;
    destructor Destroy;  override;
    procedure FreeBuffer;  override;
    procedure ResizeBuffer(const AWidth,AHeight:integer);  override;
    procedure FlushBuffer(const X,Y:integer; const DC:Cardinal);  override;
    procedure FlushBufferRect(const X,Y:integer; const DC:Cardinal; const ARect:TD2Rect);  override;
    procedure Clear(const Color:cardinal);  override;
    procedure ClearRect(const ARect:TD2Rect; const AColor:TD2Color=0);  override;
    procedure SetMatrix(const M:TD2Matrix);  override;
    procedure MultyMatrix(const M:TD2Matrix);  override;
    function  SaveCanvas:cardinal;  override;
    procedure RestoreCanvas(const AState:cardinal);  override;
    procedure SetClipRects(const ARects:array of TD2Rect);  override;
    procedure IntersectClipRect(const ARect:TD2Rect);  override;
    procedure ExcludeClipRect(const ARect:TD2Rect);  override;
    procedure ResetClipRect;  override;
    procedure DrawLine(const APt1,APt2:TD2Point; const AOpacity:single);  override;
    procedure DrawEllipse(const ARect:TD2Rect; const AOpacity:single);  override;
    procedure DrawRect(const ARect:TD2Rect; const xRadius,yRadius:single; const ACorners:TD2Corners; const AOpacity:single;
                        const ACornerType:TD2CornerType=d2CornerRound);  override;
    procedure FillRect(const ARect:TD2Rect; const xRadius,yRadius:single; const ACorners:TD2Corners; const AOpacity:single;
                        const ACornerType:TD2CornerType=d2CornerRound);  override;
    procedure FillEllipse(const ARect:TD2Rect; const AOpacity:single);  override;
    procedure FillText(const ARect,AClipRect:TD2Rect; const AText:WideString; const WordWrap:boolean;
                        const AOpacity:single; const ATextAlign:TD2TextAlign;
                        const AVTextAlign:TD2TextAlign=d2TextAlignCenter);  override;
    procedure FillPath(const APath:TD2PathData; const ARect:TD2Rect; const AOpacity:single);  override;
    function  PtInPath(const APoint:TD2Point; const ARect:TD2Rect; const APath:TD2PathData):boolean;  override;
    procedure DrawPath(const APath:TD2PathData; const ARect:TD2Rect; const AOpacity:single);  override;
    procedure DrawBitmap(const ABitmap:TD2Bitmap; const SrcRect,DstRect:TD2Rect; const AOpacity:single; const HighSpeed:boolean=false);  override;
    procedure DrawThumbnail(const ABitmap:TD2Bitmap; const aWidth,aHeight:single);  override;
    procedure MeasureText(var ARect:TD2Rect; AClipRect:TD2Rect; const AText:WideString;
                           const WordWrap:boolean; const ATextAlign:TD2TextAlign;
                           const AVTextAlign:TD2TextAlign=d2TextAlignCenter);  override;
    function  TextToPath(Path:TD2PathData; const ARect:TD2Rect; const AText:WideString;
                          const WordWrap:boolean; const ATextAlign:TD2TextAlign;
                          const AVTextAlign:TD2TextAlign=d2TextAlignCenter):boolean;  override;
  end;

//==================================================================
//==================================================================


var
  VarColorArray:array [0..100] of cardinal;
  VarOffsetArray:array [0..100] of single;

procedure fnSwapRB(const Buf:Pointer; const Size:integer);
var
  i:integer;
  b:byte;
begin
  for i:=0 to Size - 1 do
  begin
    b:=PD2ColorRecArray(Buf)[i].R;
    PD2ColorRecArray(Buf)[i].R:=PD2ColorRecArray(Buf)[i].B;
    PD2ColorRecArray(Buf)[i].B:=b;
  end;
end;

//================= TD2FilterGtk ==================================================

class function TD2FilterGtk.GetImageSize(const AFileName:string):TD2Point;
var
  W,H:gint;
begin
  Result:=d2Point(0,0);
  if gdk_pixbuf_get_file_info(PChar(AFileName),W,H) <> 0 then
    Result:=d2Point(W,H);
end;

class function TD2FilterGtk.GetFileTypes:string;
begin
  Result:='*.jpg;*.jpeg;*.gif;*.tif;*.tiff;*.png;*.bmp;'+
          '*.JPG;*.JPEG;*.GIF;*.TIF;*.TIFF;*.PNG;*.BMP';
end;

function TD2FilterGtk.LoadFromFile(const AFileName:string; const Rotate:single; var Bitmap:TD2Bitmap):boolean;
var
  res,pb:PGdkPixbuf;
begin
  pb:=gdk_pixbuf_new_from_file(PChar(AFileName),nil);
  if pb <> nil then
  begin
    Bitmap.SetSize(gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb));
    res:=gdk_pixbuf_new_from_data(PByte(Bitmap.StartLine),GDK_COLORSPACE_RGB,true,
      8,gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb),gdk_pixbuf_get_width(pb) * 4,nil,nil);
    if res <> nil then
    begin
      gdk_pixbuf_copy_area(pb,0,0,gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb),
        res,0,0);
      g_object_unref(res);
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
      Result:=true;
    end;
    g_object_unref(pb);
  end
  else
    Result:=false;
end;

function TD2FilterGtk.LoadThumbnailFromFile(const AFileName:string;
                                            const AFitWidth,AFitHeight:single;
                                            const UseEmbedded:boolean;
                                            var Bitmap:TD2Bitmap):boolean;
var
  xPbuf1,xPbuf2:PGdkPixbuf;
begin
  xPbuf2:=gdk_pixbuf_new_from_file_at_scale(PChar(AFileName),round(AFitWidth),round(AFitHeight),true,nil);

  if xPbuf2 <> nil then
  begin
     Bitmap.SetSize(gdk_pixbuf_get_width(xPbuf2),gdk_pixbuf_get_Height(xPbuf2));
     xPbuf1:=gdk_pixbuf_new_from_data(PByte(Bitmap.StartLine),GDK_COLORSPACE_RGB,true, 8,
                                  gdk_pixbuf_get_width(xPbuf2),gdk_pixbuf_get_Height(xPbuf2),gdk_pixbuf_get_width(xPbuf2) * 4,nil,nil);

    if xPbuf1 <> nil then
    begin
      gdk_pixbuf_copy_area(xPbuf2,0,0,gdk_pixbuf_get_width(xPbuf2),gdk_pixbuf_get_Height(xPbuf2), xPbuf1,0,0);
      g_object_unref(xPbuf1);
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
      Result:=true;
    end;

    g_object_unref(xPbuf2);

  end else
  begin
    Result:=false;
  end;
end;

function TD2FilterGtk.LoadFromStream(const AStream:TStream; var Bitmap:TD2Bitmap):boolean;
var
  res,pb:PGdkPixbuf;
  xbmp:TLazIntfImage;
  picjpg:TJPEGImage;
  picgif:TGIFImage ;
  picTiff:TTiffImage;
  picpng:TPortableNetworkGraphic;

//----------------------------------------------------------------
 Procedure _makebitmap(const abits:TPixelFormat);
  begin
   if xbmp=nil then exit;

    pb:=gdk_pixbuf_new_from_data(PByte(xbmp.GetDataLineStart(0)),
                                   GDK_COLORSPACE_RGB,true,
                                   8,xbmp.Width,xbmp.Height,
                                   xbmp.Width * 4,
                                   nil,nil);
    if pb <> nil then
    begin

      Bitmap.SetSize(gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb));

      res:=gdk_pixbuf_new_from_data(PByte(Bitmap.StartLine),
                                      GDK_COLORSPACE_RGB,true,
                                      8,gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb),
                                      gdk_pixbuf_get_width(pb) * 4,
                                      nil,nil);
      if res <> nil then
      begin
        gdk_pixbuf_copy_area(pb,0,0,gdk_pixbuf_get_width(pb),gdk_pixbuf_get_Height(pb),
          res,0,0);
        g_object_unref(res);

        fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);    //=== ct8888 =============
        Result:=true;
      end;
      g_object_unref(pb);
    end;

  end;
//----------------------------------------------------------------
begin
  Result:=false;

  if AStream=nil then exit;
  if AStream.Size=0 then Exit;

  xbmp:=nil;

try
//---------------------PNG--------------------------------
  picpng:=TPortableNetworkGraphic.Create;
  if picpng.IsStreamFormatSupported(AStream) then
    begin
      picpng.LoadFromStream(AStream);
      xbmp:=picpng.CreateIntfImage;
      _makebitmap(picpng.PixelFormat);
    end;
  picpng.Free;
  if xbmp<>nil then begin xbmp.free; exit; end;
  //---------------------GIF--------------------------------
  picgif:=TGIFImage.Create;
  if picgif.IsStreamFormatSupported(AStream) then
    begin
      picgif.LoadFromStream(AStream);
      xbmp:=picgif.CreateIntfImage;
      _makebitmap(picgif.PixelFormat);
    end;
  picgif.Free;
  if xbmp<>nil then begin xbmp.free; exit; end;
  //---------------------JPG--------------------------------
  picjpg:=TJPEGImage.Create;
  if picjpg.IsStreamFormatSupported(AStream) then
    begin
      picjpg.LoadFromStream(AStream);
      xbmp:=picjpg.CreateIntfImage;
      _makebitmap(picjpg.PixelFormat);
    end;
  picjpg.Free;
 //---------------------Tiff--------------------------------
  picTiff:=TTiffImage.Create;
  if picTiff.IsStreamFormatSupported(AStream) then
    begin
      picTiff.LoadFromStream(AStream);
      xbmp:=picTiff.CreateIntfImage;
      _makebitmap(picTiff.PixelFormat);
    end;
  picTiff.Free;
  if xbmp<>nil then begin xbmp.free; exit; end;

 //===================================================================
 finally
 end;
end;

function TD2FilterGtk.SaveToStream(const AStream:TStream;
                                   var Bitmap:TD2Bitmap;
                                   const Format:string;
                                   const Params:string=''):boolean;
var
  i:integer;
  k,o:array [0..10] of PChar;
  xparam,xname,xvalue,S,xsss:string;
  xPixbuf:PGdkPixbuf;
  xByte:PByte;
  xsize:gsize;
begin
  Result:=false;
  xsss:='png';
  if (LowerCase(Format)='jpg') or (LowerCase(Format)='jpeg') then xsss:='jpeg';
  if (LowerCase(Format)='bmp') then xsss:='bmp';
  if (LowerCase(Format)='png') then xsss:='png';
  if (LowerCase(Format)='tif') or (LowerCase(Format)='tiff') then xsss:='tif';
  if (LowerCase(Format)='gif') then xsss:='gif';

  xPixbuf:=gdk_pixbuf_new_from_data(PByte(Bitmap.StartLine),GDK_COLORSPACE_RGB,true,8,Bitmap.Width,Bitmap.Height,Bitmap.Width * 4,nil,nil);

  if xPixbuf <> nil then
  begin
    i:=0;
    if Params <> '' then
    begin
      S:=Params;

      while S <> '' do
      begin
        xparam:=d2GetToken(S,' ');
        xname:=d2GetToken(xparam,'=');
        xvalue:=d2GetToken(xparam,'');

        if CompareText(xname,'quality')=0 then
        begin
          o[i]:=PChar('quality');
          k[i]:=PChar(xvalue);
          inc(i);
        end;

      end;

      o[i]:=nil;
      k[i]:=nil;

      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);

      if gdk_pixbuf_save_to_bufferv(xPixbuf,xByte,xsize,PChar(xsss),@o[0],@k[0],nil) then
      begin
        AStream.Write(xByte^,xsize);
        g_free(xByte);
        Result:=true;
      end;

      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);

    end else
    begin

      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);

      if gdk_pixbuf_save_to_bufferv(xPixbuf,xByte,xsize,PChar(xsss),nil,nil,nil) then
      begin
        AStream.Write(xByte^,xsize);
        g_free(xByte);
        Result:=true;
      end;

      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
    end;

    g_object_unref(xPixbuf);
  end;
end;

function TD2FilterGtk.SaveToFile(const AFileName:string; var Bitmap:TD2Bitmap; const Params:string=''):boolean;
var
  pb:PGdkPixbuf;
  param,name,value,S,encoderType:string;
  i:integer;
  k,o:array [0..10] of PChar;
begin
  Result:=false;
  encoderType:='png';

  if (LowerCase(ExtractFileExt(AFileName))='.jpg') or (LowerCase(ExtractFileExt(AFileName))='.jpeg') then encoderType:='jpeg';
  if (LowerCase(ExtractFileExt(AFileName))='.bmp') then encoderType:='bmp';
  if (LowerCase(ExtractFileExt(AFileName))='.png') then encoderType:='png';
  if (LowerCase(ExtractFileExt(AFileName))='.tif') or (LowerCase(ExtractFileExt(AFileName))='.tiff') then encoderType:='tiff';
  if (LowerCase(ExtractFileExt(AFileName))='.gif') then encoderType:='gif';

  if encoderType='' then Exit;

  pb:=gdk_pixbuf_new_from_data(PByte(Bitmap.StartLine),GDK_COLORSPACE_RGB,true,8,Bitmap.Width,Bitmap.Height,Bitmap.Width * 4,nil,nil);

  if pb <> nil then
  begin
    i:=0;
    if Params <> '' then
    begin
      S:=Params;

      while S <> '' do
      begin
        param:=d2GetToken(S,' ');
        name:=d2GetToken(param,'=');
        value:=d2GetToken(param,'');

        if CompareText(name,'quality')=0 then
        begin
          o[i]:=PChar('quality');
          k[i]:=PChar(value);
          inc(i);
        end;

      end;

      o[i]:=nil;
      k[i]:=nil;
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
      Result:=gdk_pixbuf_savev(pb,PChar(AFileName),PChar(encoderType),@o[0],@k[0],nil);
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
    end else
    begin
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
      Result:=gdk_pixbuf_save(pb,PChar(AFileName),PChar(encoderType),nil,[]);
      fnSwapRB(Bitmap.Startline,Bitmap.Width * Bitmap.Height);
    end;

    gdk_pixbuf_unref(pb);
  end;
end;

//================== TD2CanvasCairo =====================================

class function TD2CanvasCairo.GetBitmapScanline(Bitmap:TD2Bitmap; y:integer):PD2ColorArray;
begin
  if (y >= 0) and (y < Bitmap.Height) and (Bitmap.StartLine <> nil) then
    Result:=@PD2ColorArray(Bitmap.StartLine)[(y) * Bitmap.Width] else
    Result:=nil;
end;

constructor TD2CanvasCairo.Create(const AWidth,AHeight:integer);
begin
  inherited ;
  fpCairoSurfaceT:=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,1,1);
  fpCairoT:=cairo_create(fpCairoSurfaceT);
  fpPangoLayout:=pango_cairo_create_layout(fpCairoT);
end;

constructor TD2CanvasCairo.CreateFromBitmap(const ABitmap:TD2Bitmap);
begin
  inherited;
  fpCairoSurfaceT:=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,1,1);
  fpCairoT:=cairo_create(fpCairoSurfaceT);
  fpPangoLayout:=pango_cairo_create_layout(fpCairoT);

  FBitmap:=ABitmap;
  UpdateBitmap(FBitmap);
  if FBitmap.Handle <> 0 then
    FHandle:=PtrInt(cairo_create(Pcairo_surface_t(FBitmap.Handle)))
end;

destructor TD2CanvasCairo.Destroy;
begin
  g_object_unref(fpPangoLayout);
  cairo_destroy(fpCairoT);
  cairo_surface_destroy(fpCairoSurfaceT);
  inherited;
end;

procedure TD2CanvasCairo.ResizeBuffer(const AWidth,AHeight:integer);
begin
  if (AWidth=FWidth) and (AHeight=FHeight) then Exit;

  FreeBuffer;
  FWidth:=AWidth;
  FHeight:=AHeight;

  if FWidth <= 0 then FWidth:=1;
  if FHeight <= 0 then FHeight:=1;
  FResized:=true;

  if FWidth * FHeight=0 then Exit;

  if FBuffered then
  begin
    GetMem(FBufferBits,FWidth * FHeight * 4);
    fpCairoSurface:=cairo_image_surface_create_for_data(FBufferBits,CAIRO_FORMAT_ARGB32,FWidth,FHeight,FWidth * 4);
    Handle:=PtrInt(cairo_create(fpCairoSurface));
  end else
  begin
    GetMem(FBufferBits,4);
  end;

end;

procedure TD2CanvasCairo.FreeBuffer;
begin
  if (FBitmap <> nil) and (Handle <> 0) then
  begin
    cairo_destroy(Pcairo_t(Handle));
  end;

  if FBufferBits <> nil then System.FreeMem(FBufferBits);

  if FBuffered then
  begin
    cairo_destroy(Pcairo_t(Handle));
    cairo_surface_destroy(fpCairoSurface);
  end;

  Handle:=0;
end;

procedure TD2CanvasCairo.FlushBufferRect(const X,Y:integer; const DC:Cardinal; const ARect:TD2Rect);
begin
//...
end;

procedure TD2CanvasCairo.FlushBuffer(const X,Y:integer; const DC:Cardinal);
begin
//...
end;

procedure TD2CanvasCairo.FontChanged(Sender:TObject);
begin
//...
end;

procedure TD2CanvasCairo.ClearRect(const ARect:TD2Rect; const AColor:TD2Color);
var
  R:TRect;
begin
  if FBufferBits=nil then Exit;

  R:=Rect(Trunc(ARect.Left),Trunc(ARect.Top),Round(ARect.Right),Round(ARect.Bottom));
  if R.Left < 0 then R.Left:=0;
  if R.Top < 0 then R.Top:=0;
  if R.Top < 0 then R.Top:=0;
  if R.Right > FWidth then R.Right:=FWidth;
  if R.Bottom > FHeight then R.Bottom:=FHeight;
  if R.Bottom < R.Top then R.Bottom:=R.Top;
  if R.Right < R.Left then R.Right:=R.Left;

  if (R.Right < 0) or (R.Top < 0) or (R.Left > FWidth) or (R.Top > FHeight) then Exit;

  if not FBuffered then
  begin
     with TD2ColorRec(AColor) do
       cairo_set_source_rgba(Pcairo_t(Handle),R / $FF,G / $FF,B / $FF,A / $FF);

     cairo_rectangle(Pcairo_t(Handle),R.Left,R.Top,R.right - R.left,R.bottom - R.top);
     cairo_fill(Pcairo_t(Handle));
  end else
  begin
    d2FillLongwordRect(FBufferBits,FWidth,FHeight,R.Left,R.Top,R.Right,R.Bottom,AColor);
  end;
end;

procedure TD2CanvasCairo.MultyMatrix(const M:TD2Matrix);
var
  xcm:cairo_matrix_t;
begin

  if Handle=0 then Exit;
  inherited;

  with M do
  begin
    xcm.xx:=m11;
    xcm.yx:=m12;
    xcm.xy:=m21;
    xcm.yy:=m22;
    xcm.x0:=m31;
    xcm.y0:=m32;
  end;
  cairo_transform(Pcairo_t(Handle),@xcm);
end;

procedure TD2CanvasCairo.SetMatrix(const M:TD2Matrix);
var
  xcm:cairo_matrix_t;
begin

  if Handle=0 then Exit;

  FMatrix:=M;
  with FMatrix do
  begin
    xcm.xx:=m11;
    xcm.yx:=m12;
    xcm.xy:=m21;
    xcm.yy:=m22;
    xcm.x0:=m31;
    xcm.y0:=m32;
  end;

  cairo_set_matrix(Pcairo_t(Handle),@xcm);
end;


procedure TD2CanvasCairo.Clear(const Color:cardinal);
begin
  if FBufferBits=nil then Exit;

  if not FBuffered then
  begin
    with TD2ColorRec(Color) do
        cairo_set_source_rgba(Pcairo_t(Handle),R / $FF,G / $FF,B / $FF,A / $FF);
     cairo_paint(Pcairo_t(Handle));
  end else
  begin
    d2FillLongword(FBufferBits,FWidth * FHeight,Color);
  end;
end;

function TD2CanvasCairo.SaveCanvas:cardinal;
var
  i:integer;
begin
  Result:=InvalideCanvasState;

  if Handle=0 then Exit;

  cairo_save(Pcairo_t(Handle));

  if Length(FSaveData) > 0 then
    for i:=0 to High(FSaveData) do
      if FSaveData[i].Index=InvalideCanvasState then
      begin
        Result:=i;
        Break;
      end;

  if Result=InvalideCanvasState then
  begin
    SetLength(FSaveData,Length(FSaveData) + 1);
    Result:=High(FSaveData);
  end;

  FSaveData[Result].Index:=Result;
  FSaveData[Result].Matrix:=FMatrix;
  FSaveData[Result].Stroke:=TD2Brush.Create(d2BrushSolid,InvalideCanvasState);
  FSaveData[Result].Stroke.Assign(Stroke);
  FSaveData[Result].Fill:=TD2Brush.Create(d2BrushSolid,$FF000000);
  FSaveData[Result].Fill.Assign(Fill);
  FSaveData[Result].StrokeThickness:=FStrokeThickness;
  FSaveData[Result].StrokeCap:=StrokeCap;
  FSaveData[Result].StrokeJoin:=StrokeJoin;
  FSaveData[Result].StrokeDash:=StrokeDash;

  if StrokeDash <> d2DashSolid then
  begin
    SetLength(FSaveData[Result].Dash,Length(FDash));
    Move(FDash[0],FSaveData[Result].Dash[0],SizeOf(FDash[0]) * Length(FDash));
  end;

  FSaveData[Result].DashOffset:=FDashOffset;
  FSaveData[Result].Font:=TD2Font.Create;
  FSaveData[Result].Font.Assign(Font);
end;

procedure TD2CanvasCairo.RestoreCanvas(const AState:cardinal);
begin
  if Handle=0 then Exit;

  if (AState >= 0) and (AState < Length(FSaveData)) then
  begin
    cairo_restore(Pcairo_t(Handle));
    FSaveData[AState].Index:=InvalideCanvasState;

    FMatrix:=FSaveData[AState].Matrix;
    Stroke.Assign(FSaveData[AState].Stroke);
    FSaveData[AState].Stroke.Free;
    Fill.Assign(FSaveData[AState].Fill);
    FSaveData[AState].Fill.Free;
    SetMatrix(FMatrix);
    FStrokeThickness:=FSaveData[AState].StrokeThickness;
    FStrokeCap:=FSaveData[AState].StrokeCap;
    FStrokeJoin:=FSaveData[AState].StrokeJoin;
    FStrokeDash:=FSaveData[AState].StrokeDash;

    if StrokeDash <> d2DashSolid then
    begin
      SetLength(FDash,Length(FSaveData[AState].Dash));
      Move(FSaveData[AState].Dash[0],FDash[0],SizeOf(FDash[0]) * Length(FDash));
    end;

    FDashOffset:=FSaveData[AState].DashOffset;
    Font.Assign(FSaveData[AState].Font);
    FSaveData[AState].Font.Free;
  end
end;

procedure TD2CanvasCairo.SetClipRects(const ARects:array of TD2Rect);
var
  i:integer;
begin
  if Handle=0 then Exit;

  cairo_reset_clip(Pcairo_t(Handle));
  for i:=0 to High(ARects) do
  begin
    cairo_rectangle(Pcairo_t(Handle),ARects[i].Left,ARects[i].Top,d2RectWidth(ARects[i]),d2RectHeight(ARects[i]));
  end;
  cairo_clip(Pcairo_t(Handle));
end;

procedure TD2CanvasCairo.IntersectClipRect(const ARect:TD2Rect);
begin
  if Handle=0 then Exit;

  cairo_rectangle(Pcairo_t(Handle),ARect.Left,ARect.Top,d2RectWidth(ARect),d2RectHeight(ARect));
  cairo_clip(Pcairo_t(Handle));
end;

procedure TD2CanvasCairo.ExcludeClipRect(const ARect:TD2Rect);
var
  i:integer;
  xRR:array [0..3] of TD2Rect;
  xR:TD2Rect;
begin
  if Handle=0 then Exit;

  xR:=ARect;
  xRR[0]:=d2Rect(0,0,xR.Left,FHeight);
  xRR[1]:=d2Rect(xR.Right,0,FWidth,FHeight);
  xRR[2]:=d2Rect(xR.Left,0,xR.Right,xR.Top);
  xRR[3]:=d2Rect(xR.Left,xR.Bottom,xR.Right,FHeight);

  for i:=0 to High(xRR) do
  begin
    cairo_rectangle(Pcairo_t(Handle),xRR[i].Left,xRR[i].Top,d2RectWidth(xRR[i]),d2RectHeight(xRR[i]));
  end;

  cairo_clip(Pcairo_t(Handle));
end;

procedure TD2CanvasCairo.ResetClipRect;
begin
  if Handle=0 then Exit;

  cairo_reset_clip(Pcairo_t(Handle));
end;

procedure TD2CanvasCairo.ApplyFill(ARect:TD2Rect; const AOpacity:single);
var
  i:integer;
  xnn:PtrInt;
  xMat:cairo_matrix_t;
begin
  if Handle=0 then Exit;

  if (FFill.Style=d2BrushResource) and (FFill.Resource <> nil) and (FFill.Resource.Brush <> nil) then
            FFill.Assign(FFill.Resource.Brush);

  with FFill do
  begin
    case Style of
      d2BrushBitmap:
        begin
          if (Bitmap.Bitmap <> nil) and (Bitmap.Bitmap.Width > 0) and (Bitmap.Bitmap.Height > 0) then
          begin
            UpdateBitmap(Bitmap.Bitmap);

            if (Bitmap.Bitmap.Handle <> 0) then
            begin
              fpCairoPattern:=cairo_pattern_create_for_surface(Pcairo_surface_t(Bitmap.Bitmap.Handle));

              if Bitmap.WrapMode=d2WrapTileStretch then
              begin
                cairo_matrix_init_scale(@xMat,Bitmap.Bitmap.Width / (d2RectWidth(ARect) + (StrokeThickness / 2)),
                  Bitmap.Bitmap.Height / (d2RectHeight(ARect) + (StrokeThickness / 2)));
                cairo_pattern_set_matrix(fpCairoPattern,@xMat);
              end;

              if Bitmap.WrapMode=d2WrapTile then
                cairo_pattern_set_extend (fpCairoPattern,CAIRO_EXTEND_REPEAT);
              cairo_set_source(Pcairo_t(Handle),fpCairoPattern);

            end else
            begin
              cairo_set_source_rgba(Pcairo_t(Handle),0,0,0,0);
            end;

          end else
          begin
            cairo_set_source_rgba(Pcairo_t(Handle),0,0,0,0);
          end;
        end;
      d2BrushGradient:
        begin

          if Gradient.Points.count > 1 then
          begin
            xnn:=0;

            if Gradient.Points[0].Offset > 0 then
            begin
              VarColorArray[xnn]:=d2Opacity(Gradient.Points[0].IntColor,AOpacity);
              VarOffsetArray[xnn]:=0;
              xnn:=xnn + 1;
            end;

            for i:=0 to Gradient.Points.count - 1 do
            begin
              VarColorArray[i + xnn]:=d2Opacity(Gradient.Points[i].IntColor,AOpacity);
              VarOffsetArray[i + xnn]:=Gradient.Points[i].Offset;
            end;

            if Gradient.Points[Gradient.Points.count - 1].Offset < 1 then
            begin
              xnn:=xnn + 1;
              VarColorArray[Gradient.Points.count + xnn - 1]:=d2Opacity(Gradient.Points[Gradient.Points.count - 1].IntColor,AOpacity);
              VarOffsetArray[Gradient.Points.count + xnn - 1]:=1;
            end;

            if Gradient.Style=d2LinearGradient then
            begin

              fpCairoPattern:=cairo_pattern_create_linear(ARect.Left + Gradient.StartPosition.X * ARect.Right,ARect.Top + Gradient.StartPosition.Y * ARect.Bottom,
                                                ARect.Left + Gradient.StopPosition.X * ARect.Right,ARect.Top + Gradient.StopPosition.Y * ARect.Bottom);

              for i:=0 to Gradient.Points.count + xnn - 1 do
                with TD2ColorRec(VarColorArray[i]) do
                  cairo_pattern_add_color_stop_rgba(fpCairoPattern,VarOffsetArray[i],R / $FF,G / $FF,B / $FF,A / $FF);

              cairo_set_source(Pcairo_t(Handle),fpCairoPattern);
            end else
            begin

              fpCairoPattern:=cairo_pattern_create_radial((ARect.Left + ARect.Right) / 2,(ARect.Top + ARect.Bottom) / 2,0,
                (ARect.Left + ARect.Right) / 2,(ARect.Top + ARect.Bottom) / 2,d2MaxFloat(d2RectWidth(ARect),d2RectWidth(ARect)));
              for i:=0 to Gradient.Points.count + xnn - 1 do
                with TD2ColorRec(VarColorArray[i]) do
                  cairo_pattern_add_color_stop_rgba(fpCairoPattern,VarOffsetArray[i],R / $FF,G / $FF,B / $FF,A / $FF);

              cairo_set_source(Pcairo_t(Handle),fpCairoPattern);
            end;
          end else
          begin
            cairo_set_source_rgba(Pcairo_t(Handle),0,0,0,0);
          end;

        end;
      d2BrushSolid:
        begin
          with TD2ColorRec(d2Opacity(SolidColor,AOpacity)) do
            cairo_set_source_rgba(Pcairo_t(Handle),R / $FF,G / $FF,B / $FF,A / $FF);
        end;
    else
      cairo_set_source_rgba(Pcairo_t(Handle),0,0,0,0);
    end;
  end;
end;

procedure TD2CanvasCairo.DeApplyFill;
begin
  if Handle=0 then Exit;

  with FFill do
  begin
    case Style of
      d2BrushBitmap:
        begin
          cairo_pattern_destroy(fpCairoPattern);
        end;
      d2BrushGradient:
        begin
          cairo_pattern_destroy(fpCairoPattern);
        end;
      d2BrushVisual:
        begin
        end;
      d2BrushResource:
        begin
        end;
      d2BrushSolid:
        begin
        end;
    end;
  end;
end;

procedure TD2CanvasCairo.DrawLine(const APt1,APt2:TD2Point; const AOpacity:single);
begin
  if Handle=0 then Exit;

  if FStroke.Style <> d2BrushNone then
  begin
    ApplyStroke(d2Rect(APt1.X,APt1.Y,APt2.X,APt2.Y),AOpacity);
    cairo_move_to(Pcairo_t(Handle),APt1.X,APt1.Y);
    cairo_line_to(Pcairo_t(Handle),APt2.X,APt2.Y);
    cairo_stroke(Pcairo_t(Handle));
  end;
end;

procedure TD2CanvasCairo.ApplyStroke(ARect:TD2Rect; const AOpacity:single);
var
  i:integer;
  xdoub:array of double;
begin
  if Handle=0 then Exit;

  if (FStroke.Style=d2BrushResource) and (FStroke.Resource <> nil) and (FStroke.Resource.Brush <> nil) then
    FStroke.Assign(FStroke.Resource.Brush);

  with FStroke do
  begin
    case Style of
      d2BrushSolid:
        begin
          with TD2ColorRec(d2Opacity(SolidColor,AOpacity)) do
            cairo_set_source_rgba(Pcairo_t(Handle),R / $FF,G / $FF,B / $FF,A / $FF);
        end;
    else
      cairo_set_source_rgba(Pcairo_t(Handle),0,0,0,0);
    end;
  end;

  case StrokeCap of
    d2CapFlat:cairo_set_line_cap(Pcairo_t(Handle),CAIRO_LINE_CAP_BUTT);
    d2CapRound:cairo_set_line_cap(Pcairo_t(Handle),CAIRO_LINE_CAP_ROUND);
  end;

  if Length(FDash) > 0 then
  begin
    SetLength(xdoub,Length(FDash));
    for i:=0 to High(FDash) do
    begin
      xdoub[i]:=FDash[i] * StrokeThickness;
      if (StrokeCap=d2CapRound) then
      begin

        if odd(i) then
          xdoub[i]:=(FDash[i] + 1) * StrokeThickness else
          xdoub[i]:=(FDash[i] - 1) * StrokeThickness;

      end;
    end;
    cairo_set_dash(Pcairo_t(Handle),@xdoub[0],Length(FDash),FDashOffset);
  end else
    cairo_set_dash(Pcairo_t(Handle),nil,0,0);

  case StrokeJoin of
    d2JoinRound:cairo_set_line_join(Pcairo_t(Handle),CAIRO_LINE_JOIN_ROUND);
    d2JoinBevel:cairo_set_line_join(Pcairo_t(Handle),CAIRO_LINE_JOIN_BEVEL);
    d2JoinMiter:cairo_set_line_join(Pcairo_t(Handle),CAIRO_LINE_JOIN_MITER);
  end;

  cairo_set_line_width(Pcairo_t(Handle),StrokeThickness);
end;

procedure TD2CanvasCairo.DrawRect(const ARect:TD2Rect;
                                  const xRadius,yRadius:single;
                                  const ACorners:TD2Corners;
                                  const AOpacity:single;
                                  const ACornerType:TD2CornerType=d2CornerRound);
var
  xx1,xx2,yy1,yy2:single;
  xRR:TD2Rect;
begin
  if Handle=0 then Exit;

  if FStroke.Style <> d2BrushNone then
  begin
    xRR:=ARect;
    ApplyStroke(ARect,AOpacity);

    if (xRadius < Epsilon) and (yRadius < Epsilon) then
    begin
      cairo_rectangle(Pcairo_t(Handle),xRR.Left,xRR.Top,d2RectWidth(xRR),d2RectHeight(xRR));
      cairo_stroke(Pcairo_t(Handle));
    end else
    begin
      xx1:=xRadius;
      if d2RectWidth(xRR) - (xx1 * 2) < 0 then xx1:=(xRadius * (d2RectWidth(xRR) / (xx1 * 2)));
      xx2:=xx1 * CurveKappaInv;
      yy1:=yRadius;
      if d2RectHeight(xRR) - (yy1 * 2) < 0 then yy1:=(yRadius * (d2RectHeight(xRR) / (yy1 * 2)));
      yy2:=yy1 * CurveKappaInv;
      cairo_new_path(Pcairo_t(Handle));
      cairo_move_to(Pcairo_t(Handle),xRR.Left,xRR.Top + yy1);

      if d2CornerTopLeft in ACorners then
      begin

        case ACornerType of
          d2CornerInnerLine:
                        begin
                          cairo_line_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Top + yy1);
                          cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top + yy2);
                          cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
                        end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Top + yy1,xRR.Left + xx1,xRR.Top + yy2,xRR.Left + xx1,xRR.Top);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Left,xRR.Top + (yy2),xRR.Left + xx2,xRR.Top,xRR.Left + xx1,xRR.Top)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Top);
        cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top);
      if d2CornerTopRight in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                             begin
                               cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top + yy2);
                               cairo_line_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Top + yy1);
                               cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
                             end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top + yy2,xRR.Right - xx2,xRR.Top + yy1,xRR.Right,xRR.Top + yy1);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Top,xRR.Right,xRR.Top + (yy2),xRR.Right,xRR.Top + yy1)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top);
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom - yy1);
      if d2CornerBottomRight in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                             begin
                               cairo_line_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Bottom - yy1);
                               cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom - yy2);
                               cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
                             end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Bottom - yy1,xRR.Right - xx1,xRR.Bottom - yy2,xRR.Right - xx1,xRR.Bottom);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom - (yy2),xRR.Right - xx2,xRR.Bottom,xRR.Right - xx1,xRR.Bottom)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom);
        cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom);

      if d2CornerBottomLeft in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                             begin
                               cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom - yy2);
                               cairo_line_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Bottom - yy1);
                               cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
                             end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom - yy2,xRR.Left + xx2,xRR.Bottom - yy1,xRR.Left,xRR.Bottom - yy1);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Bottom,xRR.Left,xRR.Bottom - (yy2),xRR.Left,xRR.Bottom - yy1)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom);
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
      end;

    end;
    cairo_close_path(Pcairo_t(Handle));
    cairo_stroke(Pcairo_t(Handle));
  end;
end;


procedure TD2CanvasCairo.FillEllipse(const ARect:TD2Rect; const AOpacity:single);
begin
  if Handle=0 then Exit;

  if FFill.Style <> d2BrushNone then
  begin
    ApplyFill(ARect,AOpacity);
    cairo_save(Pcairo_t(Handle));
    cairo_translate(Pcairo_t(Handle),(ARect.Left + ARect.Right) / 2,(ARect.Top + ARect.Bottom) / 2);
    cairo_scale(Pcairo_t(Handle),d2RectWidth(ARect) / 2,d2RectHeight(ARect) / 2);
    cairo_arc(Pcairo_t(Handle),0,0,1,0,Pi * 2);
    cairo_restore(Pcairo_t(Handle));
    cairo_fill(Pcairo_t(Handle));

    DeapplyFill;
  end;
end;

procedure TD2CanvasCairo.DrawEllipse(const ARect:TD2Rect; const AOpacity:single);
begin
  if Handle=0 then Exit;

  if FStroke.Style <> d2BrushNone then
  begin
    ApplyStroke(ARect,AOpacity);
    cairo_save(Pcairo_t(Handle));
    cairo_translate(Pcairo_t(Handle),(ARect.Left + ARect.Right) / 2,(ARect.Top + ARect.Bottom) / 2);
    cairo_scale(Pcairo_t(Handle),d2RectWidth(ARect) / 2,d2RectHeight(ARect) / 2);
    cairo_arc(Pcairo_t(Handle),0,0,1,0,Pi * 2);
    cairo_restore(Pcairo_t(Handle));
    cairo_stroke(Pcairo_t(Handle));
  end;
end;


function TD2CanvasCairo.TextToPath(Path:TD2PathData; const ARect:TD2Rect; const AText:WideString;
                                   const WordWrap:boolean; const ATextAlign:TD2TextAlign;
                                   const AVTextAlign:TD2TextAlign=d2TextAlignCenter):boolean;
var
  i:integer;
  xtext:string;
  xPFD:PPangoFontDescription;
  xCP1,xCP2:TD2Point;
  xsss:String;
  xpth:Pcairo_path_t;
  xpthdata:Pcairo_path_data_t;
  xpthdatatyp:cairo_path_data_type_t;
  xprec,xprec2:TPangoRectangle;
  xofsV,xofsH:single;
begin
  Result:=false;

  if AText <> '' then
  begin
    Path.Clear;

    xsss:=FFont.Family;
    xPFD:=pango_font_description_from_string(Pchar(xsss + ' ' + IntToStr(Round(FFont.Size * 0.75))));

    if xPFD=nil then
      xPFD:=pango_font_description_from_string(Pchar('Sans ' + IntToStr(Round(FFont.Size * 0.75))));

    if xPFD <> nil then
    begin
      if FFont.Style in [d2FontItalic,d2FontBoldItalic] then
        pango_font_description_set_style(xPFD,PANGO_STYLE_ITALIC);

      if FFont.Style in [d2FontBold,d2FontBoldItalic] then
        pango_font_description_set_weight(xPFD,PANGO_WEIGHT_BOLD);

      pango_layout_set_font_description(fpPangoLayout,xPFD);
      pango_font_description_free(xPFD);
    end;

    pango_layout_set_height(fpPangoLayout,Round(d2RectHeight(ARect) * PANGO_SCALE));

    if WordWrap then
      pango_layout_set_width(fpPangoLayout,Round(d2RectWidth(ARect) * PANGO_SCALE)) else
      pango_layout_set_width(fpPangoLayout,5000 * PANGO_SCALE);

    xofsH:=0;

    case ATextAlign of
         d2TextAlignFar:
          begin
             pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_RIGHT);
             if not WordWrap then xofsH:=-5000 + d2RectWidth(ARect);
          end;
         d2TextAlignNear:
          begin
              pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_LEFT);
          end;
         d2TextAlignCenter:
          begin
            pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_CENTER);
            if not WordWrap then xofsH:=-2500 + (d2RectWidth(ARect) / 2);
          end;
    end;

    xtext:=UTF8Encode(AText);
    pango_layout_set_text(fpPangoLayout,PChar(xtext),-1);

    pango_layout_get_pixel_extents(fpPangoLayout,@xprec,@xprec2);

    case AVTextAlign of
        d2TextAlignFar:
          begin
            xofsV:=d2RectHeight(ARect) - xprec2.height;
          end;
        d2TextAlignNear:
          begin
            xofsV:=0;
          end;
        d2TextAlignCenter:
          begin
            xofsV:=(d2RectHeight(ARect) - xprec2.height) / 2;
          end;
    end;

    cairo_new_path(fpCairoT);

    pango_cairo_layout_path(fpCairoT,fpPangoLayout);

    xpth:=cairo_copy_path(fpCairoT);

    if xpth <> nil then
    begin
      xpthdata:=xpth^.data;
      i:=0;
      while i < xpth^.num_data do
      begin

        xpthdatatyp:=xpthdata.header.type_;
        Inc(xpthdata);
        Inc(i);

        if xpthdatatyp=CAIRO_PATH_CLOSE_PATH then
        begin
          Path.ClosePath;
          Continue;
        end;

        if xpthdatatyp=CAIRO_PATH_MOVE_TO then
        begin
          Path.MoveTo(d2Point(xpthdata.point.x + xofsH,xpthdata.point.y + xofsV));
        end;

        if xpthdatatyp=CAIRO_PATH_LINE_TO then
        begin
          Path.LineTo(d2Point(xpthdata.point.x + xofsH,xpthdata.point.y + xofsV));
        end;

        if xpthdatatyp=CAIRO_PATH_CURVE_TO then
        begin
          xCP1:=d2Point(xpthdata.point.x + xofsH,xpthdata.point.y + xofsV);
          Inc(xpthdata);
          Inc(i);
          xCP2:=d2Point(xpthdata.point.x + xofsH,xpthdata.point.y + xofsV);
          Inc(xpthdata);
          Inc(i);
          Path.CurveTo(xCP1,xCP2,d2Point(xpthdata.point.x + xofsH,xpthdata.point.y + xofsV));
        end;
        Inc(xpthdata);
        Inc(i);
      end;
    end;

    Result:=true;
    cairo_path_destroy(xpth);

  end else
  begin
    Result:=false;
  end;
end;

procedure TD2CanvasCairo.MeasureText(var ARect:TD2Rect; AClipRect:TD2Rect; const AText:WideString;
                                     const WordWrap:boolean; const ATextAlign,AVTextAlign:TD2TextAlign);
var
  xss:string;
  xxss:String;
  xprec,xprec2:TPangoRectangle;
  xPFD:PPangoFontDescription;
begin
  if not WordWrap then ARect.Right:=ARect.Left;

  if Length(AText)=0 then Exit;

  xxss:=FFont.Family;
  xPFD:=pango_font_description_from_string(Pchar(xxss + ' ' + IntToStr(Round(FFont.Size * 0.75))));

  if xPFD=nil then
    xPFD:=pango_font_description_from_string(Pchar('Sans ' + IntToStr(Round(FFont.Size * 0.75))));

  if xPFD <> nil then
  begin
    if FFont.Style in [d2FontItalic,d2FontBoldItalic] then
      pango_font_description_set_style(xPFD,PANGO_STYLE_ITALIC);

    if FFont.Style in [d2FontBold,d2FontBoldItalic] then
      pango_font_description_set_weight(xPFD,PANGO_WEIGHT_BOLD);

    pango_layout_set_font_description(fpPangoLayout,xPFD);
    pango_font_description_free(xPFD);
  end;

  pango_layout_set_height(fpPangoLayout,Round(d2RectHeight(ARect) * PANGO_SCALE));

  if WordWrap then
    pango_layout_set_width(fpPangoLayout,Round(d2RectWidth(ARect) * PANGO_SCALE)) else
    pango_layout_set_width(fpPangoLayout,5000 * PANGO_SCALE);

  case ATextAlign of
      d2TextAlignFar:
        begin
          pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_RIGHT);
        end;
      d2TextAlignNear:
        begin
          pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_LEFT);
        end;
      d2TextAlignCenter:
        begin
          pango_layout_set_alignment(fpPangoLayout,PANGO_ALIGN_CENTER);
        end;
  end;

  xss:=UTF8Encode(AText);
  pango_layout_set_text(fpPangoLayout,PChar(xss),-1);

  pango_layout_get_pixel_extents(fpPangoLayout,@xprec,@xprec2);

  ARect.Right:=ARect.Left + xprec2.width;
  ARect.Bottom:=ARect.Top + xprec2.height;
end;


procedure TD2CanvasCairo.FillRect(const ARect:TD2Rect;
                                  const xRadius,yRadius:single;
                                  const ACorners:TD2Corners;
                                  const AOpacity:single;
                                  const ACornerType:TD2CornerType=d2CornerRound);
var
  xx1,xx2,yy1,yy2:single;
  xRR:TD2Rect;
begin
  if Handle=0 then Exit;

  if FFill.Style <> d2BrushNone then
  begin
    xRR:=ARect;
    ApplyFill(xRR,AOpacity);

    if ((xRadius=0) and (yRadius=0)) or (ACorners=[]) then
    begin
      cairo_rectangle(Pcairo_t(Handle),xRR.Left,xRR.Top,d2RectWidth(xRR),d2RectHeight(xRR));
      cairo_fill(Pcairo_t(Handle));
    end else
    begin

      xx1:=xRadius;
      if d2RectWidth(xRR) - (xx1 * 2) < 0 then xx1:=(xRadius * (d2RectWidth(xRR) / (xx1 * 2)));
      xx2:=xx1 * CurveKappaInv;
      yy1:=yRadius;
      if d2RectHeight(xRR) - (yy1 * 2) < 0 then yy1:=(yRadius * (d2RectHeight(xRR) / (yy1 * 2)));
      yy2:=yy1 * CurveKappaInv;
      cairo_new_path(Pcairo_t(Handle));
      cairo_move_to(Pcairo_t(Handle),xRR.Left,xRR.Top + yy1);

      if d2CornerTopLeft in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                           begin
                             cairo_line_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Top + yy1);
                             cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top + yy2);
                             cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
                           end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Top + yy1,xRR.Left + xx1,xRR.Top + yy2,xRR.Left + xx1,xRR.Top);
         else
           cairo_curve_to(Pcairo_t(Handle),xRR.Left,xRR.Top + (yy2),xRR.Left + xx2,xRR.Top,xRR.Left + xx1,xRR.Top)
         end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Top);
        cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Top);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top);

      if d2CornerTopRight in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                            begin
                              cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top + yy2);
                              cairo_line_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Top + yy1);
                              cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
                            end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Top + yy2,xRR.Right - xx2,xRR.Top + yy1,xRR.Right,xRR.Top + yy1);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Top,xRR.Right,xRR.Top + (yy2),xRR.Right,xRR.Top + yy1)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top);
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Top + yy1);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom - yy1);

      if d2CornerBottomRight in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                            begin
                              cairo_line_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Bottom - yy1);
                              cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom - yy2);
                              cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
                            end;
          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Right - xx2,xRR.Bottom - yy1,xRR.Right - xx1,xRR.Bottom - yy2,xRR.Right - xx1,xRR.Bottom);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom - (yy2),xRR.Right - xx2,xRR.Bottom,xRR.Right - xx1,xRR.Bottom)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Right,xRR.Bottom);
        cairo_line_to(Pcairo_t(Handle),xRR.Right - xx1,xRR.Bottom);
      end;

      cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom);

      if d2CornerBottomLeft in ACorners then
      begin
        case ACornerType of
          d2CornerInnerLine:
                            begin
                              cairo_line_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom - yy2);
                              cairo_line_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Bottom - yy1);
                              cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
                            end;

          d2CornerBevel:cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
          d2CornerInnerRound:cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx1,xRR.Bottom - yy2,xRR.Left + xx2,xRR.Bottom - yy1,xRR.Left,xRR.Bottom - yy1);
        else
          cairo_curve_to(Pcairo_t(Handle),xRR.Left + xx2,xRR.Bottom,xRR.Left,xRR.Bottom - (yy2),xRR.Left,xRR.Bottom - yy1)
        end;

      end else
      begin
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom);
        cairo_line_to(Pcairo_t(Handle),xRR.Left,xRR.Bottom - yy1);
      end;

    end;

    cairo_close_path(Pcairo_t(Handle));
    cairo_fill(Pcairo_t(Handle));
    DeapplyFill;
  end;
end;

procedure TD2CanvasCairo.DrawThumbnail(const ABitmap:TD2Bitmap; const aWidth,aHeight:single);
begin
 //.....
end;

procedure TD2CanvasCairo.DrawPath(const APath:TD2PathData; const ARect:TD2Rect; const AOpacity:single);
var
  i:integer;
  W,H,xNW,xNH:single;
  xD2R:TD2Rect;
  xCP1,xCP2:TD2Point;
begin
  if Handle=0 then Exit;
  if FStroke.Style=d2BrushNone then Exit;

  ApplyStroke(ARect,AOpacity);
  xD2R:=APath.GetBounds;
  W:=d2RectWidth(xD2R);
  H:=d2RectHeight(xD2R);
  xNW:=d2RectWidth(ARect);
  xNH:=d2RectHeight(ARect);

  cairo_save(Pcairo_t(Handle));
  cairo_new_path(Pcairo_t(Handle));

  i:=0;

  while i < Length(APath.PathData) do
  begin
    case APath.PathData[i].Kind of
      d2PathPointLineTo:
        begin
          cairo_line_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
            ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
      d2PathPointMoveTo:
        begin
          cairo_move_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
            ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
      d2PathPointClose:
        begin
          cairo_close_path(Pcairo_t(Handle));
        end;
      d2PathPointCurveTo:
        begin
          xCP1:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW, ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          xCP2:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW, ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          cairo_curve_to(Pcairo_t(Handle),xCP1.X,xCP1.Y,xCP2.X,xCP2.Y,ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                          ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
    end;

    inc(i);
  end;

  cairo_stroke(Pcairo_t(Handle));
  cairo_restore(Pcairo_t(Handle));
end;


procedure TD2CanvasCairo.DoDestroyBitmap(Sender:TObject);
begin
  if (Sender <> nil) then
  begin
    if (TD2Bitmap(Sender).Handle <> 0) then
      cairo_surface_destroy(Pcairo_surface_t(TD2Bitmap(Sender).Handle));

    TD2Bitmap(Sender).Handle:=0;
  end;
end;

procedure TD2CanvasCairo.UpdateBitmap(ABitmap:TD2Bitmap);
begin

  if ABitmap=nil then Exit;

  if ABitmap.Handle=0 then
  begin
    ABitmap.Handle:=PtrInt(cairo_image_surface_create_for_data(PByte(ABitmap.StartLine),CAIRO_FORMAT_ARGB32,
                            ABitmap.Width,ABitmap.Height,ABitmap.Width * 4));
  end;

  ABitmap.NeedUpdate:=false;
  ABitmap.OnDestroyHandle:=DoDestroyBitmap;
end;

function TD2CanvasCairo.PtInPath(const APoint:TD2Point; const ARect:TD2Rect; const APath:TD2PathData):boolean;
var
  i:integer;
  W,H,xNW,xNH:single;
  xCP1,xCP2:TD2Point;
  xD2R:TD2Rect;
begin
  Result:=false;
  if Handle=0 then Exit;

  xD2R:=APath.GetBounds;
  W:=d2RectWidth(xD2R);
  H:=d2RectHeight(xD2R);
  xNW:=d2RectWidth(ARect);
  xNH:=d2RectHeight(ARect);
  cairo_save(Pcairo_t(Handle));
  cairo_new_path(Pcairo_t(Handle));

  i:=0;

  while i < Length(APath.PathData) do
  begin

    case APath.PathData[i].Kind of
      d2PathPointLineTo:
        begin
          cairo_line_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                         ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
      d2PathPointMoveTo:
        begin
          cairo_move_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                        ARect.Top + (APath.PathData[i].Point.Y -xD2R.Top) / H * xNH);
        end;
      d2PathPointClose:
        begin
          cairo_close_path(Pcairo_t(Handle));
        end;
      d2PathPointCurveTo:
        begin
          xCP1:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW, ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          xCP2:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW, ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          cairo_curve_to(Pcairo_t(Handle),xCP1.X,xCP1.Y,xCP2.X,xCP2.Y, ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                                 ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
     end;

    inc(i);
  end;

  cairo_restore(Pcairo_t(Handle));
  Result:=cairo_in_fill(Pcairo_t(Handle),APoint.X,APoint.Y) > 0;
end;

procedure TD2CanvasCairo.FillPath(const APath:TD2PathData; const ARect:TD2Rect; const AOpacity:single);
var
  i:integer;
  xCP1,xCP2:TD2Point;
  xD2R:TD2Rect;
  W,H,xNW,xNH:single;
begin
  if Handle=0 then Exit;
  if FFill.Style=d2BrushNone then Exit;

  ApplyFill(ARect,AOpacity);
  xD2R:=APath.GetBounds;
  W:=d2RectWidth(xD2R);
  H:=d2RectHeight(xD2R);
  xNW:=d2RectWidth(ARect);
  xNH:=d2RectHeight(ARect);
  cairo_save(Pcairo_t(Handle));
  cairo_new_path(Pcairo_t(Handle));
  i:=0;

  while i < Length(APath.PathData) do
  begin

    case APath.PathData[i].Kind of
      d2PathPointLineTo:
        begin
          cairo_line_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                         ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
      d2PathPointMoveTo:
        begin
          cairo_move_to(Pcairo_t(Handle),ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                        ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
      d2PathPointClose:
        begin
          cairo_close_path(Pcairo_t(Handle));
        end;
      d2PathPointCurveTo:
        begin
          xCP1:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          xCP2:=d2Point(ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
          Inc(i);
          cairo_curve_to(Pcairo_t(Handle),xCP1.X,xCP1.Y,xCP2.X,xCP2.Y,ARect.Left + (APath.PathData[i].Point.X - xD2R.Left) / W * xNW,
                          ARect.Top + (APath.PathData[i].Point.Y - xD2R.Top) / H * xNH);
        end;
    end;

    inc(i);
  end;

  cairo_fill(Pcairo_t(Handle));
  DeApplyFill;
  cairo_restore(Pcairo_t(Handle));
end;


procedure TD2CanvasCairo.FillText(const ARect,AClipRect:TD2Rect; const AText:WideString;
                                  const WordWrap:boolean; const AOpacity:single; const ATextAlign:TD2TextAlign;
                                  const AVTextAlign:TD2TextAlign=d2TextAlignCenter);
var
  xtext:string;
  xsss:String;
  xPFD:PPangoFontDescription;
  xpplayout:PPangoLayout;
  xprec,xprec2:TPangoRectangle;
begin
  if Handle=0 then Exit;

  ApplyFill(ARect,AOpacity);
  cairo_save(Pcairo_t(Handle));

  cairo_rectangle(Pcairo_t(Handle),ARect.Left,ARect.Top,d2RectWidth(ARect),d2RectHeight(ARect));
  cairo_clip(Pcairo_t(Handle));

  cairo_translate(Pcairo_t(Handle),ARect.left,ARect.Top);

  xpplayout:=pango_cairo_create_layout(Pcairo_t(Handle));

  if xpplayout <> nil then
  begin
    xsss:=FFont.Family;
    xPFD:=pango_font_description_from_string(Pchar(xsss + ' ' + IntToStr(Round(FFont.Size * 0.75))));

    if xPFD=nil then
      xPFD:=pango_font_description_from_string(Pchar('Sans ' + IntToStr(Round(FFont.Size * 0.75))));

    if xPFD <> nil then
    begin
      if FFont.Style in [d2FontItalic,d2FontBoldItalic] then
        pango_font_description_set_style(xPFD,PANGO_STYLE_ITALIC);

      if FFont.Style in [d2FontBold,d2FontBoldItalic] then
        pango_font_description_set_weight(xPFD,PANGO_WEIGHT_BOLD);

      pango_layout_set_font_description(xpplayout,xPFD);
      pango_font_description_free(xPFD);
    end;
    pango_layout_set_height(xpplayout,Round(d2RectHeight(ARect) * PANGO_SCALE));

    if WordWrap then
      pango_layout_set_width(xpplayout,Round(d2RectWidth(ARect) * PANGO_SCALE)) else
      pango_layout_set_width(xpplayout,5000 * PANGO_SCALE);

    case ATextAlign of
        d2TextAlignFar:
          begin
            pango_layout_set_alignment(xpplayout,PANGO_ALIGN_RIGHT);
            if not WordWrap then
              cairo_translate(Pcairo_t(Handle),-5000 + d2RectWidth(ARect),0);
          end;
        d2TextAlignNear:
          begin
            pango_layout_set_alignment(xpplayout,PANGO_ALIGN_LEFT);
          end;
        d2TextAlignCenter:
          begin
            pango_layout_set_alignment(xpplayout,PANGO_ALIGN_CENTER);
            if not WordWrap then
              cairo_translate(Pcairo_t(Handle),-2500 + (d2RectWidth(ARect) / 2),0);
          end;
    end;

    xtext:=UTF8Encode(AText);
    pango_layout_set_text(xpplayout,PChar(xtext),-1);

    pango_layout_get_pixel_extents(xpplayout,@xprec,@xprec2);

    case AVTextAlign of
        d2TextAlignFar:
          begin
            cairo_translate(Pcairo_t(Handle),0,(d2RectHeight(ARect) - xprec2.height));
          end;
        d2TextAlignNear:
          begin
          end;
        d2TextAlignCenter:
          begin
            cairo_translate(Pcairo_t(Handle),0,(d2RectHeight(ARect) - xprec2.height) / 2);
          end;
    end;

    pango_cairo_show_layout(Pcairo_t(Handle),xpplayout);

    g_object_unref(xpplayout);
  end;
  cairo_restore(Pcairo_t(Handle));
  DeapplyFill;
end;


procedure TD2CanvasCairo.DrawBitmap(const ABitmap:TD2Bitmap;
                                    const SrcRect,DstRect:TD2Rect;
                                    const AOpacity:single;
                                    const HighSpeed:boolean=false);
var
  xcm:cairo_matrix_t;
  xpb,xsubpb:PGdkPixbuf;
  xpat:Pcairo_pattern_t;
begin
  UpdateBitmap(ABitmap);
  if Handle=0 then Exit;
  if ABitmap.Handle=0 then Exit;

  if (SrcRect.Left <> 0) or (SrcRect.top <> 0) or (SrcRect.right <> ABitmap.Width) or (SrcRect.bottom <> ABitmap.Height) then
  begin
    xpb:=gdk_pixbuf_new_from_data(PByte(ABitmap.StartLine),GDK_COLORSPACE_RGB,true,8,ABitmap.Width,ABitmap.Height,ABitmap.Width * 4,nil,nil);

    if xpb <> nil then
    begin

      xsubpb:=gdk_pixbuf_new_subpixbuf(xpb,Trunc(SrcRect.Left),Trunc(SrcRect.top),trunc(d2RectWidth(SrcRect)),trunc(d2RectHeight(SrcRect)));

      if xsubpb <> nil then
      begin
        cairo_save(Pcairo_t(Handle));
        gdk_cairo_set_source_pixbuf(Pcairo_t(Handle),xsubpb,0,0);
        xpat:=cairo_get_source(Pcairo_t(Handle));
        cairo_matrix_init_translate(@xcm,-DstRect.Left * (d2RectWidth(SrcRect) / d2RectWidth(DstRect)),-DstRect.Top * (d2RectHeight(SrcRect) / d2RectHeight(DstRect)));
        cairo_matrix_scale(@xcm,d2RectWidth(SrcRect) / d2RectWidth(DstRect),d2RectHeight(SrcRect) / d2RectHeight(DstRect));
        cairo_pattern_set_matrix(xpat,@xcm);

        if HighSpeed then
          cairo_pattern_set_filter(xpat,CAIRO_FILTER_FAST) else
          cairo_pattern_set_filter(xpat,CAIRO_FILTER_BEST);

        cairo_pattern_set_extend(xpat,CAIRO_EXTEND_NONE);
        cairo_set_source(Pcairo_t(Handle),xpat);
        cairo_rectangle(Pcairo_t(Handle),DstRect.Left,DstRect.Top,d2RectWidth(DstRect),d2RectHeight(DstRect));
        cairo_clip(Pcairo_t(Handle));
        cairo_paint_with_alpha(Pcairo_t(Handle),AOpacity);
        cairo_restore(Pcairo_t(Handle));

        g_object_unref(xsubpb);
      end;

      g_object_unref(xpb);
    end;
    Exit;
  end;

  cairo_save(Pcairo_t(Handle));
  xpat:=cairo_pattern_create_for_surface(Pcairo_surface_t(ABitmap.Handle));
  cairo_matrix_init_translate(@xcm,-DstRect.Left * (d2RectWidth(SrcRect) / d2RectWidth(DstRect)),-DstRect.Top * (d2RectHeight(SrcRect) / d2RectHeight(DstRect)));
  cairo_matrix_scale(@xcm,d2RectWidth(SrcRect) / d2RectWidth(DstRect),d2RectHeight(SrcRect) / d2RectHeight(DstRect));
  cairo_pattern_set_matrix(xpat,@xcm);

  if HighSpeed then
    cairo_pattern_set_filter(xpat,CAIRO_FILTER_FAST) else
    cairo_pattern_set_filter(xpat,CAIRO_FILTER_BEST);

  cairo_pattern_set_extend(xpat,CAIRO_EXTEND_NONE);

  cairo_set_source(Pcairo_t(Handle),xpat);
  cairo_rectangle(Pcairo_t(Handle),DstRect.Left,DstRect.Top,d2RectWidth(DstRect),d2RectHeight(DstRect));
  cairo_clip(Pcairo_t(Handle));
  cairo_paint_with_alpha(Pcairo_t(Handle),AOpacity);
  cairo_pattern_destroy(xpat);
  cairo_restore(Pcairo_t(Handle));
end;

