   {%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 *****
 
***************************************************************************}


Var
   Res: HResult;

Const
   TargetMode: TD2D1_RenderTargetType = D2D1_RENDER_TARGET_TYPE_default;

Type

   TD2HackBitmap = Class(TD2Bitmap);

Var
   Factory: ID2D1Factory;
   DWriteFactory: IDWriteFactory;
   ImagingFactory: IWICImagingFactory;

Type

   TD2FilterWIC = 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 SaveToFile(Const AFileName: String; Var Bitmap: TD2Bitmap; Const Params: String = ''): Boolean; Override;
      Function LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean; Var Bitmap: TD2Bitmap): 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;

   TD2CanvasD2D = Class(TD2Canvas)
   Private
      FLastState: Integer;
      FBufferHandle: Cardinal;
      FBitmapInfo: TBitmapInfo;
      RenderTarget: ID2D1RenderTarget;
      Brush: ID2D1Brush;
      StrokeBrush: ID2D1Brush;
      StrokeStyle: ID2D1StrokeStyle;
      Layer: ID2D1Layer;
      FSaved: Pointer;
      WICBitmap: IWICBitmap;
      Procedure CreateResources;
      Procedure DisposeResources;
   Protected
      Procedure ApplyFill(ARect: TD2Rect; Const AOpacity: Single);
      Procedure ApplyStroke(ARect: TD2Rect; Const AOpacity: Single);
      Procedure FontChanged(Sender: TObject); Override;
      Procedure IntFillPath(P: ID2D1Geometry; R: TD2Rect; Opacity: Single);
      Procedure IntFillRect(R: TD2Rect; Opacity: Single);
   Public
      Constructor Create(Const AWidth, AHeight: Integer); Override;
      Constructor CreateFromBitmap(Const ABitmap: TD2Bitmap); Override;
      Destructor Destroy; Override;
      Class Function GetBitmapScanline(Bitmap: TD2Bitmap; y: Integer): PD2ColorArray; Override;
      Function BeginScene: Boolean; Override;
      Procedure EndScene; 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: TD2Color); 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 FillRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound); Override;
      Procedure DrawRect(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 DrawEllipse(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 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;
      Function PtInPath(Const APoint: TD2Point; Const ARect: TD2Rect; Const APath: TD2PathData): Boolean; Override;
      Procedure FillPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single); 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;
   End;

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


Procedure SAFE_RELEASE(Var i);
Begin
   If IUnknown(i) <> nil Then
      IUnknown(i) := nil;
End;

//==================== TD2FilterWIC =======================================

Class Function TD2FilterWIC.GetFileTypes: String;
Begin
   Result := '*.bmp;*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.gif;*.ico;*.wmp';
End;

Class Function TD2FilterWIC.GetImageSize(Const AFileName: String): TD2Point;
Var
   Dec: IWICBitmapDecoder;
   frame: IWICBitmapFrameDecode;
   W, H: Cardinal;
   fn: WideString;
Begin
   W := 0;
   H := 0;
   fn := AFileName;
   ImagingFactory.CreateDecoderFromFilename(Pwidechar(fn), nil, $FFFFFFFF, WICDecodeMetadataCacheOnDemand, Dec);

   If Assigned(Dec) Then
   Begin
      Dec.GetFrame(0, frame);

      If Assigned(frame) Then
      Begin
         frame.GetSize(W, H);
      End;
   End;

   Result := d2Point(W, H);
End;

Function TD2FilterWIC.LoadFromFile(Const AFileName: String; Const Rotate: Single; Var Bitmap: TD2Bitmap): Boolean;
Var
   Dec: IWICBitmapDecoder;
   frame: IWICBitmapFrameDecode;
   conv: IWICFormatConverter;
   W, H: Cardinal;
   fn: WideString;
Begin
   fn := AFileName;
   Result := False;
   ImagingFactory.CreateDecoderFromFilename(Pwidechar(fn), nil, $FFFFFFFF, WICDecodeMetadataCacheOnDemand, Dec);

   If Assigned(Dec) Then
   Begin
      Dec.GetFrame(0, frame);
      If Assigned(frame) Then
      Begin
         ImagingFactory.CreateFormatConverter(conv);
         Res := conv.Initialize(frame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0);

         If Res = 0 Then
         Begin
            conv.GetSize(W, H);

            Bitmap.SetSize(W, H);
            Bitmap.Clear($FFFF0000);
            Res := conv.CopyPixels(nil, W * 4, W * H * 4, Pbyte(Bitmap.Startline));
            Result := True;
         End;
      End;
   End;
End;

Function TD2FilterWIC.LoadFromStream(Const AStream: TStream; Var Bitmap: TD2Bitmap): Boolean;
Var
   Dec: IWICBitmapDecoder;
   mem: TMemoryStream;
   stream: IWICStream;
   frame: IWICBitmapFrameDecode;
   conv: IWICFormatConverter;
   W, H: Cardinal;
Begin
   Result := False;
   mem := TMemoryStream.Create;
   mem.CopyFrom(AStream, AStream.Size);
   ImagingFactory.CreateStream(stream);
   stream.InitializeFromMemory(mem.Memory, mem.Size);

   ImagingFactory.CreateDecoderFromStream(stream, nil, WICDecodeMetadataCacheOnDemand, Dec);
   If Assigned(Dec) Then
   Begin
      Dec.GetFrame(0, frame);
      If Assigned(frame) Then
      Begin
         ImagingFactory.CreateFormatConverter(conv);
         Res := conv.Initialize(frame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0);
         If Res = 0 Then
         Begin
            conv.GetSize(W, H);

            Bitmap.SetSize(W, H);
            Res := conv.CopyPixels(nil, W * 4, W * H * 4, Pbyte(Bitmap.Startline));
            Result := True;
         End;
      End;
   End;
   mem.Free;
End;

Function TD2FilterWIC.LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean; Var Bitmap: TD2Bitmap): Boolean;
Var
   bmp: IWICBitmapSource;
   Dec: IWICBitmapDecoder;
   scale: Single;
   scaler: IWICBitmapScaler;
   frame: IWICBitmapFrameDecode;
   conv: IWICFormatConverter;
   W, H: Cardinal;
   R: TD2Rect;
   fn: WideString;
Begin
   fn := AFileName;
   Result := False;
   ImagingFactory.CreateDecoderFromFilename(Pwidechar(fn), nil, $FFFFFFFF, WICDecodeMetadataCacheOnDemand, Dec);
   If Assigned(Dec) Then
   Begin
      Dec.GetFrame(0, frame);
      If UseEmbedded Then
         frame.GetThumbnail(bmp);
      If Assigned(bmp) Then
      Begin
         ImagingFactory.CreateFormatConverter(conv);
         Res := conv.Initialize(bmp, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0);
         If Res = 0 Then
         Begin
            conv.GetSize(W, H);

            Bitmap.SetSize(W, H);
            Bitmap.Clear($FFFF0000);
            Res := conv.CopyPixels(nil, W * 4, W * H * 4, Pbyte(Bitmap.Startline));
            Result := True;
         End;
      End Else Begin
         If Assigned(frame) Then
         Begin
            frame.GetSize(W, H);
            R := d2Rect(0, 0, W, H);
            scale := d2FitRect(R, d2Rect(0, 0, AFitWidth, AFitHeight));
            If scale = 0 Then
               scale := 0.001;
            If scale < 1 Then
               scale := 1;

            ImagingFactory.CreateBitmapScaler(scaler);
            Res := scaler.Initialize(frame, round(W / scale), round(H / scale), WICBitmapInterpolationModeLinear);

            ImagingFactory.CreateFormatConverter(conv);
            Res := conv.Initialize(scaler, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0, 0);

            If Res = 0 Then
            Begin
               conv.GetSize(W, H);

               Bitmap.SetSize(W, H);
               Bitmap.Clear($FFFF0000);
               Res := conv.CopyPixels(nil, W * 4, W * H * 4, Pbyte(Bitmap.Startline));
               Result := True;
            End;
         End;
      End;
   End;
End;

Function TD2FilterWIC.SaveToFile(Const AFileName: String; Var Bitmap: TD2Bitmap; Const Params: String): Boolean;
Var
   bmp: IWICBitmap;
   enc: IWICBitmapEncoder;
   encoderType: TGuid;
   stream: IWICStream;
   frame: IWICBitmapFrameEncode;
   conv: IWICFormatConverter;
   props: IPropertyBag2;
   fn: WideString;
   pformat: WICPixelFormatGUID;
