{**********************************************}
{   TeeChart and TeeTree Direct2D Canvas       }
{                                              }
{   Copyright (c) 2010-2020 by Steema Software }
{        All Rights Reserved                   }
{**********************************************}
unit TeeD2Canvas;
{$I TeeDefs.inc}

interface

uses
  Windows, Classes, Graphics, Types,
  {$IFDEF D17}
  System.UITypes,
  {$ENDIF}
  TeCanvas, Direct2D;

type
  TTeeD2Canvas = class(TTeeCanvas3D)
  private
    FAlign: TCanvasTextAlign;
    FAnti : Boolean;

    FBackColor : TColor;
    FBackMode: TCanvasBackMode;
    FInterChar : Integer;

    ICanvas: TDirect2DCanvas;
    IHeight: Integer;
    IWidth: Integer;
    IOldDC: TTeeCanvasHandle;

  protected
    function CheckFastBrush:Boolean; override;

    procedure DoChangedBrush(const ABrush:TTeeBrush); override;
    procedure DoChangedFont; override;
    procedure DoChangedPen; override;

    procedure FastLineTo(const X,Y:Integer); override;

    function GetBackColor:TColor; override;
    function GetBackMode:TCanvasBackMode; override;
    Function GetTextAlign:TCanvasTextAlign; override;

    Function GetSupportsXORMode:Boolean; override;

    function IsGradientVisible:Boolean; // re-use !!!

    procedure SetAntiAlias(const Value:Boolean); override;
    procedure SetBackColor(const Value:TColor); override;
    procedure SetBackMode(const Value:TCanvasBackMode); override;
    procedure SetFastLine(const Value:Boolean); override;
    procedure SetInterCharSize(ASize:Integer); override;
    procedure SetPixel(X, Y: Integer; Value: TColor); override;
    procedure SetPixel3D(X, Y, Z: Integer; Value: TColor); override;
    procedure SetTextAlign(const Value:TCanvasTextAlign); override;
    Procedure SetUseBuffer(Value:Boolean); override;

    procedure SmoothShadow(const Shadow:TTeeShadow; const Rect:TRect;
                           Ellipse:Boolean; RoundSize:Integer;
                           const P:Array of TPoint;
                           DonutPercent:Integer=0;
                           const StartAngle:Double=0;
                           const EndAngle:Double=360); override;

  public
    Constructor Create; override;
    Destructor Destroy; override;

    class function Description:String; override;
    class function EditorClass:String; override;

    Function InitWindow(const DestCanvas: TCanvas; const A3DOptions: TView3DOptions;
      ABackColor: TColor; Is3D: Boolean; Const UserRect: TRect): TRect;
      override;

    function BeginBlending(const R: TRect; Transparency: TTeeTransparency):TTeeBlend; override;
    procedure EndBlending(const Blend:TTeeBlend); override;

    Procedure ClipEllipse(Const Rect:TRect; Inverted:Boolean=False); override;

    Procedure ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                          DiffRegion:Boolean=False); override;

    procedure ClipRectangle(Const Rect:TRect); override;

    procedure ClipRectangle(Const Rect:TRect; RoundX,RoundY:Integer); override;

    function CreatePath(const Points:TPointFloatArray; const Types:TPathPointTypes):TTeeBasePath; override;

    procedure Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean; const Z:Single); override;

    procedure DoHorizLine(const X0,X1,Y:Integer); override;
    procedure Draw(X, Y: Integer; const Graphic: TGraphic); override;
    procedure Ellipse(const X1, Y1, X2, Y2: Integer); override;
    procedure EraseBackground(const Rect: TRect); override;
    procedure FillRect(const Rect: TRect); override;
    Procedure GradientFill( Const Rect:TRect;
                               StartColor,EndColor:TColor;
                               Direction:TGradientDirection;
                               Balance:Integer=50;
                               RadialX:Integer=0;
                               RadialY:Integer=0); override;

    procedure LineTo(const X, Y: Integer); override;
    procedure MoveTo(const X, Y: Integer); override;
    procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); override;
    Procedure Polyline(const Points:Array of TPoint); override;
    Procedure Polygon(const Points: Array of TPoint); override;
    procedure Rectangle(const X0,Y0,X1,Y1:Integer); override;
    procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer); override;
    Procedure ShowImage(const DestCanvas, DefaultCanvas: TCanvas;
      Const UserRect: TRect); override;
    procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic); override;
    Function TextHeight(Const St:String):Integer; override;
    procedure TextOut(X, Y: Integer; const Text: String); override;
    Function TextWidth(Const St:String):Integer; override;
    procedure UnClipRectangle; override;

    property AntiAlias:Boolean read FAnti write SetAntiAlias default True;
    property Canvas:TDirect2DCanvas read ICanvas;
  end;

