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