Begin
   fn := AFileName;
   Result := False;
   encoderType := GUID_ContainerFormatPng;

   If (LowerCase(ExtractFileExt(AFileName)) = '.jpg') Or (LowerCase(ExtractFileExt(AFileName)) = '.jpeg') Then
      encoderType := GUID_ContainerFormatJpeg;

   If (LowerCase(ExtractFileExt(AFileName)) = '.bmp') Then
      encoderType := GUID_ContainerFormatBmp;

   If (LowerCase(ExtractFileExt(AFileName)) = '.png') Then
      encoderType := GUID_ContainerFormatPng;

   If (LowerCase(ExtractFileExt(AFileName)) = '.tif') Or (LowerCase(ExtractFileExt(AFileName)) = '.tiff') Then
      encoderType := GUID_ContainerFormatTiff;

   If (LowerCase(ExtractFileExt(AFileName)) = '.gif') Then
      encoderType := GUID_ContainerFormatGif;

   If (LowerCase(ExtractFileExt(AFileName)) = '.wmp') Then
      encoderType := GUID_ContainerFormatWmp;

   ImagingFactory.CreateEncoder(encoderType, nil, enc);

   If Assigned(enc) Then
   Begin
      ImagingFactory.CreateStream(stream);
      stream.InitializeFromFilename(Pwidechar(fn), $FFFFFFFF);
      enc.Initialize(stream, WICBitmapEncoderNoCache);
      enc.CreateNewFrame(frame, props);
      If Assigned(frame) Then
      Begin
         Res := frame.Initialize(props);

         Res := frame.SetSize(Bitmap.Width, Bitmap.Height);
         pformat := GUID_WICPixelFormat32bppPBGRA;
         Res := frame.SetPixelFormat(pformat);
         If IsEqualGuid(pformat, GUID_WICPixelFormat32bppPBGRA) Then
         Begin
            Res := frame.WritePixels(Bitmap.Height, Bitmap.Width * 4, Bitmap.Width * Bitmap.Height * 4, Pbyte(Bitmap.Startline));
            Res := frame.Commit;
            Res := enc.Commit;
         End Else Begin
            ImagingFactory.CreateBitmapFromMemory(Bitmap.Width, Bitmap.Height, GUID_WICPixelFormat32bppPBGRA,
               Bitmap.Width * 4, Bitmap.Width * Bitmap.Height * 4, Pbyte(Bitmap.Startline), bmp);
            ImagingFactory.CreateFormatConverter(conv);
            Res := conv.Initialize(bmp, pformat, WICBitmapDitherTypeNone, nil, 0, 0);
            Res := frame.WriteSource(bmp, nil);
            Res := frame.Commit;
            Res := enc.Commit;
         End;
      End;
   End;
End;

Function TD2FilterWIC.SaveToStream(Const AStream: TStream; Var Bitmap: TD2Bitmap; Const Format, Params: String): Boolean;
Var
   bmp: IWICBitmap;
   enc: IWICBitmapEncoder;
   encoderType: TGuid;
   stream: IWICStream;
   frame: IWICBitmapFrameEncode;
   conv: IWICFormatConverter;
   props: IPropertyBag2;
   pformat: WICPixelFormatGUID;
   adapter: TStreamAdapter;
Begin
   Result := False;

   encoderType := GUID_ContainerFormatPng;

   If LowerCase(Format) = 'jpeg' Then
      encoderType := GUID_ContainerFormatJpeg;
   If LowerCase(Format) = 'jpg' Then
      encoderType := GUID_ContainerFormatJpeg;
   If LowerCase(Format) = 'png' Then
      encoderType := GUID_ContainerFormatPng;
   If LowerCase(Format) = 'bmp' Then
      encoderType := GUID_ContainerFormatBmp;
   If LowerCase(Format) = 'tif' Then
      encoderType := GUID_ContainerFormatTiff;
   If LowerCase(Format) = 'tiff' Then
      encoderType := GUID_ContainerFormatTiff;
   If LowerCase(Format) = 'gif' Then
      encoderType := GUID_ContainerFormatGif;
   If LowerCase(Format) = 'wmp' Then
      encoderType := GUID_ContainerFormatWmp;

   ImagingFactory.CreateEncoder(encoderType, nil, enc);

   If Assigned(enc) Then
   Begin
      ImagingFactory.CreateStream(stream);
      adapter := TStreamAdapter.Create(AStream);
      IUnknown(adapter)._AddRef;
      stream.InitializeFromIStream(adapter);
      enc.Initialize(stream, WICBitmapEncoderNoCache);
      enc.CreateNewFrame(frame, props);

      If Assigned(frame) Then
      Begin
         frame.Initialize(props);
         Res := frame.SetSize(Bitmap.Width, Bitmap.Height);
         pformat := GUID_WICPixelFormat32bppPBGRA;
         Res := frame.SetPixelFormat(pformat);

         If IsEqualGuid(pformat, GUID_WICPixelFormat32bppPBGRA) Then
         Begin
            Res := frame.WritePixels(Bitmap.Height, Bitmap.Width * 4, Bitmap.Width * Bitmap.Height * 4, Pbyte(Bitmap.Startline));
            Res := frame.Commit;
            Res := enc.Commit;
         End Else Begin
            ImagingFactory.CreateBitmapFromMemory(Bitmap.Width, Bitmap.Height, GUID_WICPixelFormat32bppPBGRA,
               Bitmap.Width * 4, Bitmap.Width * Bitmap.Height * 4, Pbyte(Bitmap.Startline), bmp);
            ImagingFactory.CreateFormatConverter(conv);
            Res := conv.Initialize(bmp, pformat, WICBitmapDitherTypeNone, nil, 0, 0);
            Res := frame.WriteSource(bmp, nil);
            Res := frame.Commit;
            Res := enc.Commit;
         End;
      End;
      IUnknown(adapter)._Release;
   End;
End;


Const
   D2DERR_RECREATE_TARGET = HResult($8899000C);

Var
   D2DLibrary: Cardinal = 0;
   DWriteLibrary: Cardinal = 0;

Procedure LoadD2D;
Begin
End;

Procedure FreeD2D;
Begin
   If (D2DLibrary <> 0) Then
   Begin
      D2DLibrary := 0;
   End;
   If DWriteLibrary <> 0 Then
   Begin
      DWriteLibrary := 0;
   End;
End;

Procedure SetD2DDefault;
Begin
   If D2DLibrary <> 0 Then
   Begin
      GvarD2DefaultCanvasClass := TD2CanvasD2D;
      GvarD2DefaultFilterClass := TD2FilterWIC;
   End;
End;

Function D2iRect(Const R: TD2Rect): TD2D1_RectF;
Begin
   Result := TD2D1_RectF(R);
End;

Function D2iColor(Const AColor: TD2Color; Const Opacity: Single): TD2D1_ColorF;
Begin
   With TD2ColorRec(AColor) Do
      Result := D2D1ColorF(R / $FF, G / $FF, B / $FF, (A / $FF) * Opacity);
End;

Function D2iPoint(X, Y: Single): TD2D1_Point2F;
Begin
   Result.X := X;
   Result.Y := Y;
End;

Function D2iSize(W, H: Cardinal): TD2D_SizeU;
Begin
   Result.Width := W;
   Result.Height := H;
End;

Function D2iMatrix(M: TD2Matrix): TD2D1_Matrix3X2F;
Begin
   Result._11 := M.m11;
   Result._12 := M.m12;
   Result._21 := M.m21;
   Result._22 := M.m22;
   Result._31 := M.m31;
   Result._32 := M.m32;
End;

Function D2iBezier(x1, y1, x2, y2, x3, y3: Single): TD2D1_BezierSegment;
Begin
   Result.Point1.X := x1;
   Result.Point1.Y := y1;
   Result.Point2.X := x2;
   Result.Point2.Y := y2;
   Result.Point3.X := x3;
   Result.Point3.Y := y3;
End;

Function D2iEllipse(R: TD2Rect): TD2D1_Ellipse;
Begin
   Result.Point.x := (R.Right + R.left) / 2;
   Result.Point.y := (R.bottom + R.top) / 2;
   Result.RadiusX := (R.Right - R.left) / 2;
   Result.RadiusY := (R.bottom - R.top) / 2;
End;

Function D2iFontStyle(Style: TD2FontStyle): TDWrite_FontStyle;
Begin
   Case Style Of
      d2FontRegular: Result := DWRITE_FONT_STYLE_NORMAL;
      d2FontBold: Result := DWRITE_FONT_STYLE_NORMAL;
      d2FontItalic: Result := DWRITE_FONT_STYLE_OBLIQUE;
      d2FontBoldItalic: Result := DWRITE_FONT_STYLE_OBLIQUE;
      d2FontUnderline: Result := DWRITE_FONT_STYLE_NORMAL;
      d2FontStrikeout: Result := DWRITE_FONT_STYLE_NORMAL;
   End;
End;

Function D2iFontWeight(Style: TD2FontStyle): TDWrite_FontWeight;
Begin
   Case Style Of
      d2FontRegular: Result := DWRITE_FONT_WEIGHT_NORMAL;
      d2FontBold: Result := DWRITE_FONT_WEIGHT_BOLD;
      d2FontItalic: Result := DWRITE_FONT_WEIGHT_NORMAL;
      d2FontBoldItalic: Result := DWRITE_FONT_WEIGHT_BOLD;
      d2FontUnderline: Result := DWRITE_FONT_WEIGHT_NORMAL;
      d2FontStrikeout: Result := DWRITE_FONT_WEIGHT_NORMAL;
   End;
End;

Function BitmapProp(DXGI: TDXGI_Format; AlphaMode: TD2D1_AlphaMode): TD2D1_BitmapProperties;
Begin
   Result.PixelFormat.Format := DXGI;
   Result.PixelFormat.AlphaMode := AlphaMode;
   Result.DpiX := 0;
   Result.DpiY := 0;
End;

//================= TD2CanvasD2D ================================

Const
   SavedCount = 200;