implementation

Uses
  D2D1, SysUtils;

procedure TTeeD2Canvas.DoChangedBrush(const ABrush:TTeeBrush);
begin
  ICanvas.Brush.Assign(Brush);
end;

procedure TTeeD2Canvas.DoChangedFont;
begin
  ICanvas.Font.Assign(Font);
end;

procedure TTeeD2Canvas.SetAntiAlias(const Value:Boolean);
begin
  if FAnti<>Value then
  begin
    FAnti:=Value;

    if Assigned(View3DOptions) then
       View3DOptions.Repaint;
  end;
end;

procedure TTeeD2Canvas.SetBackColor(const Value: TColor);
begin
  FBackColor:=Value;
end;

procedure TTeeD2Canvas.SetBackMode(const Value: TCanvasBackMode);
begin
  FBackMode:=Value;
end;

procedure TTeeD2Canvas.SetFastLine(const Value: Boolean);
begin
//  inherited;
end;

procedure TTeeD2Canvas.SetInterCharSize(ASize: Integer);
begin
  FInterChar:=ASize;
end;

procedure TTeeD2Canvas.DoChangedPen;
{
const
  Styles: array[psDash..psDashDotDot] of TD2D1DashStyle = (D2D1_DASH_STYLE_DASH,
   D2D1_DASH_STYLE_DOT, D2D1_DASH_STYLE_DASH_DOT, D2D1_DASH_STYLE_DASH_DOT_DOT);
var tmpJoin : TD2D1LineJoin;
}
begin
  ICanvas.Pen.Assign(Pen);

  if Pen.Style=psClear then
     ICanvas.Pen.Width:=0;

 {
  if APen is TChartPen then
  begin
    case TChartPen(APen).JoinStyle of
      jsRound: tmpJoin:=D2D1_LINE_JOIN_ROUND;
      jsBevel: tmpJoin:=D2D1_LINE_JOIN_BEVEL;
    else
      tmpJoin:=D2D1_LINE_JOIN_MITER;
    end;

    D2DFactory.CreateStrokeStyle(D2D1StrokeStyleProperties(D2D1_CAP_STYLE_FLAT,
        D2D1_CAP_STYLE_FLAT, D2D1_CAP_STYLE_ROUND, tmpJoin, 10, Styles[Pen.Style]),
        nil, 0, ICanvas.Pen.StrokeStyle);
  end;
 }

end;

function TTeeD2Canvas.BeginBlending(const R: TRect;
  Transparency: TTeeTransparency): TTeeBlend;
begin
  result:=nil;
end;

procedure TTeeD2Canvas.DoHorizLine(const X0, X1, Y: Integer);
begin
  Line(X0,Y,X1,Y);
end;

function TTeeD2Canvas.CheckFastBrush: Boolean;
begin
  result:=False;
end;

procedure TTeeD2Canvas.ClipEllipse(const Rect: TRect; Inverted: Boolean);
begin
//  inherited;
end;

procedure TTeeD2Canvas.ClipPolygon(const Points: array of TPoint;
  NumPoints: Integer; DiffRegion: Boolean);
begin
//  inherited;
end;

procedure TTeeD2Canvas.ClipRectangle(const Rect: TRect);
begin
//  inherited;
end;

procedure TTeeD2Canvas.ClipRectangle(const Rect: TRect; RoundX,
  RoundY: Integer);