Type

   TSaveData = Record
      GdiIndex: Integer;
      Matrix: TD2Matrix;
      AbsoluteMatrix, InvertMatrix: TD2Matrix;
      StrokeThickness: Single;
      Stroke: TD2Brush;
      Fill: TD2Brush;
   End;

   PSaveDataArray = ^TSaveDataArray;
   TSaveDataArray = Array [0..100] Of TSaveData;

Var
   D2DLoadcount: Integer = 0;

Procedure AddD2DRef;
Begin
   If Factory = nil Then
   Begin
      D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, ID2D1Factory, nil, Factory);
      DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, IDWriteFactory, IUnknown(DWriteFactory));
      CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER, IUnknown, ImagingFactory);
   End;
   D2DLoadcount := D2DLoadcount + 1;
End;

Procedure DecD2DRef;
Begin
   D2DLoadcount := D2DLoadcount - 1;
   If D2DLoadcount = 0 Then
   Begin
      SAFE_RELEASE(ImagingFactory);
      SAFE_RELEASE(DWriteFactory);
      SAFE_RELEASE(Factory);
   End;
End;

Constructor TD2CanvasD2D.Create(Const AWidth, AHeight: Integer);
Begin
   AddD2DRef;
   Inherited;
   GetMem(FSaved, SizeOf(TSaveData) * SavedCount);
End;

Constructor TD2CanvasD2D.CreateFromBitmap(Const ABitmap: TD2Bitmap);
Begin
   AddD2DRef;
   Inherited;
   FBitmap := ABitmap;
   //  UpdateBitmap(FBitmap);
   GetMem(FSaved, SizeOf(TSaveData) * SavedCount);

   If Assigned(ImagingFactory) Then
   Begin
      ImagingFactory.CreateBitmapFromMemory(FBitmap.Width, FBitmap.Height,
         GUID_WICPixelFormat32bppPBGRA,
         FBitmap.Width * 4,
         FBitmap.Width * FBitmap.Height * 4,
         Pbyte(FBitmap.Startline),
         WICBitmap);
      CreateResources;
   End;
End;

Destructor TD2CanvasD2D.Destroy;
Begin
   FreeMem(FSaved, SizeOf(TSaveData) * 100);
   SAFE_RELEASE(Brush);
   SAFE_RELEASE(StrokeBrush);
   SAFE_RELEASE(StrokeStyle);
   Inherited;
   DecD2DRef;
End;

Procedure TD2CanvasD2D.FreeBuffer;
Begin
   DisposeResources;
   If FBuffered Then
   Begin
      If FBufferHandle = 0 Then
         Exit;
      If FHandle <> 0 Then
         DeleteDC(FHandle);

      FHandle := 0;

      If FBufferHandle <> 0 Then
         DeleteObject(FBufferHandle);

      FBufferHandle := 0;
   End;
End;

Procedure TD2CanvasD2D.CreateResources;
Var
   SizeInPixels: D2D1_SIZE_U;
Begin
   If RenderTarget = nil Then
   Begin
      SizeInPixels.Width := FWidth;
      SizeInPixels.Height := FHeight;

      If FBitmap <> nil Then
      Begin
         Factory.CreateWicBitmapRenderTarget(WICBitmap, D2D1RenderTargetProperties(TargetMode, D2D1PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
            RenderTarget);
      End Else Begin
         If FBuffered Then
         Begin
            If FHandle = 0 Then
            Begin
               If Assigned(ImagingFactory) Then
               Begin
                  ImagingFactory.CreateBitmapFromMemory(FBitmap.Width, FBitmap.Height, GUID_WICPixelFormat32bppPBGRA,
                     FBitmap.Width * 4, FBitmap.Width * FBitmap.Height * 4, Pbyte(FBitmap.Startline), WICBitmap);
                  Factory.CreateWicBitmapRenderTarget(WICBitmap, D2D1RenderTargetProperties(TargetMode, D2D1PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
                     RenderTarget);
               End;
            End Else Begin
               Factory.CreateDCRenderTarget(D2D1RenderTargetProperties(TargetMode, D2D1PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
                  ID2D1DCRenderTarget(RenderTarget));
               ID2D1DCRenderTarget(RenderTarget).BindDC(FHandle, Rect(0, 0, FWidth, FHeight));
            End;
         End Else
            Factory.CreateHWndRenderTarget(D2D1RenderTargetProperties(TargetMode, D2D1PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
               D2D1HwndRenderTargetProperties(FParent, SizeInPixels),
               ID2D1HwndRenderTarget(RenderTarget));
      End;

      RenderTarget.SetDpi(96, 96);
      RenderTarget.SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT);
   End;
End;

Procedure TD2CanvasD2D.DisposeResources;
Begin
   SAFE_RELEASE(StrokeBrush);
   SAFE_RELEASE(Brush);
   SAFE_RELEASE(Layer);
   SAFE_RELEASE(RenderTarget);
   SAFE_RELEASE(WICBitmap);
End;

Procedure TD2CanvasD2D.Clear(Const Color: TD2Color);
Begin
   RenderTarget.Clear(D2iColor(Color, 1));
End;

Procedure TD2CanvasD2D.ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color);
Begin
   //...
End;

Procedure TD2CanvasD2D.ResizeBuffer(Const AWidth, AHeight: Integer);
Var
   Size: D2D1_SIZE_U;
Begin
   If Assigned(FScene) And (FScene.GetTransparency) And Not FBuffered Then
   Begin
      FBuffered := True;
   End;

   If (AWidth = FWidth) And (AHeight = FHeight) Then
      Exit;

   If (FParent <> 0) And Not (FBuffered) And (RenderTarget <> nil) Then
   Begin
      FWidth := AWidth;
      FHeight := AHeight;
      If FWidth <= 0 Then
         FWidth := 1;
      If FHeight <= 0 Then
         FHeight := 1;
      Size.Width := AWidth;
      Size.Height := AHeight;
      ID2D1HwndRenderTarget(RenderTarget).Resize(Size);
      Exit;
   End;

   FreeBuffer;
   FWidth := AWidth;
   FHeight := AHeight;
   If FWidth <= 0 Then
      FWidth := 1;
   If FHeight <= 0 Then
      FHeight := 1;
   FResized := True;

   If FBuffered Then
   Begin
      With FBitmapInfo.bmiHeader Do
      Begin
         biSize := SizeOf(TBitmapInfoHeader);
         biPlanes := 1;
         biBitCount := 32;
         biCompression := BI_RGB;
         biWidth := AWidth;
         If biWidth <= 0 Then
            biWidth := 1;
         biHeight := -AHeight;
         If biHeight >= 0 Then
            biHeight := -1;
      End;

      FBufferHandle := CreateDIBSection(0, FBitmapInfo, DIB_RGB_COLORS, Pointer(FBufferBits), 0, 0);
      If FBufferBits = nil Then
         Raise Exception.Create('Can''t allocate the DIB handle');

      FHandle := CreateCompatibleDC(0);
      If FHandle = 0 Then
      Begin
         DeleteObject(FBufferHandle);
         FHandle := 0;
         FBufferBits := nil;
         Raise Exception.Create('Can''t create compatible DC');
      End;

      If SelectObject(FHandle, FBufferHandle) = 0 Then
      Begin
         DeleteDC(FHandle);
         DeleteObject(FBufferHandle);
         FHandle := 0;
         FBufferHandle := 0;
         FBufferBits := nil;
         Raise Exception.Create('Can''t select an object into DC');
      End;
   End;
End;

Procedure TD2CanvasD2D.FlushBuffer(Const X, Y: Integer; Const DC: Cardinal);
Begin
   If FBufferHandle = 0 Then
      Exit;
   If DC = 0 Then
      Exit;
   Windows.BitBlt(DC, X, Y, FWidth, FHeight, FHandle, 0, 0, SRCCOPY);
End;

Procedure TD2CanvasD2D.FlushBufferRect(Const X, Y: Integer; Const DC: Cardinal; Const ARect: TD2Rect);
Var
   R: TRect;
Begin
   If FBufferHandle = 0 Then
      Exit;
   If DC = 0 Then
      Exit;
   R := Rect(trunc(ARect.left), trunc(ARect.top), trunc(ARect.right) + 1, trunc(ARect.bottom) + 1);
   With R Do
   Begin
      Windows.BitBlt(DC, X + R.left, Y + R.top, R.right - R.left, R.bottom - R.top, FHandle, R.left, R.top, SRCCOPY);
   End;
End;

Function TD2CanvasD2D.BeginScene: Boolean;
Begin
   CreateResources;
   Result := Inherited BeginScene And (RenderTarget <> nil);
   If Result Then
   Begin
      FLastState := -1;
      If (FParent <> 0) And Not (FBuffered) Then
         Result := (Longword(ID2D1HwndRenderTarget(RenderTarget).CheckWindowState) And Longword(D2D1_WINDOW_STATE_OCCLUDED) = 0);
      RenderTarget.BeginDraw;
   End;
End;

Procedure TD2CanvasD2D.EndScene;
Var
   Res: HResult;
Begin
   Inherited;
   If RenderTarget <> nil Then
   Begin
      If Assigned(Layer) Then
      Begin
         RenderTarget.PopLayer;
         SAFE_RELEASE(Layer);
      End;
      Res := RenderTarget.EndDraw;
      If Res = D2DERR_RECREATE_TARGET Then
      Begin
         DisposeResources;
      End Else
      If Res <> 0 Then
      Begin
         //Res:=10;
      End;
      If WICBitmap <> nil Then
      Begin
         Res := WICBitmap.CopyPixels(nil, Width * 4, Width * Height * 4, Pbyte(FBitmap.Startline));
      End;
   End;
End;

Class Function TD2CanvasD2D.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;

Procedure TD2CanvasD2D.SetMatrix(Const M: TD2Matrix);
Begin
   FMatrix := M;
   RenderTarget.SetTransform(D2iMatrix(FMatrix));
End;

Procedure TD2CanvasD2D.MultyMatrix(Const M: TD2Matrix);
// var T:TD2Matrix;
Begin
   Inherited;
{  RenderTarget.GetTransform(D2D_MATRIX_3X2_F(T));
  M:=d2MatrixMultipy(T,M);
  RenderTarget.SetTransform(D2iMatrix(T));}
End;

Function TD2CanvasD2D.SaveCanvas: Cardinal;
Var
   i: Integer;
   B: ID2D1DrawingStateBlock;
Begin
   Result := InvalideCanvasState;
   If RenderTarget = nil Then
      Exit;

   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;

   Factory.CreateDrawingStateBlock(nil, nil, B);
   B._AddRef;
   RenderTarget.SaveDrawingState(B);

   FLastState := Result;
   FSaveData[Result].Index := Cardinal(B);
   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);
   FSaveData[Result].Data := nil;
End;

Procedure TD2CanvasD2D.RestoreCanvas(Const AState: Cardinal);
Var
   B: ID2D1DrawingStateBlock;
Begin
   If RenderTarget = nil Then
      Exit;

   If (AState >= 0) And (AState < Length(FSaveData)) Then
   Begin
      B := ID2D1DrawingStateBlock(FSaveData[AState].Index);
      RenderTarget.RestoreDrawingState(B);
      B._Release;
      SAFE_RELEASE(B);

      If FSaveData[AState].Data <> nil Then
      Begin
         RenderTarget.PopLayer;
         SAFE_RELEASE(ID2D1Layer(FSaveData[AState].Data));
         FSaveData[AState].Data := nil;
      End;

      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 TD2CanvasD2D.SetClipRects(Const ARects: Array Of TD2Rect);
Var
   i: Integer;
   Geoms: Array Of ID2D1Geometry;
   FClipGeom: ID2D1GeometryGroup;
   LayerPar: TD2D1_LayerParameters;
   R: TD2Rect;
Begin
   If Length(ARects) > 0 Then
   Begin
      RenderTarget.SetTransform(D2iMatrix(IdentityMatrix));

      For i := 0 To High(ARects) Do
      Begin
         R := ARects[i];
         RenderTarget.PushAxisAlignedClip(D2iRect(R), D2D1_ANTIALIAS_MODE_ALIASED);
         RenderTarget.Clear(D2iColor(0, 0));
         RenderTarget.PopAxisAlignedClip;
      End;

      SAFE_RELEASE(FClipGeom);
      SetLength(Geoms, Length(ARects));

      For i := 0 To High(ARects) Do
      Begin
         Factory.CreateRectangleGeometry(D2iRect(ARects[i]), ID2D1RectangleGeometry(Geoms[i]));
      End;

      Factory.CreateGeometryGroup(D2D1_FILL_MODE_WINDING, @Geoms[0], Length(Geoms), FClipGeom);

      For i := 0 To High(ARects) Do
      Begin
         SAFE_RELEASE(Geoms[i]);
      End;

      RenderTarget.CreateLayer(nil, Layer);
      LayerPar.ContentBounds := D2iRect(d2Rect(0, 0, FWidth, FHeight));
      LayerPar.GeometricMask := FClipGeom;
      LayerPar.MaskAntialiasMode := D2D1_ANTIALIAS_MODE_ALIASED;
      LayerPar.MaskTransform := D2iMatrix(IdentityMatrix);
      LayerPar.Opacity := 1;
      LayerPar.OpacityBrush := nil;
      LayerPar.LayerOptions := D2D1_LAYER_OPTIONS_NONE;
      RenderTarget.PushLayer(LayerPar, Layer);
      SAFE_RELEASE(FClipGeom);
   End;
End;

Procedure TD2CanvasD2D.IntersectClipRect(Const ARect: TD2Rect);
Var
   Geom: ID2D1Geometry;
   LayerPar: TD2D1_LayerParameters;
Begin
   If FLastState < 0 Then
      Exit;
   Factory.CreateRectangleGeometry(D2iRect(ARect), ID2D1RectangleGeometry(Geom));

   If Assigned(Geom) Then
   Begin
      RenderTarget.CreateLayer(nil, ID2D1Layer(FSaveData[FLastState].Data));
      LayerPar.ContentBounds := D2iRect(d2Rect(0, 0, 100000, 400000));
      LayerPar.GeometricMask := Geom;
      LayerPar.MaskAntialiasMode := D2D1_ANTIALIAS_MODE_ALIASED;
      LayerPar.MaskTransform := D2iMatrix(IdentityMatrix);
      LayerPar.Opacity := 1;
      LayerPar.OpacityBrush := nil;
      LayerPar.LayerOptions := D2D1_LAYER_OPTIONS_NONE;
      RenderTarget.PushLayer(LayerPar, ID2D1Layer(FSaveData[FLastState].Data));
      SAFE_RELEASE(Geom);
   End;
End;

Procedure TD2CanvasD2D.ExcludeClipRect(Const ARect: TD2Rect);
Var
   i: Integer;
   Geoms: Array [0..3] Of ID2D1Geometry;
   R: TD2Rect;
   RR: Array [0..3] Of TD2Rect;
   GeomGroup: ID2D1GeometryGroup;
   LayerPar: TD2D1_LayerParameters;
Begin
   If FLastState < 0 Then
      Exit;

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

   For i := 0 To High(RR) Do
   Begin
      Factory.CreateRectangleGeometry(D2iRect(RR[i]), ID2D1RectangleGeometry(Geoms[i]));
   End;

   Factory.CreateGeometryGroup(D2D1_FILL_MODE_WINDING, @Geoms[0], Length(Geoms), GeomGroup);

   For i := 0 To High(RR) Do
   Begin
      SAFE_RELEASE(Geoms[i]);
   End;

   If Assigned(GeomGroup) Then
   Begin
      RenderTarget.CreateLayer(nil, ID2D1Layer(FSaveData[FLastState].Data));
      LayerPar.ContentBounds := D2iRect(d2Rect(0, 0, FWidth, FHeight));
      LayerPar.GeometricMask := GeomGroup;
      LayerPar.MaskAntialiasMode := D2D1_ANTIALIAS_MODE_ALIASED;
      LayerPar.MaskTransform := D2iMatrix(IdentityMatrix);
      LayerPar.Opacity := 1;
      LayerPar.OpacityBrush := nil;
      LayerPar.LayerOptions := D2D1_LAYER_OPTIONS_NONE;
      RenderTarget.PushLayer(LayerPar, ID2D1Layer(FSaveData[FLastState].Data));
      SAFE_RELEASE(GeomGroup);
   End;
End;

Procedure TD2CanvasD2D.ResetClipRect;
Begin
   //....
End;

Procedure TD2CanvasD2D.ApplyFill(ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   Count: Integer;
   B: TD2Bitmap;
   M: TD2Matrix;
   grad: Array [0..100] Of TD2D1_GradientStop;
   gradcol: ID2D1GradientStopCollection;
   gradbrushprop: TD2D1_LinearGradientBrushProperties;
   rgradbrushprop: TD2D1_RadialGradientBrushProperties;
   bitmapbrushprop: TD2D1_BitmapBrushProperties;
   brushprop: TD2D1_BrushProperties;
   d2dbmp: ID2D1Bitmap;
Begin
   SAFE_RELEASE(Brush);

   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
         d2BrushSolid:
         Begin
            RenderTarget.CreateSolidColorBrush(D2iColor(FFill.SolidColor, AOpacity), nil, ID2D1SolidColorBrush(Brush));
         End;

         d2BrushGradient:
         Begin
            If Gradient.Points.Count > 1 Then
            Begin
               Count := 0;
               If Gradient.Points[0].Offset > 0 Then
               Begin
                  grad[Count].Color := D2iColor(d2Opacity(Gradient.Points[0].IntColor, AOpacity), 1);
                  grad[Count].Position := 0;
                  Count := Count + 1;
               End;
               For i := 0 To Gradient.Points.Count - 1 Do
               Begin
                  grad[i + Count].Color := D2iColor(d2Opacity(Gradient.Points[i].IntColor, AOpacity), 1);
                  grad[i + Count].Position := Gradient.Points[i].Offset;
               End;
               If Gradient.Points[Gradient.Points.Count - 1].Offset < 1 Then
               Begin
                  Count := Count + 1;
                  grad[Gradient.Points.Count + Count - 1].Color := D2iColor(d2Opacity(Gradient.Points[Gradient.Points.Count - 1].IntColor, AOpacity), 1);
                  grad[Gradient.Points.Count + Count - 1].Position := 1;
               End;

               If Gradient.Style = d2LinearGradient Then
               Begin
                  RenderTarget.CreateGradientStopCollection(@grad[0], Gradient.Points.Count + Count, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, gradcol);
                  gradbrushprop.StartPoint := D2iPoint(ARect.Left + Gradient.StartPosition.X * ARect.Right, ARect.Top + Gradient.StartPosition.Y * ARect.Bottom);
                  gradbrushprop.EndPoint := D2iPoint(ARect.Left + Gradient.StopPosition.X * ARect.Right, ARect.Top + Gradient.StopPosition.Y * ARect.Bottom);
                  RenderTarget.CreateLinearGradientBrush(gradbrushprop, nil, gradcol, ID2D1LinearGradientBrush(Brush));
                  gradcol := nil;
               End Else Begin
                  For i := 0 To Gradient.Points.Count + Count - 1 Do
                     grad[i].Position := 1 - grad[i].Position;

                  RenderTarget.CreateGradientStopCollection(@grad[0], Gradient.Points.Count + Count, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, gradcol);
                  rgradbrushprop.GradientOriginOffset := TD2D1_Point2F(d2Point(0, 0));
                  rgradbrushprop.Center := TD2D1_Point2F(d2Point(Gradient.RadialTransform.RotateCenter.X * d2RectWidth(ARect), Gradient.RadialTransform.RotateCenter.Y * d2RectHeight(ARect)));
                  rgradbrushprop.RadiusX := d2RectWidth(ARect) / 2;
                  rgradbrushprop.RadiusY := d2RectHeight(ARect) / 2;
                  RenderTarget.CreateRadialGradientBrush(rgradbrushprop, nil, gradcol, ID2D1RadialGradientBrush(Brush));
                  gradcol := nil;
               End;
            End Else
               RenderTarget.CreateSolidColorBrush(D2iColor(0, 0), nil, ID2D1SolidColorBrush(Brush));
         End;

         d2BrushResource:
         Begin
         End;

         d2BrushVisual:
         Begin
         End;

         d2BrushBitmap:
         Begin
            B := Bitmap.Bitmap;
            If (B <> nil) And (B.ResourceBitmap <> nil) Then
               B := B.ResourceBitmap;
            If (B <> nil) And (B.Width > 0) And (B.Height > 0) Then
            Begin
               bitmapbrushprop.InterpolationMode := D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
               brushprop.Opacity := AOpacity;
               M := IdentityMatrix;
               Case Bitmap.WrapMode Of
                  d2WrapTile:
                  Begin
                     bitmapbrushprop.ExtendModeX := D2D1_EXTEND_MODE_WRAP;
                     bitmapbrushprop.ExtendModeY := D2D1_EXTEND_MODE_WRAP;
                  End;
                  d2WrapTileOriginal:
                  Begin
                     bitmapbrushprop.ExtendModeX := D2D1_EXTEND_MODE_CLAMP;
                     bitmapbrushprop.ExtendModeY := D2D1_EXTEND_MODE_CLAMP;
                  End;
                  d2WrapTileStretch:
                  Begin
                     bitmapbrushprop.ExtendModeX := D2D1_EXTEND_MODE_WRAP;
                     bitmapbrushprop.ExtendModeY := D2D1_EXTEND_MODE_WRAP;
                     M.m11 := (d2RectWidth(ARect) + (StrokeThickness / 2)) / B.Width;
                     M.m22 := (d2RectHeight(ARect) + (StrokeThickness / 2)) / B.Height;
                  End;
               End;
               brushprop.Transform := D2iMatrix(M);
               Res := RenderTarget.CreateBitmap(D2iSize(B.Width, B.Height), B.StartLine, B.Width * 4, BitmapProp(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), d2dbmp);
               RenderTarget.CreateBitmapBrush(
                  d2dbmp, @bitmapbrushprop, @brushprop,
                  ID2D1BitmapBrush(Brush));
            End Else
               RenderTarget.CreateSolidColorBrush(D2iColor(0, 0), nil, ID2D1SolidColorBrush(Brush));
         End;

         Else RenderTarget.CreateSolidColorBrush(D2iColor(0, 0), nil, ID2D1SolidColorBrush(Brush));
      End;
   End;
End;

Procedure TD2CanvasD2D.ApplyStroke(ARect: TD2Rect; Const AOpacity: Single);
Var
   StyleProp: TD2D1_StrokeStyleProperties;
Begin
   SAFE_RELEASE(StrokeBrush);
   SAFE_RELEASE(StrokeStyle);

   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, d2BrushGradient, d2BrushBitmap:
         Begin
            RenderTarget.CreateSolidColorBrush(D2iColor(FStroke.SolidColor, AOpacity), nil, ID2D1SolidColorBrush(StrokeBrush));
         End;

         Else RenderTarget.CreateSolidColorBrush(D2iColor(0, 0), nil, ID2D1SolidColorBrush(StrokeBrush));
      End;
   End;

   Case StrokeCap Of
      d2CapFlat: StyleProp.DashCap := D2D1_CAP_STYLE_SQUARE;
      d2CapRound: StyleProp.DashCap := D2D1_CAP_STYLE_ROUND;
   End;

   StyleProp.StartCap := StyleProp.DashCap;
   StyleProp.EndCap := StyleProp.DashCap;

   Case StrokeJoin Of
      d2JoinMiter: StyleProp.LineJoin := D2D1_LINE_JOIN_MITER;
      d2JoinRound: StyleProp.LineJoin := D2D1_LINE_JOIN_ROUND;
      d2JoinBevel: StyleProp.LineJoin := D2D1_LINE_JOIN_BEVEL;
   End;

   StyleProp.MiterLimit := 10;
   StyleProp.DashOffset := FDashOffset;
   StyleProp.DashStyle := TD2D1_DashStyle(StrokeDash);

   If StrokeDash = d2DashCustom Then
      Factory.CreateStrokeStyle(StyleProp, @FDash[0], Length(FDash), StrokeStyle)
   Else
      Factory.CreateStrokeStyle(StyleProp, nil, 0, StrokeStyle);
End;

Procedure TD2CanvasD2D.FontChanged(Sender: TObject);
Begin
   //....
End;

Procedure TD2CanvasD2D.DrawLine(Const APt1, APt2: TD2Point; Const AOpacity: Single);
Begin
   If FStroke.Style <> d2BrushNone Then
   Begin
      ApplyStroke(d2Rect(APt1.X, APt1.Y, APt2.X, APt2.Y), AOpacity);
      RenderTarget.DrawLine(D2iPoint(APt1.X, APt1.Y), D2iPoint(APt2.X, APt2.Y), StrokeBrush, StrokeThickness, StrokeStyle);
   End;
End;

Procedure TD2CanvasD2D.IntFillRect(R: TD2Rect; Opacity: Single);
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      If FFill.Style = d2BrushVisual Then
      Begin
      End Else Begin
         ApplyFill(R, Opacity);
         RenderTarget.FillRectangle(D2iRect(R), Brush);
      End;
   End;
End;

Procedure TD2CanvasD2D.IntFillPath(P: ID2D1Geometry; R: TD2Rect; Opacity: Single);
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      If FFill.Style = d2BrushVisual Then
      Begin
      End Else Begin
         ApplyFill(R, Opacity);
         RenderTarget.FillGeometry(P, Brush);
      End;
   End;
End;

Procedure TD2CanvasD2D.DrawRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   Geometry: ID2D1PathGeometry;
   Path: ID2D1GeometrySink;
   x1, x2, y1, y2: Single;
   R: TD2Rect;
Begin
   If FStroke.Style <> d2BrushNone Then
   Begin
      R := ARect;
      ApplyStroke(R, AOpacity);

      If (xRadius < Epsilon) And (yRadius < Epsilon) Then
      Begin
         RenderTarget.DrawRectangle(D2iRect(ARect), StrokeBrush, StrokeThickness, StrokeStyle);
      End Else Begin
         x1 := xRadius;

         If d2RectWidth(R) - (x1 * 2) < 0 Then
            x1 := (xRadius * (d2RectWidth(R) / (x1 * 2)));

         x2 := x1 * CurveKappaInv;
         y1 := yRadius;

         If d2RectHeight(R) - (y1 * 2) < 0 Then
            y1 := (yRadius * (d2RectHeight(R) / (y1 * 2)));

         y2 := y1 * CurveKappaInv;
         Factory.CreatePathGeometry(Geometry);
         Geometry.Open(Path);
         Path.BeginFigure(D2iPoint(R.Left, R.Top + y1), D2D1_FIGURE_BEGIN_FILLED);

         If d2CornerTopLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Left + x1, R.Top));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Left + x2, R.Top + y1, R.Left + x1, R.Top + y2, R.Left + x1, R.Top));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Left + x2, R.Top + y1));
                  Path.AddLine(D2iPoint(R.Left + x1, R.Top + y2));
                  Path.AddLine(D2iPoint(R.Left + x1, R.Top));
               End;
               Else Path.AddBezier(D2iBezier(R.Left, R.Top + (y2), R.Left + x2, R.Top, R.Left + x1, R.Top))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Left, R.Top));
            Path.AddLine(D2iPoint(R.Left + x1, R.Top));
         End;

         Path.AddLine(D2iPoint(R.Right - x1, R.Top));

         If d2CornerTopRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Right, R.Top + y1));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Right - x1, R.Top + y2, R.Right - x2, R.Top + y1, R.Right, R.Top + y1));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Right - x1, R.Top + y2));
                  Path.AddLine(D2iPoint(R.Right - x2, R.Top + y1));
                  Path.AddLine(D2iPoint(R.Right, R.Top + y1));
               End;
               Else Path.AddBezier(D2iBezier(R.Right - x2, R.Top, R.Right, R.Top + (y2), R.Right, R.Top + y1))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Right, R.Top));
            Path.AddLine(D2iPoint(R.Right, R.Top + y1));
         End;

         Path.AddLine(D2iPoint(R.Right, R.Bottom - y1));

         If d2CornerBottomRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Right - x2, R.Bottom - y1, R.Right - x1, R.Bottom - y2, R.Right - x1, R.Bottom));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Right - x2, R.Bottom - y1));
                  Path.AddLine(D2iPoint(R.Right - x1, R.Bottom - y2));
                  Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
               End;
               Else Path.AddBezier(D2iBezier(R.Right, R.Bottom - (y2), R.Right - x2, R.Bottom, R.Right - x1, R.Bottom))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Right, R.Bottom));
            Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
         End;

         Path.AddLine(D2iPoint(R.Left + x1, R.Bottom));

         If d2CornerBottomLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Left + x1, R.Bottom - y2, R.Left + x2, R.Bottom - y1, R.Left, R.Bottom - y1));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Left + x1, R.Bottom - y2));
                  Path.AddLine(D2iPoint(R.Left + x2, R.Bottom - y1));
                  Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
               End;
               Else Path.AddBezier(D2iBezier(R.Left + x2, R.Bottom, R.Left, R.Bottom - (y2), R.Left, R.Bottom - y1))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Left, R.Bottom));
            Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
         End;

         Path.EndFigure(D2D1_FIGURE_END_CLOSED);
         Path.Close;
         SAFE_RELEASE(Path);
         RenderTarget.DrawGeometry(Geometry, StrokeBrush, StrokeThickness, StrokeStyle);
         SAFE_RELEASE(Geometry);
      End;
   End;