begin
//  inherited;
end;

Constructor TTeeD2Canvas.Create;
begin
  inherited;
  FAnti:=True;
end;

function TTeeD2Canvas.CreatePath(const Points:TPointFloatArray; const Types:TPathPointTypes):TTeeBasePath;
begin
  result:=nil; // TODO
end;

procedure TTeeD2Canvas.Curve(const Points: array of TPoint;
  const Tension: Single; Closed: Boolean; const Z: Single);
begin
//  inherited;
end;

class function TTeeD2Canvas.Description: String;
begin
  result:='Direct 2D';
end;

Destructor TTeeD2Canvas.Destroy;
begin
  ICanvas.Free;
  inherited;
end;

procedure TTeeD2Canvas.Draw(X, Y: Integer; const Graphic: TGraphic);
begin
  if Assigned(Graphic) and (not Graphic.Empty) then
     ICanvas.Draw(X,Y,Graphic);
end;

class function TTeeD2Canvas.EditorClass: String;
begin
  result:='';
end;

procedure TTeeD2Canvas.Ellipse(const X1, Y1, X2, Y2: Integer);
begin
  ICanvas.Ellipse(X1,Y1,X2,Y2);
end;

procedure TTeeD2Canvas.EndBlending(const Blend: TTeeBlend);
begin
//  inherited;
end;

procedure TTeeD2Canvas.EraseBackground(const Rect: TRect);
begin
  ICanvas.RenderTarget.Clear(D2D1ColorF(Brush.Color));
end;

procedure TTeeD2Canvas.FastLineTo(const X, Y: Integer);
begin
  LineTo(X,Y);
end;

procedure TTeeD2Canvas.FillRect(const Rect: TRect);
begin
  Rectangle(Rect);
end;

function TTeeD2Canvas.GetBackColor: TColor;
begin
  result:=FBackColor;
end;

function TTeeD2Canvas.GetBackMode: TCanvasBackMode;
begin
  result:=FBackMode;
end;

function TTeeD2Canvas.GetSupportsXORMode: Boolean;
begin
  result:=False;
end;

function TTeeD2Canvas.GetTextAlign: TCanvasTextAlign;
begin
  result:=FAlign;
end;

procedure TTeeD2Canvas.GradientFill(const Rect: TRect; StartColor,
  EndColor: TColor; Direction: TGradientDirection; Balance, RadialX,
  RadialY: Integer);
begin
//  inherited;
  // Temporary !
  ICanvas.Brush.Color:=StartColor;
  ICanvas.Pen.Style:=psClear;
  ICanvas.Pen.Brush.Style:=bsClear;
  ICanvas.Pen.Color:=clNone;
  ICanvas.Pen.Width:=0;

  with Rect do
  ICanvas.Rectangle(Left-1,Top-1,Right+1,Bottom+1);
end;

function TTeeD2Canvas.IsGradientVisible:Boolean; // re-use !!!
begin
  result:=(Brush.Style<>bsClear) and Assigned(Gradient) and Gradient.Visible;
end;

procedure TTeeD2Canvas.Rectangle(const X0,Y0,X1,Y1:Integer);
var tmpGradient : Boolean;
    oldStyle    : TBrushStyle;
begin
  oldStyle:=Brush.Style;

  tmpGradient:=IsGradientVisible;

  if tmpGradient then
  begin
    GradientFill(Gradient,TeeRect(X0,Y0,X1,Y1));
    Brush.Style:=bsClear;
  end;

  if (Brush.Style<>bsClear) or (Pen.Style<>psClear) then
     ICanvas.Rectangle(X0,Y0,X1,Y1);

  if tmpGradient then
     Brush.Style:=oldStyle;
end;

procedure TTeeD2Canvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
begin
  ICanvas.RoundRect(X1,Y1,X2,Y2,X3,Y3);
end;

function TTeeD2Canvas.InitWindow(const DestCanvas: TCanvas;
  const A3DOptions: TView3DOptions; ABackColor: TColor; Is3D: Boolean;
  const UserRect: TRect): TRect;