End;

Procedure TD2CanvasD2D.FillRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   Geometry: ID2D1PathGeometry;
   Path: ID2D1GeometrySink;
   x1, x2, y1, y2: Single;
   R: TD2Rect;
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      If ((xRadius = 0) And (yRadius = 0)) Or (ACorners = []) Then
      Begin
         IntFillRect(ARect, AOpacity);
      End Else Begin
         R := ARect;
         x1 := xRadius;

         If d2RectWidth(R) - (x1 * 2) < 0 Then
            x1 := (xRadius * (d2RectWidth(R) / (x1 * 2)));

         x2 := x1 * CurveKappaInv;
         y1 := yRadius;

         If d2RectHeight(R) - (y1 * 2) < 0 Then
            y1 := (yRadius * (d2RectHeight(R) / (y1 * 2)));

         y2 := y1 * CurveKappaInv;
         Factory.CreatePathGeometry(Geometry);
         Geometry.Open(Path);
         Path.BeginFigure(D2iPoint(R.Left, R.Top + y1), D2D1_FIGURE_BEGIN_FILLED);

         If d2CornerTopLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Left + x1, R.Top));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Left + x2, R.Top + y1, R.Left + x1, R.Top + y2, R.Left + x1, R.Top));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Left + x2, R.Top + y1));
                  Path.AddLine(D2iPoint(R.Left + x1, R.Top + y2));
                  Path.AddLine(D2iPoint(R.Left + x1, R.Top));
               End;
               Else Path.AddBezier(D2iBezier(R.Left, R.Top + (y2), R.Left + x2, R.Top, R.Left + x1, R.Top))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Left, R.Top));
            Path.AddLine(D2iPoint(R.Left + x1, R.Top));
         End;
         Path.AddLine(D2iPoint(R.Right - x1, R.Top));
         If d2CornerTopRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Right, R.Top + y1));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Right - x1, R.Top + y2, R.Right - x2, R.Top + y1, R.Right, R.Top + y1));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Right - x1, R.Top + y2));
                  Path.AddLine(D2iPoint(R.Right - x2, R.Top + y1));
                  Path.AddLine(D2iPoint(R.Right, R.Top + y1));
               End;
               Else Path.AddBezier(D2iBezier(R.Right - x2, R.Top, R.Right, R.Top + (y2), R.Right, R.Top + y1))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Right, R.Top));
            Path.AddLine(D2iPoint(R.Right, R.Top + y1));
         End;
         Path.AddLine(D2iPoint(R.Right, R.Bottom - y1));
         If d2CornerBottomRight In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Right - x2, R.Bottom - y1, R.Right - x1, R.Bottom - y2, R.Right - x1, R.Bottom));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Right - x2, R.Bottom - y1));
                  Path.AddLine(D2iPoint(R.Right - x1, R.Bottom - y2));
                  Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
               End;
               Else Path.AddBezier(D2iBezier(R.Right, R.Bottom - (y2), R.Right - x2, R.Bottom, R.Right - x1, R.Bottom))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Right, R.Bottom));
            Path.AddLine(D2iPoint(R.Right - x1, R.Bottom));
         End;
         Path.AddLine(D2iPoint(R.Left + x1, R.Bottom));
         If d2CornerBottomLeft In ACorners Then
         Begin
            Case ACornerType Of
               d2CornerBevel: Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
               d2CornerInnerRound: Path.AddBezier(D2iBezier(R.Left + x1, R.Bottom - y2, R.Left + x2, R.Bottom - y1, R.Left, R.Bottom - y1));
               d2CornerInnerLine:
               Begin
                  Path.AddLine(D2iPoint(R.Left + x1, R.Bottom - y2));
                  Path.AddLine(D2iPoint(R.Left + x2, R.Bottom - y1));
                  Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
               End;
               Else Path.AddBezier(D2iBezier(R.Left + x2, R.Bottom, R.Left, R.Bottom - (y2), R.Left, R.Bottom - y1))
            End;
         End Else Begin
            Path.AddLine(D2iPoint(R.Left, R.Bottom));
            Path.AddLine(D2iPoint(R.Left, R.Bottom - y1));
         End;

         Path.EndFigure(D2D1_FIGURE_END_CLOSED);
         Path.Close;
         SAFE_RELEASE(Path);
         IntFillPath(Geometry, ARect, AOpacity);
         SAFE_RELEASE(Geometry);
      End;
   End;
End;

Procedure TD2CanvasD2D.DrawEllipse(Const ARect: TD2Rect; Const AOpacity: Single);
Var
   R: TD2Rect;
Begin
   If FStroke.Style <> d2BrushNone Then
   Begin
      ApplyStroke(ARect, AOpacity);
      RenderTarget.DrawEllipse(D2iEllipse(ARect), StrokeBrush, StrokeThickness, StrokeStyle);
   End;
End;

Procedure TD2CanvasD2D.FillEllipse(Const ARect: TD2Rect; Const AOpacity: Single);
Var
   Geometry: ID2D1EllipseGeometry;
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      If FFill.Style <> d2BrushVisual Then
      Begin
         ApplyFill(ARect, AOpacity);
         RenderTarget.FillEllipse(D2iEllipse(ARect), Brush);
      End Else Begin
         Factory.CreateEllipseGeometry(D2iEllipse(ARect), Geometry);
         IntFillPath(Geometry, ARect, AOpacity);
         SAFE_RELEASE(Geometry);
      End;
   End;
End;

Procedure TD2CanvasD2D.FillText(Const ARect, AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const AOpacity: Single; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter);
Var
   TextRange: TDWrite_TextRange;
   TextLayout: IDWriteTextLayout;
   TextFormat: IDWriteTextFormat;
   R: TD2Rect;
   S: TD2FontStyle;
   WS: WideString;
Begin
   If (FFill.Style <> d2BrushNone) And (AText <> '') Then
   Begin
      WS := FFont.Family;
      DWriteFactory.CreateTextFormat(Pwidechar(WS), nil, D2iFontWeight(FFont.Style),
         D2iFontStyle(FFont.Style), DWRITE_FONT_STRETCH_NORMAL, FFont.Size, 'en-us', TextFormat);

      DWriteFactory.CreateTextLayout(Pwidechar(AText), Length(AText), TextFormat, d2RectWidth(ARect), d2RectHeight(ARect), TextLayout);

      TextRange.startPosition := 0;
      TextRange.length := Length(AText);

      If Not WordWrap Then
         TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP)
      Else
         TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);

      If FFont.Style = d2FontStrikeout Then
         TextLayout.SetStrikethrough(True, TextRange);

      If FFont.Style = d2FontUnderline Then
         TextLayout.SetUnderline(True, TextRange);


      Case AVTextAlign Of
         d2TextAlignCenter: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
         d2TextAlignNear: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
         d2TextAlignFar: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
      End;

      Case ATextAlign Of
         d2TextAlignCenter: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
         d2TextAlignNear: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
         d2TextAlignFar: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
      End;

      ApplyFill(ARect, AOpacity);

      RenderTarget.DrawTextLayout(D2iPoint(ARect.Left, ARect.Top), TextLayout, Brush, D2D1_DRAW_TEXT_OPTIONS_CLIP);
      SAFE_RELEASE(TextFormat);
      SAFE_RELEASE(TextLayout);
   End;
End;