var
  tmpH,
  tmpW : Integer;
  tmpDC : TTeeCanvasHandle;
begin
  result := inherited;

  tmpW:=UserRect.Right-UserRect.Left;
  tmpH:=UserRect.Bottom-UserRect.Top;

  if UseBuffer then
     tmpDC:=GetHandle
  else
     tmpDC:=A3DOptions.Parent.Handle;

  if (not Assigned(ICanvas)) or (IWidth<>tmpW) or (IHeight<>tmpH)
     or (IOldDC<>tmpDC) then
    if TDirect2DCanvas.Supported then
    begin
      ICanvas.Free;

      if UseBuffer then
         ICanvas := TDirect2DCanvas.Create(tmpDC, UserRect)
      else
         ICanvas := TDirect2DCanvas.Create(tmpDC);

      IWidth:=tmpW;
      IHeight:=tmpH;
      IOldDC:=tmpDC;
    end;

  if FAnti then
     ICanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE)
  else
     ICanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);

  ICanvas.BeginDraw;
end;

procedure TTeeD2Canvas.LineTo(const X, Y: Integer);
begin
  ICanvas.LineTo(X, Y);
end;

procedure TTeeD2Canvas.MoveTo(const X, Y: Integer);
begin
  ICanvas.MoveTo(X, Y);
end;

procedure TTeeD2Canvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
begin
  ICanvas.Pie(X1,Y1,X2,Y2,X3,Y3,X4,Y4);
end;

procedure TTeeD2Canvas.Polygon(const Points: array of TPoint);
begin
  ICanvas.Polygon(Points);
end;

procedure TTeeD2Canvas.Polyline(const Points:Array of TPoint);
begin
  ICanvas.Polyline(Points);
end;

procedure TTeeD2Canvas.SetPixel(X, Y: Integer; Value: TColor);
begin
//  ICanvas.Pixels[X,Y]:=Value;
end;

procedure TTeeD2Canvas.SetPixel3D(X, Y, Z: Integer; Value: TColor);
begin
//  inherited;
end;

procedure TTeeD2Canvas.SetTextAlign(const Value: TCanvasTextAlign);
begin
  FAlign:=Value;
end;

procedure TTeeD2Canvas.SetUseBuffer(Value: Boolean);
begin
  if FBufferedDisplay<>Value then
  begin
    inherited;
    //FreeAndNil(ICanvas);
  end;
end;

procedure TTeeD2Canvas.ShowImage(const DestCanvas, DefaultCanvas: TCanvas;
  const UserRect: TRect);
begin
  ICanvas.EndDraw;
  inherited;
end;

procedure TTeeD2Canvas.SmoothShadow(const Shadow: TTeeShadow; const Rect: TRect;
  Ellipse: Boolean; RoundSize: Integer; const P: array of TPoint;
  DonutPercent: Integer; const StartAngle, EndAngle: Double);
begin
//  inherited;

end;

procedure TTeeD2Canvas.StretchDraw(const Rect: TRect; const Graphic: TGraphic);
begin
  ICanvas.StretchDraw(Rect,Graphic);
end;

function TTeeD2Canvas.TextHeight(const St: String): Integer;
begin
  result:=ICanvas.TextHeight(St);
end;

procedure TTeeD2Canvas.TextOut(X, Y: Integer; const Text: String);
begin
  if FBackMode=cbmOpaque then
     ICanvas.Brush.Color:=FBackColor
  else
     ICanvas.Brush.Color:=clNone;

  if FAlign=TA_RIGHT then
     Dec(X,TextWidth(Text))
  else
  if FAlign=TA_CENTER then
     Dec(X,TextWidth(Text) div 2);

  ICanvas.TextOut(X, Y, Text);
end;

function TTeeD2Canvas.TextWidth(const St: String): Integer;
begin
  result:=ICanvas.TextWidth(St);
end;

procedure TTeeD2Canvas.UnClipRectangle;
begin
//  inherited;
end;

initialization
  TeeRenderClasses.Add(TTeeD2Canvas);
finalization
  TeeRenderClasses.Remove(TTeeD2Canvas);
end.