Procedure TD2CanvasD2D.MeasureText(Var ARect: TD2Rect; AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign, AVTextAlign: TD2TextAlign);
Var
   TextRange: TDWrite_TextRange;
   TextLayout: IDWriteTextLayout;
   TextMetrics: TDWrite_TextMetrics;
   TextFormat: IDWriteTextFormat;
   WS: WideString;
Begin
   If Not WordWrap Then
      ARect.Right := ARect.Left;
   If Length(AText) = 0 Then
      Exit;

   WS := FFont.Family;
   DWriteFactory.CreateTextFormat(Pwidechar(WS), nil, D2iFontWeight(FFont.Style),
      D2iFontStyle(FFont.Style), DWRITE_FONT_STRETCH_NORMAL, FFont.Size, 'en-us', TextFormat);

   DWriteFactory.CreateTextLayout(Pwidechar(AText), Length(AText), TextFormat, d2RectWidth(ARect), d2RectHeight(ARect), TextLayout);

   TextRange.startPosition := 0;
   TextRange.length := Length(AText);

   If Not WordWrap Then
      TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP)
   Else
      TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);

   If FFont.Style = d2FontStrikeout Then
      TextLayout.SetStrikethrough(True, TextRange);

   If FFont.Style = d2FontUnderline Then
      TextLayout.SetUnderline(True, TextRange);

   Case AVTextAlign Of
      d2TextAlignCenter: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
      d2TextAlignNear: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
      d2TextAlignFar: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
   End;

   Case ATextAlign Of
      d2TextAlignCenter: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
      d2TextAlignNear: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
      d2TextAlignFar: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
   End;

   TextLayout.GetMetrics(TextMetrics);
   ARect := d2Rect(ARect.Left + TextMetrics.Left, ARect.Top + TextMetrics.Top, ARect.Left + TextMetrics.Left + TextMetrics.widthIncludingTrailingWhitespace,
      ARect.Top + TextMetrics.Top + TextMetrics.Height);

   SAFE_RELEASE(TextFormat);
   SAFE_RELEASE(TextLayout);
End;

Procedure TD2CanvasD2D.DrawThumbnail(Const ABitmap: TD2Bitmap; Const aWidth, aHeight: Single);
Var
   xscale: Single;
Begin
   //  UpdateBitmap(ABitmap);
   If ABitmap.Handle = 0 Then
      Exit;
   xscale := aWidth / ABitmap.Width;
   If FBitmap <> nil Then
   Begin
      //    Fgraphics.ScaleTransform(xscale,xscale);
      //    RenderTarget.DrawBitmap(ID2D1Bitmap(ABitmap.Handle),@DR,AOpacity,IntMode,@SR);
   End;
End;

Procedure TD2CanvasD2D.DrawBitmap(Const ABitmap: TD2Bitmap; Const SrcRect, DstRect: TD2Rect; Const AOpacity: Single; Const HighSpeed: Boolean = False);
Var
   SR, DR: TD2D1_RectF;
   IntMode: TD2D1_BitmapInterpolationMode;
   B: ID2D1Bitmap;
Begin
   If HighSpeed Then
      IntMode := D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
   Else
      IntMode := D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;

   SR := D2iRect(SrcRect);
   DR := D2iRect(DstRect);

   Res := RenderTarget.CreateBitmap(D2iSize(ABitmap.Width, ABitmap.Height), ABitmap.StartLine, ABitmap.Width * 4, BitmapProp(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), B);
   RenderTarget.DrawBitmap(B, @DR, AOpacity, IntMode, @SR);
End;

Procedure TD2CanvasD2D.DrawPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   B: TD2Rect;
   W, H, NewW, NewH: Single;
   CP1, CP2: TD2Point;
   Geometry: ID2D1PathGeometry;
   Path: ID2D1GeometrySink;
   Closed: Boolean;
Begin
   If FStroke.Style = d2BrushNone Then
      Exit;
   If APath.IsEmpty Then
      Exit;

   ApplyStroke(ARect, AOpacity);

   B := APath.GetBounds;
   W := d2RectWidth(B);
   H := d2RectHeight(B);
   NewW := d2RectWidth(ARect);
   NewH := d2RectHeight(ARect);

   Factory.CreatePathGeometry(Geometry);
   Geometry.Open(Path);
   i := 0;
   Closed := False;
   While i < Length(APath.PathData) Do
   Begin
      Case APath.PathData[i].Kind Of
         d2PathPointMoveTo:
         Begin
            If (i > 0) And (APath.PathData[i - 1].Kind <> d2PathPointClose) Then
               Path.EndFigure(D2D1_FIGURE_END_OPEN);
            Path.BeginFigure(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH), D2D1_FIGURE_BEGIN_FILLED);
         End;
         d2PathPointLineTo:
         Begin
            Path.AddLine(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH));
         End;
         d2PathPointCurveTo:
         Begin
            CP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
            Inc(i);
            CP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
            Inc(i);
            Path.AddBezier(D2iBezier(CP1.X, CP1.Y, CP2.X, CP2.Y, ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH));
         End;
         d2PathPointClose:
         Begin
            Path.EndFigure(D2D1_FIGURE_END_CLOSED);
            Closed := True;
         End;
      End;
      Inc(i);
   End;

   If Not Closed Then
      Path.EndFigure(D2D1_FIGURE_END_OPEN);

   Res := Path.Close;
   SAFE_RELEASE(Path);
   RenderTarget.DrawGeometry(Geometry, StrokeBrush, StrokeThickness, StrokeStyle);
   SAFE_RELEASE(Geometry);
End;

Procedure TD2CanvasD2D.FillPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single);
Var
   i: Integer;
   B: TD2Rect;
   W, H, NewW, NewH: Single;
   CP1, CP2: TD2Point;
   Geometry: ID2D1PathGeometry;
   Path: ID2D1GeometrySink;
   Closed: Boolean;
Begin
   //exit;
   If FFill.Style = d2BrushNone Then
      Exit;
   If APath.IsEmpty Then
      Exit;

   B := APath.GetBounds;
   W := d2RectWidth(B);
   H := d2RectHeight(B);
   NewW := d2RectWidth(ARect);
   NewH := d2RectHeight(ARect);

   Factory.CreatePathGeometry(Geometry);
   Geometry.Open(Path);
   i := 0;
   Closed := False;
   While i < Length(APath.PathData) Do
   Begin
      Case APath.PathData[i].Kind Of
         d2PathPointMoveTo:
         Begin
            If (i > 0) And (APath.PathData[i - 1].Kind <> d2PathPointClose) Then
               Path.EndFigure(D2D1_FIGURE_END_OPEN);
            Path.BeginFigure(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH), D2D1_FIGURE_BEGIN_FILLED);
         End;
         d2PathPointLineTo:
         Begin
            Path.AddLine(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH));
         End;
         d2PathPointCurveTo:
         Begin
            CP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
            Inc(i);
            CP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
            Inc(i);
            Path.AddBezier(D2iBezier(CP1.X, CP1.Y, CP2.X, CP2.Y, ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH));
         End;
         d2PathPointClose:
         Begin
            Path.EndFigure(D2D1_FIGURE_END_CLOSED);
            Closed := True;
         End;
      End;
      Inc(i);
   End;
   If Not Closed Then
      Path.EndFigure(D2D1_FIGURE_END_OPEN);

   Path.Close;
   SAFE_RELEASE(Path);
   IntFillPath(Geometry, ARect, AOpacity);
   SAFE_RELEASE(Geometry);
End;

Function TD2CanvasD2D.PtInPath(Const APoint: TD2Point; Const ARect: TD2Rect; Const APath: TD2PathData): Boolean;
Var
   i: Integer;
   B: TD2Rect;
   W, H, NewW, NewH: Single;
   CP1, CP2: TD2Point;
   Geometry: ID2D1PathGeometry;
   Path: ID2D1GeometrySink;
   Cont: Longbool;
   Closed: Boolean;
Begin
   Result := False;
   If Not d2PtInRect(APoint, ARect) Then
      Result := False
   Else Begin
      If APath.IsEmpty Then
         Exit;
      B := APath.GetBounds;
      W := d2RectWidth(B);
      H := d2RectHeight(B);
      NewW := d2RectWidth(ARect);
      NewH := d2RectHeight(ARect);

      Factory.CreatePathGeometry(Geometry);
      Geometry.Open(Path);
      i := 0;
      Closed := False;
      While i < Length(APath.PathData) Do
      Begin
         Case APath.PathData[i].Kind Of
            d2PathPointMoveTo:
            Begin
               Path.BeginFigure(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH), D2D1_FIGURE_BEGIN_FILLED);
            End;
            d2PathPointLineTo:
            Begin
               Path.AddLine(D2iPoint(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH));
            End;
            d2PathPointCurveTo:
            Begin
               CP1 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
               Inc(i);
               CP2 := d2Point(ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top + (APath.PathData[i].Point.Y - B.Top) / H * NewH);
               Inc(i);
               Path.AddBezier(D2iBezier(CP1.X, CP1.Y, CP2.X, CP2.Y, ARect.Left + (APath.PathData[i].Point.X - B.Left) / W * NewW, ARect.Top +
                  (APath.PathData[i].Point.Y - B.Top) / H * NewH));
            End;
            d2PathPointClose:
            Begin
               Path.EndFigure(D2D1_FIGURE_END_CLOSED);
               Closed := True;
            End;
         End;
         Inc(i);
      End;
      If Not Closed Then
         Path.EndFigure(D2D1_FIGURE_END_OPEN);

      Path.Close;
      SAFE_RELEASE(Path);
      Geometry.FillContainsPoint(D2iPoint(APoint.X, APoint.Y), nil, 1, Cont);
      SAFE_RELEASE(Geometry);

      Result := Cont;
   End;
End;


//============= TD2Sink ==============================

Type
   TD2Sink = Class(TInterfacedPersistent, ID2D1SimplifiedGeometrySink)
   Private
      Path: TD2PathData;
      Procedure SetSegmentFlags(VertexFlags: TD2D1_PathSegment); Stdcall;
      Procedure SetFillMode(FillMode: TD2D1_FillMode); Stdcall;
      Procedure AddLines(Points: PTD2D1_Point2F; PointsCount: Longword); Stdcall;
      Procedure AddBeziers(Beziers: PTD2D1_BezierSegment; BeziersCount: Longword); Stdcall;
      Procedure BeginFigure(StartPoint: TD2D1_Point2F; FigureBegin: TD2D1_FigureBegin); Stdcall;
      Procedure EndFigure(FigureEnd: TD2D1_FigureEnd); Stdcall;
      Function Close: HResult; Stdcall;
   End;


Procedure TD2Sink.SetSegmentFlags(VertexFlags: TD2D1_PathSegment);
Begin
   //.............
End;

Procedure TD2Sink.SetFillMode(FillMode: TD2D1_FillMode); Stdcall;
Begin
   //.............
End;

Procedure TD2Sink.AddBeziers(Beziers: PTD2D1_BezierSegment; BeziersCount: Longword); Stdcall;
Var
   i: Integer;
Begin
   For i := 0 To BeziersCount - 1 Do
   Begin
      Path.CurveTo(TD2Point(Beziers.Point1), TD2Point(Beziers.Point2), TD2Point(Beziers.Point3));
      Inc(Beziers);
   End;
End;

Procedure TD2Sink.AddLines(Points: PTD2D1_Point2F; PointsCount: Longword); Stdcall;
Var
   i: Integer;
Begin
   For i := 0 To PointsCount - 1 Do
   Begin
      Path.LineTo(TD2Point(Points^));
      Inc(Points);
   End;
End;

Procedure TD2Sink.BeginFigure(StartPoint: TD2D1_Point2F; FigureBegin: TD2D1_FigureBegin); Stdcall;
Begin
   Path.MoveTo(TD2Point(StartPoint));
End;

Procedure TD2Sink.EndFigure(FigureEnd: TD2D1_FigureEnd); Stdcall;
Begin
   Path.ClosePath;
End;

Function TD2Sink.Close: HResult; Stdcall;
Begin
   Result := S_OK;
End;


//============== TD2TextRendering ================================================
Type

   TD2TextRendering = Class(TInterfacedPersistent, IDWriteTextRenderer)
   Private
      Canvas: TD2CanvasD2D;
      Function IsPixelSnappingDisabled(ClientDrawingContext: Pointer; out IsDisabled: Longbool): HResult; Stdcall;
      Function GetCurrentTransform(ClientDrawingContext: Pointer; out Transform: TDWrite_Matrix): HResult; Stdcall;
      Function GetPixelsPerDip(ClientDrawingContext: Pointer; out PixelsPerDip: Single): HResult; Stdcall;
      Function DrawGlyphRun(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; MeasuringMode: TDWrite_MeasuringMode; Const GlyphRun: TDWrite_GlyphRun;
         Const GlyphRunDescription: TDWrite_GlyphRunDescription; ClientDrawingEffect: IUnknown): HResult; Stdcall;
      Function DrawUnderline(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; Const Underline: TDWrite_Underline; ClientDrawingEffect: IUnknown): HResult; Stdcall;
      Function DrawStrikethrough(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; Const Strikethrough: TDWrite_Strikethrough; ClientDrawingEffect: IUnknown): HResult; Stdcall;
      Function DrawInlineObject(ClientDrawingContext: Pointer; OriginX: Single; OriginY: Single; InlineObject: IDWriteInlineObject; IsSideways: Longbool; IsRightToLeft: Longbool; ClientDrawingEffect: IUnknown): HResult; Stdcall;
   End;


Function TD2TextRendering.IsPixelSnappingDisabled(ClientDrawingContext: Pointer; out IsDisabled: Longbool): HResult; Stdcall;
Begin
   IsDisabled := True;
   Result := S_OK;
End;

Function TD2TextRendering.GetPixelsPerDip(ClientDrawingContext: Pointer; out PixelsPerDip: Single): HResult;
Begin
   PixelsPerDip := 1;
   Result := S_OK;
End;

Function TD2TextRendering.GetCurrentTransform(ClientDrawingContext: Pointer; out Transform: TDWrite_Matrix): HResult; Stdcall;
Begin
   Transform := TDWrite_Matrix(D2iMatrix(IdentityMatrix));
   Result := S_OK;
End;

Function TD2TextRendering.DrawGlyphRun(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; MeasuringMode: TDWrite_MeasuringMode;
   Const GlyphRun: TDWrite_GlyphRun; Const GlyphRunDescription: TDWrite_GlyphRunDescription; ClientDrawingEffect: IUnknown): HResult; Stdcall;
Var
   pPathGeometry: ID2D1PathGeometry;
   pSink: ID2D1GeometrySink;
   pSimSink: TD2Sink;
Begin
   Res := Factory.CreatePathGeometry(pPathGeometry);
   If (SUCCEEDED(Res)) Then
      Res := pPathGeometry.Open(pSink);
   If (SUCCEEDED(Res)) Then
   Begin
      Res := glyphRun.fontFace.GetGlyphRunOutline(glyphRun.fontEmSize, glyphRun.glyphIndices, glyphRun.glyphAdvances, glyphRun.glyphOffsets, glyphRun.glyphCount,
         glyphRun.isSideways, False {glyphRun.bidiLevel}, pSink);
   End;
   If (SUCCEEDED(Res)) Then
      Res := pSink.Close();

   pSimSink := TD2Sink.Create;
   pSimSink.Path := TD2PathData(ClientDrawingContext);
   Res := pPathGeometry.Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, nil, 0, pSimSink);
   pSimSink.Close;
   pSimSink.Path.Offset(BaselineOriginX, BaselineOriginY);
   pSimSink.Free;

   SAFE_RELEASE(pSink);
   SAFE_RELEASE(pPathGeometry);
End;

Function TD2TextRendering.DrawInlineObject(ClientDrawingContext: Pointer; OriginX: Single; OriginY: Single; InlineObject: IDWriteInlineObject; IsSideways: Longbool;
   IsRightToLeft: Longbool; ClientDrawingEffect: IUnknown): HResult; Stdcall;
Begin
   Result := S_OK;
End;

Function TD2TextRendering.DrawStrikethrough(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; Const Strikethrough: TDWrite_Strikethrough; ClientDrawingEffect: IUnknown): HResult; Stdcall;
Begin
   Result := S_OK;
End;

Function TD2TextRendering.DrawUnderline(ClientDrawingContext: Pointer; BaselineOriginX: Single; BaselineOriginY: Single; Const Underline: TDWrite_Underline; ClientDrawingEffect: IUnknown): HResult; Stdcall;
Begin
   Result := S_OK;
End;


//------------ TD2CanvasD2D ----------------------

Function TD2CanvasD2D.TextToPath(Path: TD2PathData; Const ARect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign, AVTextAlign: TD2TextAlign): Boolean;
Var
   TextRange: TDWrite_TextRange;
   TextLayout: IDWriteTextLayout;
   TextFormat: IDWriteTextFormat;
   WS: WideString;
   MyRenderer: TD2TextRendering;
Begin
   Result := False;
   If (AText <> '') Then
   Begin
      Path.Clear;

      WS := FFont.Family;
      DWriteFactory.CreateTextFormat(Pwidechar(WS), nil, D2iFontWeight(FFont.Style),
         D2iFontStyle(FFont.Style), DWRITE_FONT_STRETCH_NORMAL, FFont.Size, 'en-us', TextFormat);

      DWriteFactory.CreateTextLayout(Pwidechar(AText), Length(AText),
         TextFormat, d2RectWidth(ARect), d2RectHeight(ARect), TextLayout);

      TextRange.startPosition := 0;
      TextRange.length := Length(AText);

      If Not WordWrap Then
         TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP)
      Else
         TextLayout.SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);

      If FFont.Style = d2FontStrikeout Then
         TextLayout.SetStrikethrough(True, TextRange);

      If FFont.Style = d2FontUnderline Then
         TextLayout.SetUnderline(True, TextRange);

      Case AVTextAlign Of
         d2TextAlignCenter: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
         d2TextAlignNear: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
         d2TextAlignFar: TextLayout.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
      End;

      Case ATextAlign Of
         d2TextAlignCenter: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
         d2TextAlignNear: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
         d2TextAlignFar: TextLayout.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
      End;

      MyRenderer := TD2TextRendering.Create;
      MyRenderer.Canvas := Self;
      TextLayout.Draw(Path, MyRenderer, 0, 0);
      MyRenderer.Free;

      SAFE_RELEASE(TextFormat);
      SAFE_RELEASE(TextLayout);
   End;
End;

