{********************************************}
{   Base types and Procedures                }
{ Copyright (c) 1995-2020 by Steema Software }
{        All Rights Reserved                 }
{********************************************}
unit TeeGDIPlus;
{$I TeeDefs.inc}

// Microsoft GDI+ Redistributable:
//
// http://www.microsoft.com/downloads/details.aspx?FamilyID=6a63ab9c-df12-4d41-933c-be590feaa05a&DisplayLang=en
//
// http://msdn.microsoft.com/en-us/library/ms533798%28VS.85%29.aspx

interface

uses
  {$IFNDEF LINUX}
  Windows,
  Classes,
  {$ENDIF}

  {$IFDEF D6}
  Types,
  {$ENDIF}

  {$IFDEF D17}
  System.UITypes,
  {$ENDIF}
  
  Controls, Graphics,

  {$IFDEF TEEOCX}
  TeeGDIPAPI,
  TeeGDIPOBJ,
  {$ELSE}
  {$IFDEF D16}
  GDIPAPI,
  GDIPOBJ,
  {$ELSE}
  TeeGDIPAPI,
  TeeGDIPOBJ,
  {$ENDIF}
  {$ENDIF}

  TeCanvas, TeeProcs;

type
  TGDIPlusFontQuality=(gpfBest, gpfDefault, gpfClearType, gpfNormal);

  (*
  {$IFDEF D16}
  {$HPPEMIT 'typedef VclTee::Gdiplus::Graphics TGPGraphics;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::Brush TGPBrush;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::Pen TGPPen;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::Font TGPFont;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::PrivateFontCollection TGPPrivateFontCollection;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::RectF TGPRectF;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::PointF TGPPointF;'}
  {$HPPEMIT 'typedef VclTee::Gdiplus::GraphicsPath TGPGraphicsPath;'}
  {$ENDIF}
  *)

  {$IFDEF D16}
  {$HPPEMIT 'typedef Gdiplus::Graphics TGPGraphics;'}
  {$HPPEMIT 'typedef Gdiplus::Brush TGPBrush;'}
  {$HPPEMIT 'typedef Gdiplus::Pen TGPPen;'}
  {$HPPEMIT 'typedef Gdiplus::Font TGPFont;'}
  {$HPPEMIT 'typedef Gdiplus::PrivateFontCollection TGPPrivateFontCollection;'}
  {$HPPEMIT 'typedef Gdiplus::RectF TGPRectF;'}
  {$HPPEMIT 'typedef Gdiplus::PointF TGPPointF;'}
  {$HPPEMIT 'typedef Gdiplus::GraphicsPath TGPGraphicsPath;'}
  {$HPPEMIT 'typedef Gdiplus::Matrix TGPMatrix;'}
  {$HPPEMIT 'typedef Gdiplus::Image TGPImage;'}
  {$HPPEMIT 'typedef Gdiplus::Rect TGPRect;'}
  {$HPPEMIT 'typedef Gdiplus::PathGradientBrush TGPPathGradientBrush;'}
  {$HPPEMIT 'typedef Gdiplus::LinearGradientBrush TGPLinearGradientBrush;'}
  {$ENDIF}

  TColorBlend=record
    Colors    : PGPColor;
    Positions : PSingle;
  end;

  TTeeFontCache=record
    Name : String;
    Size : Single;
    Style : TFontStyles;
  end;

  {$IFDEF TEECACHEIMAGES}
  TImageCache=record
    Graphic: TGraphic;
    Image: TGPImage;
  end;
  {$ENDIF}

  TGDIPlusCanvas=class(TTeeCanvas3D)
  private
    FGraphics : TGPGraphics;
    FGPFont   : TGPFont;
    FGPPen    : TGPPen;
    FGPPenFill: TGPBrush;
    FGPBrush  : TGPBrush;

    FX,FY,FZ  : Integer;
    FAnti     : Boolean;
    FScale    : Single;

    ITextRotation: Double;

    IXORCanvas : TTeeCanvas3D;
    IOldClipRect : Array of TGPRectF;

    IPrivateFonts: TGPPrivateFontCollection;

    // Maximum 256 colors for Gradient:
    IBlendColors : Array[0..255] of TGPColor;
    IBlendPositions : Array[0..255] of Single;

    IGradientPolygon : TPointFloatArray;
    IOldFont : TTeeFontCache;

    FBackColor : TColor;
    FBackMode  : TCanvasBackMode;
    FTextAlign : TCanvasTextAlign;
    FInterCharSize : Integer;

    ICachedAntialias : Boolean;

    ITransform : Array of TGPMatrix;

    {$IFDEF TEECACHEIMAGES}
    ICachedImage : Array of TImageCache;
    {$ENDIF}

    procedure ApplyTransform(const Transform:TTeeTransform);
    function ApplyTransparency(const Value:Byte):Byte;

    function CalcTextRect(const Origin:TGPPointF; const Text:String):TRect;
    procedure CheckXorCanvas;

    function CreateLinearGradient(const AGradient:TTeeGradient; const R:TGPRectF):TGPBrush;

    procedure CreateGPPen(const AColor:Cardinal);

    procedure DoAddFonts;
    procedure DoSetAnti(const Value:Boolean);
    procedure DoSetAntiAlias(const Value:Boolean);
    procedure DoSetAntiAliasText;

    function DonutPath( XCenter,YCenter,XRadius,YRadius:Integer;
                        const StartAngle,EndAngle,HolePercent:Double):TGPGraphicsPath;

    procedure DrawBlendImage(const AImage:TGPImage; const Dest:TGPRect);

    {$IFDEF TEECACHEIMAGES}
    function FindCachedImage(const AGraphic:TGraphic):TGPImage;
    {$ENDIF}

    procedure FixAngles(const StartAngle,EndAngle:Double; out AStart,ASweep:Single);

    function GDIPColor(AColor:TColor):Cardinal; overload;
    class function GDIPColor(AColor:TColor; ATransparency:Byte):Cardinal; overload;

    function GetAntiText:TGDIPlusFontQuality;
    function GetColorBlend(const AGradient:TTeeGradient; out ACount:Integer):TColorBlend;
    procedure GetGradientStartEnd(const AGradient:TTeeGradient; out Color0,Color1:TGPColor);
    function GradientToColor(const AColor:TTeeGradientColor):Cardinal;

    function HasPen:Boolean; overload;
    function HasPen(const R:TRect):Boolean; overload;
    function HasPen(const Points:Array of TPoint; FixCurve:Boolean=False):Boolean; overload;
    function HasPen(const Points:Array of TPointFloat):Boolean; overload;
    function HasPen(const Bounds:TGPRectF):Boolean; overload;

    Function InternalTextWidth(const St:String; TypoGraphic:Boolean=False):Integer;

    procedure SetGradientColors(const AGradient:TTeeGradient; const ABrush:TGPPathGradientBrush); overload;
    procedure SetGradientColors(const AGradient:TTeeGradient; const ABrush:TGPLinearGradientBrush); overload;

    class function CreateNewBrush(const ABrush:TTeeBrush; ATransparency:Byte):TGPBrush;

    function GBrush:TGPBrush; overload;
    function GBrush(const R:TRect):TGPBrush; overload;

    function GBrush(const ABrush:TTeeBrush):TGPBrush; overload;
    function GBrush(const ABrush:TTeeBrush; const R:TRect):TGPBrush; overload;
    function GBrush(const ABrush:TTeeBrush; const R:TRectF):TGPBrush; overload;
    function GBrush(const ABrush:TTeeBrush; const Points:Array of TPoint):TGPBrush; overload;
    function GBrush(const ABrush:TTeeBrush; const Points:Array of TPointFloat):TGPBrush; overload;

    function GetGraphics:TGPGraphics;
    procedure RecreateGraphics(const AHandle:HDC);

    procedure SetAnti(const Value: Boolean);
    procedure SetAntiText(const Value: TGDIPlusFontQuality);
    procedure SetScale(const Value:Single);
    procedure SetTextHint(const AHint:TGDIPlusFontQuality);
  protected
    function BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock; override;
    procedure EndEntity; override;

    function CheckFastBrush:Boolean; override;

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

    procedure DrawLighting( AEdgeStyle: TEdgeStyle; ZPos: Integer;
                            const Z0BevelRect, Z1BevelRect, Z0DonutRect: TRect;
                            IStartAngle, IEndAngle: Integer;
                            const Middle0: TPoint;
                            IsDonut: Boolean); override;

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

    procedure PushClipRect;

    function TextBrush(const AColor:TColor; const Origin:TGPPointF; const Text:String):TGPBrush;
    procedure DrawSmoothShadow(const P:TGPGraphicsPath; const AColor:Cardinal; const ABalance:Single);

    procedure SetCanvas(const ACanvas:TCanvas); 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;

    function GetBackColor:TColor; override;
    function GetBackMode:TCanvasBackMode; override;
    Function GetTextAlign:TCanvasTextAlign; override;
    Function IsAliased:Boolean; override;
    function CanvasStyle:TCanvasStyle; override;

    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 XORRectangle(const R:TRect);
    Procedure XORLine(const X0,Y0,X1,Y1:Integer);
  public
    { 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;

    class function IsAvailable:Boolean; override;

    Procedure ShowImage(const DestCanvas,DefaultCanvas:TCanvas; const UserRect:TRect); override;

    procedure Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer); override;
    procedure Arc(const Left, Top, Right, Bottom:Integer; const StartAngle,EndAngle:Single); override;
    procedure ArcTo(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer; const ClockWise:Boolean=False); 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 Donut( XCenter,YCenter,XRadius,YRadius:Integer;
                     Const StartAngle,EndAngle,HolePercent:Double); override;

    procedure DoVertLine(const X,Y0,Y1:Integer); override;
    procedure Draw(X,Y:Integer; const AGraphic:TGraphic); override;
    procedure Ellipse(const X1, Y1, X2, Y2: Integer); override;
    procedure FillRect(const Rect: TRect); override;
    Procedure Line(const X0,Y0,X1,Y1:Integer); override;
    procedure LineTo(const X,Y:Integer); override;
    procedure LineTo3D(const X,Y,Z:Integer); override;
    procedure MoveTo(const X,Y:Integer); override;
    procedure MoveTo3D(const X,Y,Z:Integer); override;

    procedure Path(const P:TGPGraphicsPath);

    procedure Pie(const R:TRect; const StartAngle,EndAngle:Double); override;
    procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); override;

    procedure Polygon({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;
    procedure PolygonFloat(const Points:Array of TPointFloat); override;

    Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;
    procedure PolylineFloat(const Points:Array of TPointFloat); override;
    procedure Rectangle(const X0,Y0,X1,Y1:Integer); override;

    class procedure ResizeBitmap(const Source,Dest:TBitmap);

    procedure RotateLabel(x,y:Integer; const St:String; RotDegree:Double;
                          AllowHTML:Boolean); override;

    procedure RoundRect(X1,Y1,X2,Y2,X3,Y3:Integer); override;
    class function RoundRectPath(R:TRect; x,y:Integer):TGPGraphicsPath;
    procedure RoundRectWithZ(const R:TRect; X,Y,Z:Integer); override;
    procedure StretchDraw(const ARect:TRect; const AGraphic:TGraphic); override;
    procedure TextOut(X,Y:Integer; const Text:String); overload; override;
    Procedure TextOut(X,Y:Single; const Text:String); overload;
    Procedure TextOut3D(x,y,z:Integer; const Text:String; AllowHTML:boolean); override;

    Function TextHeight(const St:String):Integer; override;
    Function TextSize(const St:String):TPointFloat; override;
    Function TextWidth(const St:String):Integer; override;

    Procedure HorizLine3D(Left,Right,Y,Z:Integer); override;
    Procedure LineWithZ(X0,Y0,X1,Y1,Z:Integer); override;

    procedure UnClipRectangle; override;

    Procedure GradientFill( Const Rect:TRect;
                        StartColor,EndColor:TColor;
                        Direction:TGradientDirection;
                        Balance:Integer=50;
                        RadialX:Integer=0;
                        RadialY:Integer=0); overload; override;

    Procedure VertLine3D(X,Top,Bottom,Z:Integer); override;
    Procedure ZLine3D(X,Y,Z0,Z1:Integer); override;

    // public
    property Graphics:TGPGraphics read GetGraphics;
    property Scale:Single read FScale write SetScale;

    // published
    property AntiAlias:Boolean read FAnti write SetAnti default True;
    property AntiAliasText:TGDIPlusFontQuality read GetAntiText write SetAntiText default gpfDefault;
  end;

  {$IFDEF D16}
  [ComponentPlatformsAttribute(pidWin32 or pidWin64)]
  {$ENDIF}
  TTeeGDIPlus = class(TComponent)
  private
    FActive       : Boolean;
    FAntialias    : Boolean;
    FAntiAliasText: TGDIPlusFontQuality;
    FScale        : Single;
    FTeePanel     : TCustomTeePanel;

    Procedure Activate;
    function GetCanvas:TGDIPlusCanvas;
    function IsScaleStored:Boolean;
    procedure RemoveTag;
    Procedure ResetCanvas;

    Procedure SetActive(const Value:Boolean);
    Procedure SetAntialias(const Value:Boolean);
    Procedure SetAntialiasText(const Value:TGDIPlusFontQuality);
    Procedure SetScale(const Value:Single);
    Procedure SetTeePanel(const Value:TCustomTeePanel);
  protected
    procedure Notification( AComponent: TComponent;
                            Operation: TOperation); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    property Canvas:TGDIPlusCanvas read GetCanvas;
  published
    property Active:Boolean read FActive write SetActive default False;
    property Antialias:Boolean read FAntialias write SetAntialias default True;
    property AntiAliasText:TGDIPlusFontQuality read FAntiAliasText write SetAntiAliasText default gpfDefault;
    property Scale:Single read FScale write SetScale stored IsScaleStored;

    property TeePanel:TCustomTeePanel read FTeePanel write SetTeePanel;
  end;

implementation

uses
  TeeHTML, Math, SysUtils;

type
  TTeeGDIPlusPath=class(TTeeBasePath)
  private
    ICanvas : TGDIPlusCanvas;
    IPath : TGPGraphicsPath;
    FX,FY : Single;
  public
    Constructor Create(const ACanvas:TGDIPlusCanvas);
    Destructor Destroy; override;

    procedure ArcTo(const X,Y,Width,Height,StartAngle,SweepAngle:Single); override;
    procedure Close; override;
    procedure Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean); override;
    procedure Draw(const ACanvas:TTeeCanvas); override;
    procedure GetPoints(out Types:TPathPointTypes; out Points:TPointFloatArray; out Bounds:TRectF); override;
    procedure LineTo(const X,Y:Single); override;
    procedure MoveTo(const X,Y:Single); override;
    procedure TextOut(const X,Y:Single; const Text:String); override;
  end;

{ TGDIPlusCanvas }

Constructor TGDIPlusCanvas.Create;
begin
  inherited;

  {$IFDEF TEEOCX}
  TeeGDIPlusStartup;
  {$ELSE}
  {$IFNDEF D16}
  TeeGDIPlusStartup;
  {$ENDIF}
  {$ENDIF}

  FAnti:=True;
  FontQuality:=fqDefault;

  FScale:=1;
  Transparency:=255;

  INoFixAlign:=FAnti;
  FSupportsAlpha:=True;

  // Internal non-GDI+ Canvas to draw zoom rectangle, etc:
  IXORCanvas:=TTeeCanvas3D.Create; //TV52014961
  IXORCanvas.UseBuffer:=False;

  if IsAvailable then
     GetGraphics;
end;

Destructor TGDIPlusCanvas.Destroy;

  {$IFDEF TEECACHEIMAGES}
  procedure FreeImageCache;
  var t : Integer;
  begin
    for t:=0 to Length(ICachedImage)-1 do
        ICachedImage[t].Image.Free;

    ICachedImage:=nil;
  end;
  {$ENDIF}

begin
  FGPPenFill.Free;
  FGPPen.Free;

  {$IFDEF TEEOCX}
  if ocxShutDown then
  begin
    FGPBrush := nil;
    FGPFont:=nil;
    FGraphics:=nil;
  end
  else
  {$ENDIF}
  begin
    FGPBrush.Free;
    FGPFont.Free;
    FGraphics.Free;
  end;

  IXORCanvas.Free;

  IPrivateFonts.Free;

  {$IFDEF TEECACHEIMAGES}
  FreeImageCache;
  {$ENDIF}

  inherited;
end;

class function TGDIPlusCanvas.Description:String;
begin
  result:='GDI+'; //TeeMsg_GDIPlus;
end;

// Returns the Form class name that allows editing our properties (protected)
class Function TGDIPlusCanvas.EditorClass:String;
Begin
  result:='TGDIPlusEditor';
end;

procedure TGDIPlusCanvas.SetAnti(const Value: Boolean);
begin
  if FAnti<>Value then
  begin
    FAnti:=Value;
    INoFixAlign:=FAnti;

    if Assigned(FGraphics) then
       DoSetAntiAlias(FAnti);

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

function TGDIPlusCanvas.GetAntiText:TGDIPlusFontQuality;
begin
  case FontQuality of
    fqBest    : result:=gpfBest;
    fqNormal  : result:=gpfNormal;
    fqDefault : result:=gpfDefault;
  else
    result:=gpfClearType;
  end;
end;

procedure TGDIPlusCanvas.SetAntiText(const Value: TGDIPlusFontQuality);
begin
  if GetAntiText<>Value then
  begin
    case Value of
        gpfBest : FontQuality:=fqBest;
     gpfDefault : FontQuality:=fqDefault;
     gpfNormal  : FontQuality:=fqNormal;
    else
      FontQuality:=fqClearType;
    end;

    if Assigned(View3DOptions) then
       View3DOptions.Repaint;

    if Assigned(FGraphics) then
       DoSetAntiAliasText;
  end;
end;

procedure TGDIPlusCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
  if Assigned(FGPPen) then
     FGPPen.SetColor(GDIPColor(Value))
  else
     CreateGPPen(GDIPColor(Value));

  FGraphics.DrawLine(FGPPen,X,Y,X+1,Y);
end;

procedure TGDIPlusCanvas.SetPixel3D(X, Y, Z: Integer; Value: TColor);
begin
  Calc3DPos(x,y,z);
  SetPixel(x,y,Value);
end;

function TGDIPlusCanvas.GBrush(const ABrush:TTeeBrush; const Points: array of TPoint): TGPBrush;
var P : TPointFloatArray;
    t, l : Integer;
begin
  l:=Length(Points);
  SetLength(P,l);
  try
    for t:=0 to l-1 do
    begin
      P[t].x:=Points[t].X;
      P[t].y:=Points[t].Y;
    end;

    result:=GBrush(ABrush,P);
  finally
    P:=nil;
  end;
end;

procedure TGDIPlusCanvas.SetGradientColors(const AGradient:TTeeGradient; const ABrush:TGPPathGradientBrush);
var tmpBlend : TColorBlend;
    ColorItems : Integer;
begin
  tmpBlend:=GetColorBlend(AGradient,ColorItems);
  ABrush.SetInterpolationColors(PARGB(tmpBlend.Colors), tmpBlend.Positions, ColorItems);
end;

procedure TGDIPlusCanvas.SetGradientColors(const AGradient:TTeeGradient; const ABrush:TGPLinearGradientBrush);
var tmpBlend : TColorBlend;
    ColorItems : Integer;
begin
  tmpBlend:=GetColorBlend(AGradient,ColorItems);
  ABrush.SetInterpolationColors(tmpBlend.Colors, tmpBlend.Positions, ColorItems);
end;

function GetLinearGradientDirection(const Direction: TGradientDirection):LinearGradientMode;
Begin
  case Direction of
    gdTopBottom: result:= LinearGradientMode(LinearGradientModeVertical);
    gdBottomTop: result:= LinearGradientMode(LinearGradientModeVertical);
    gdLeftRight: result:= LinearGradientMode(LinearGradientModeHorizontal);
    gdRightLeft: result:= LinearGradientMode(LinearGradientModeHorizontal);
  gdFromTopLeft: result:= LinearGradientMode(LinearGradientModeForwardDiagonal);
 gdFromBottomLeft: result:= LinearGradientMode(LinearGradientModeBackwardDiagonal);
   gdDiagonalUp: result:= LinearGradientMode(LinearGradientModeBackwardDiagonal);
 gdDiagonalDown: result:= LinearGradientMode(LinearGradientModeForwardDiagonal);
  else
    result:=LinearGradientMode(LinearGradientModeVertical);
  end;
end;

function GradientAngle(const ADirection:TGradientDirection):Integer;
begin
  case ADirection of
    gdTopBottom: result:=90;
    gdBottomTop: result:=270;
    gdRightLeft: result:=180;
    gdFromTopLeft,
    gdDiagonalUp: result:=45;
    gdFromBottomLeft,
    gdDiagonalDown: result:=135;
  else
    result:=0;
  end;
end;

function TGDIPlusCanvas.CreateLinearGradient(const AGradient:TTeeGradient;
                 const R:TGPRectF): TGPBrush;
var Color0 : TGPColor;
    Color1 : TGPColor;
begin
  GetGradientStartEnd(AGradient,Color0,Color1);

  if AGradient.Angle=0 then
     result:= TGPLinearGradientBrush.Create(
                        R, Color0,Color1, GetLinearGradientDirection(AGradient.Direction))
  else
     result:= TGPLinearGradientBrush.Create(
                        R, Color0,Color1, GradientAngle(AGradient.Direction)+AGradient.Angle);

  if AGradient.Colors.Count>2 then
     SetGradientColors(AGradient,TGPLinearGradientBrush(result));

//  TGPLinearGradientBrush(result).SetGammaCorrection(True);
end;

function TGDIPlusCanvas.GBrush(const ABrush:TTeeBrush; const Points: Array of TPointFloat): TGPBrush;
var tmpCenter : TGPPoint;
    tmpPoints : PGPPointF;
    tmpPath   : TGPGraphicsPath;
    tmpBounds : TGPRectF;
    tmpW,
    tmpH      : Integer;

    function ConvertPoints(const P:Array of TPointFloat):Integer;
    begin
      result:=Length(P);
      tmpPoints:=@P[0];
    end;

var tmpNum : Integer;
    tmpGradient: TTeeGradient;
begin
  tmpGradient:=ABrush.Gradient;

  if Assigned(IGradientPolygon) and (Length(IGradientPolygon) > 0) then
     tmpNum:=ConvertPoints(IGradientPolygon)
  else
     tmpNum:=ConvertPoints(Points);

  tmpPath:=TGPGraphicsPath.Create;
  tmpPath.AddPolygon(tmpPoints,tmpNum);

  tmpPath.GetBounds(tmpBounds);

  if (tmpGradient.Direction=gdFromCenter) or (tmpGradient.Direction=gdRadial) then
  begin
    if tmpGradient.Direction=gdRadial then
    begin
       tmpW:=Round(tmpBounds.Width*0.25);
       tmpH:=Round(tmpBounds.Height*0.25);
       tmpBounds.X:=tmpBounds.X-tmpW;
       tmpBounds.Y:=tmpBounds.Y-tmpH;
       tmpBounds.Width:=tmpBounds.Width+(2*tmpW);
       tmpBounds.Height:=tmpBounds.Height+(2*tmpH);
       tmpPath.Reset;
       tmpPath.AddEllipse(tmpBounds);
    end
    else
    begin
       tmpPath.Reset;
       tmpPath.AddRectangle(tmpBounds);
    end;

    result:=TGPPathGradientBrush.Create(tmpPath);

    if (tmpGradient.RadialX<>0) or (tmpGradient.RadialY<>0) then
    begin
      tmpCenter.X:=0;
      tmpCenter.Y:=0;

      TGPPathGradientBrush(result).GetCenterPoint(tmpCenter);

      tmpCenter.X:=tmpCenter.X+tmpGradient.RadialX;
      tmpCenter.Y:=tmpCenter.Y+tmpGradient.RadialY;

      TGPPathGradientBrush(result).SetCenterPoint(tmpCenter);
    end;

    SetGradientColors(tmpGradient,TGPPathGradientBrush(result));
  end
  else
    result:=CreateLinearGradient(tmpGradient,tmpBounds);

  tmpPath.Free;
end;

procedure TGDIPlusCanvas.DrawLighting(AEdgeStyle: TEdgeStyle; ZPos: Integer;
                                      const Z0BevelRect, Z1BevelRect, Z0DonutRect: TRect;
                                      IStartAngle, IEndAngle: Integer;
                                      const Middle0: TPoint;
                                      IsDonut: Boolean);
var list: TPointFloatArray;
    count: Integer;

  Procedure DrawPoints;
  begin
    PolygonFloat(list);
  end;

  procedure DoTopGradient(ZDepth: Integer);
  var i   : Integer;
      tmp : TPointFloatArray;
  begin
    SetLength(tmp, 361);
    for i := 0 to 360 do
        tmp[i]:=PointFromCircle(Z0BevelRect, i, ZDepth);

    Gradient.Visible:=True;

    Gradient.Colors.Clear;
    Gradient.Colors.Add(0,clWhite);
    Gradient.Colors.Add(1,clWhite,100);

    Gradient.Direction:=gdRadial;
    Gradient.RadialX:= -((z0BevelRect.Right - z0BevelRect.Left) div 2);
    Gradient.RadialY:= -((z0BevelRect.Bottom - z0BevelRect.Top) div 2);

    IGradientPolygon:=tmp;

    count:=0;

    if IsDonut then
       SetLength(list, 2 + ((IEndAngle - IStartAngle) * 2))
    else
       SetLength(list, 2 + (IEndAngle - IStartAngle));

    if (not isDonut) then
    begin
      list[count].X:=middle0.X;
      list[count].Y:=middle0.Y;
      Inc(count);
    end;

    for i := iStartAngle to iEndAngle do
    begin
      list[count]:=PointFromCircle(z0BevelRect, i, zDepth);
      Inc(count);
    end;

    if isDonut then
       for i := iEndAngle downto iStartAngle do
       begin
         list[count]:=PointFromCircle(z0DonutRect, i, zDepth);
         Inc(count);
       end;

    DrawPoints;
  end;

  procedure DoFlatGradient(ZDepth: Integer);
  var start,
      aend,
      i     : Integer;
      tmp   : TPointFloatArray;
  begin
    if ((iStartAngle <= 295) and (iEndAngle   >= 335)) or
       ((iStartAngle >= 295) and (iStartAngle <= 335)) or
       ((iEndAngle   >= 295) and (iEndAngle   <= 335)) then
    begin

      count:=0;
      SetLength(tmp, (2*40)+2);

      for i := 295 to 335 do
      begin
        tmp[count]:=PointFromCircle(z1BevelRect, i, zDepth);
        Inc(count);
      end;

      for i := 335 downto 295 do
      begin
        tmp[count]:=PointFromCircle(z0BevelRect, i, zDepth);
        Inc(count);
      end;

      Gradient.Colors.Clear;
      Gradient.Colors.Add(0,clWhite,100);
      Gradient.Colors.Add(0.5,clWhite,20);
      Gradient.Colors.Add(1,clWhite,100);

      Gradient.Direction:=gdTopBottom;
      Gradient.Angle:=90;
      IGradientPolygon:=tmp;

      if (iStartAngle <= 295) and (iEndAngle >= 335) then
      begin
        start := 295;
        aend := 335;
      end
      else
      if (iStartAngle >= 295) and (iStartAngle <= 335) then
      begin
        start := iStartAngle;
        aend := 335;
      end
      else
      if (iEndAngle >= 295) and (iEndAngle <= 335) then
      begin
        start := 295;
        aend := iEndAngle;
      end
      else
      begin
        start := 0;
        aend := 0;
      end;

      count:=0;
      SetLength(list, 2 + ((aend - start) * 2));

      for i := start to aend do
      begin
        list[count]:=PointFromCircle(z1BevelRect, i, zDepth);
        Inc(count);
      end;

      for i := aend downto start do
      begin
        list[count]:=PointFromCircle(z0BevelRect, i, zDepth);
        Inc(count);
      end;

      DrawPoints;
    end;
  end;

  procedure DoCurvedGradient(ZDepth: Integer);
  var start,
      aend,
      i    : Integer;
      tmp  : TPointFloatArray;
      tmpR : TRectF;
  begin
    if ((iStartAngle <= 295) and (iEndAngle   >= 335)) or
       ((iStartAngle >= 295) and (iStartAngle <= 335)) or
       ((iEndAngle   >= 295) and (iEndAngle   <= 335)) then
    begin
      count:=0;
      SetLength(tmp, (2*40)+2);

      for i := 295 to 335 do
      begin
        tmp[count]:=PointFromCircle(z1BevelRect, i, zDepth);
        Inc(count);
      end;

      for i := 335 downto 295 do
      begin
        tmp[count]:=PointFromCircle(z0BevelRect, i, zDepth);
        Inc(count);
      end;

      Gradient.Colors.Clear;
      Gradient.Colors.Add(0,clWhite);
      Gradient.Colors.Add(1,clWhite,100);

      tmpR:=PolygonBounds(tmp);
      Gradient.Direction:=gdRadial;
      Gradient.RadialX:= -Round((tmpR.Right - tmpR.Left) *0.125);
      Gradient.RadialY:= -Round((tmpR.Bottom - tmpR.Top) *0.125);
      IGradientPolygon:=tmp;

      if (iStartAngle <= 295) and (iEndAngle >= 335) then
      begin
        start := 295;
        aend := 335;
      end
      else
      if (iStartAngle >= 295) and (iStartAngle <= 335) then
      begin
        start := iStartAngle;
        aend := 335;
      end
      else
      if (iEndAngle >= 295) and (iEndAngle <= 335) then
      begin
        start := 295;
        aend := iEndAngle;
      end
      else
      begin
        start := 0;
        aend := 0;
      end;

      count:=0;
      SetLength(list, 2 + ((aend - start) * 2));

      for i := start to aend do
      begin
        list[count]:=(PointFromCircle(z1BevelRect, i, zDepth));
        Inc(count);
      end;

      for i := aend downto start do
      begin
        list[count]:=(PointFromCircle(z0BevelRect, i, zDepth));
        Inc(count);
      end;

      DrawPoints;
    end;
  end;

var OldGradient : TTeeGradient;
    OldStyle    : TPenStyle;
begin
  OldGradient:=TTeeGradient.Create(nil);
  try
    OldGradient.Assign(Gradient);

    OldStyle:=Pen.Style;
    Pen.Style:=psClear;

    DoTopGradient(ZPos);

    //{$IFDEF D10} //Code below crashes in D2005 and older
    case AEdgeStyle of
      edFlat   : DoFlatGradient(ZPos);
      edCurved : DoCurvedGradient(ZPos);
    end;
    //{$ENDIF}

    Pen.Style:=OldStyle;
    Gradient.Assign(OldGradient);

    IGradientPolygon:=nil;
  finally
    OldGradient.Free;
  end;
end;

class function TGDIPlusCanvas.GDIPColor(AColor:TColor; ATransparency:Byte):Cardinal;
var tmp : Integer;
begin
  // RGB --> BGR
  TRGBA(result).Red:=TRGBA(AColor).Blue;
  TRGBA(result).Green:=TRGBA(AColor).Green;
  TRGBA(result).Blue:=TRGBA(AColor).Red;

  tmp:=ATransparency-TRGBA(AColor).Alpha;
  if tmp<0 then tmp:=0;

  TRGBA(result).Alpha:=tmp;
end;

function TGDIPlusCanvas.GDIPColor(AColor:TColor):Cardinal;
begin
  result:=GDIPColor(AColor, Transparency);
end;

function TGDIPlusCanvas.GBrush:TGPBrush;
begin
  result:=GBrush(Brush);
end;

function TGDIPlusCanvas.GBrush(const R:TRect):TGPBrush;
begin
  result:=GBrush(Brush,R);
end;

function CreateImgAttributes(Transparency:Byte):TGPImageAttributes;
var tmpMatrix : TColorMatrix;
    x,y : Integer;
begin
  result:=TGPImageAttributes.Create;

  for y:=0 to 4 do
      for x:=0 to 4 do
          if x=y then
             tmpMatrix[x,y]:=1
          else
             tmpMatrix[x,y]:=0;

  tmpMatrix[3,3]:=Transparency/255;

  result.SetColorMatrix(tmpMatrix,ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
end;

type
  TTeeBrushAccess=class(TTeeBrush);

class function TGDIPlusCanvas.CreateNewBrush(const ABrush:TTeeBrush; ATransparency:Byte):TGPBrush;

  function GetHatchStyle:HatchStyle;
  begin
    if (Ord(ABrush.HatchStyle) < 6) then
    Begin
      case ABrush.Style of
        bsHorizontal: result:=HatchStyleHorizontal;
          bsVertical: result:=HatchStyleVertical;
         bsFDiagonal: result:=HatchStyleForwardDiagonal;
         bsBDiagonal: result:=HatchStyleBackwardDiagonal;
             bsCross: result:=HatchStyleCross
      else
         result := HatchStyleDiagonalCross; //bsDiagCross
      end;
    end
    else
    if (Ord(ABrush.HatchStyle) <= Ord(HatchStyleMax)) then
      result:=HatchStyle(Ord(ABrush.HatchStyle))
    else
        result := HatchStyleHorizontal; //default
    end;

  function CreateBrushImage(const AImage:TGPImage):TGPBrush;
  var tmpAttr : TGPImageAttributes;
  begin
    //(*  Needs origin XY, not available here ?
    if ATransparency<255 then
    begin
      tmpAttr:=CreateImgAttributes(ATransparency);

      result:=TGPTextureBrush.Create(AImage,MakeRect(0,0,AImage.GetWidth,AImage.GetHeight),tmpAttr);
      TGPTextureBrush(result).SetWrapMode(WrapModeTile);

      tmpAttr.Free;
    end
    else
       result:=TGPTextureBrush.Create(AImage);
  end;

var FGPImage : TGPImage;
    tmp : TGraphic;
    tmpB : TBitmap;
begin
  if TTeeBrushAccess(ABrush).HasImage and
     Assigned(ABrush.Image.Graphic) and (not ABrush.Image.Graphic.Empty) then
  begin
    tmp:=ABrush.Image.Filtered;

    if tmp is TBitmap then
       FGPImage:=TGPBitmap.Create(TBitmap(tmp).Handle,TBitmap(tmp).Palette)
    else
    begin
      tmpB:=TBitmap.Create;
      try
        tmpB.Assign(ABrush.Image.Graphic);
        FGPImage:=TGPBitmap.Create(tmpB.Handle,tmpB.Palette);
      finally
        tmpB.Free;
      end;
    end;

    result:=CreateBrushImage(FGPImage);
    FGPImage.Free;
  end
  else
  if Assigned(ABrush.Bitmap) and (not ABrush.Bitmap.Empty) then
  begin
    FGPImage:=TGPBitmap.Create(ABrush.Bitmap.Handle,ABrush.Bitmap.Palette);

    result:=CreateBrushImage(FGPImage);
    FGPImage.Free;
  end
  else
  if ABrush.Style=bsSolid then
  begin
    (* It's faster to free and re-create the brush, than casting "is" and changing its color
    if FGPBrush is TGPSolidBrush then
       TGPSolidBrush(FGPBrush).SetColor(GDIPColor(Brush.Color))
    else
    *)
    result:=TGPSolidBrush.Create(GDIPColor(ABrush.Color,ATransparency));
  end
  else
    result:=TGPHatchBrush.Create(GetHatchStyle,
                           GDIPColor(ABrush.Color,ATransparency),GDIPColor(ABrush.BackColor,ATransparency));
end;

function TGDIPlusCanvas.GBrush(const ABrush:TTeeBrush):TGPBrush;
begin
  result:=CreateNewBrush(ABrush,Transparency);
end;

function TGDIPlusCanvas.GBrush(const ABrush:TTeeBrush; const R:TRect):TGPBrush;
begin
  result:=GBrush(ABrush,TeeRect(R));
end;

function MakeRect(const R:TRectF):TGPRectF; overload;
begin
  result.X:=R.Left;
  result.Y:=R.Top;
  result.Width:=R.Right-R.Left+1;
  result.Height:=R.Bottom-R.Top+1;
end;

function TGDIPlusCanvas.GBrush(const ABrush:TTeeBrush; const R:TRectF):TGPBrush;

  function CreateCircular(const AGradient:TTeeGradient;
                          const FromCenter:Boolean;
                          const R:TRectF):TGPPathGradientBrush;
  var tmpPath : TGPGraphicsPath;
      tmpR : TRectF;

      tmpX, tmpY: Single;
      tmpCenter:TGPPointF;
  begin
    tmpPath:=TGPGraphicsPath.Create;

    if FromCenter then
       tmpPath.AddRectangle(MakeRect(R))
    else
    begin
      tmpR:=R;
      InflateRectF(tmpR, ((tmpR.Right - tmpR.Left)*0.25), ((tmpR.Bottom - tmpR.Top)*0.25));
      tmpPath.AddEllipse(MakeRect(tmpR.Left-1,tmpR.Top-1,(tmpR.Right+2)-tmpR.Left, (tmpR.Bottom+2)-tmpR.Top));
    end;

    result:=TGPPathGradientBrush.Create(tmpPath);

    tmpPath.Free;

    RectCenter(tmpR, tmpX, tmpY);
    tmpCenter.X:=tmpX+AGradient.RadialX;
    tmpCenter.Y:=tmpY+AGradient.RadialY;

    TGPPathGradientBrush(result).SetCenterPoint(tmpCenter);

    SetGradientColors(AGradient,TGPPathGradientBrush(result));
  end;

  function CreateNewGradientBrush(const AGradient:TTeeGradient):TGPBrush;
  var Direction: TGradientDirection;

      tmpH, tmpW: Single;
      tmpR: TRectF;
  begin
    Direction:=AGradient.Direction;

    tmpR:=AGradient.CustomTargetRectangle;
    RectSize(tmpR, tmpW, tmpH);

    if (tmpW=0) and (tmpH=0) then
        tmpR:=R;

    if (Direction=gdFromCenter) or (Direction=gdRadial) then
       result:=CreateCircular(AGradient,Direction=gdFromCenter,tmpR)
    else
       result:=CreateLinearGradient(AGradient,MakeRect(tmpR));
  end;

begin
  if (ABrush.Style<>bsClear) and TTeeBrushAccess(ABrush).HasGradient then
     result:=CreateNewGradientBrush(ABrush.Gradient)
  else
     result:=CreateNewBrush(ABrush,Transparency);
end;

// Deprecated. Use Gradient.Draw(ACanvas...) methods:
procedure TGDIPlusCanvas.GradientFill(const Rect: TRect; StartColor,
  EndColor: TColor; Direction: TGradientDirection; Balance, RadialX,
  RadialY: Integer);
begin
  Gradient.Visible:=True;
  Gradient.StartColor:=StartColor;
  Gradient.MidColor:=clNone;
  Gradient.EndColor:=EndColor;
  Gradient.Direction:=Direction;
  Gradient.Balance:=Balance;
  Gradient.RadialX:=RadialX;
  Gradient.RadialY:=RadialY;

  FGPBrush.Free;
  FGPBrush:=GBrush(Rect);
  FGraphics.FillRectangle(FGPBrush,MakeRect(Rect));
end;

procedure TGDIPlusCanvas.DoAddFonts;

  procedure AddFontFromResource(const ResName:String);
  var
    tmp     : TResourceStream;
    MemSize : Int64;
    Buffer  : Pointer;
  begin
    tmp:=TeeResourceStream(resName);
    try
      MemSize:=tmp.Size;  // Windows 2003 "+1" ??

      GetMem(Buffer, MemSize);
      try
        tmp.Read(Buffer^,MemSize);

        IPrivateFonts.AddMemoryFont(Buffer, MemSize);

      finally
        FreeMem(Buffer);
      end;
    finally
      tmp.Free;
    end;
  end;

const
  resFile1='TEELCDFONT';
  resFile2='TEELEDFONT';
begin
  AddFontFromResource(resFile1);
  AddFontFromResource(resFile2);
end;

procedure TGDIPlusCanvas.RecreateGraphics(const AHandle:HDC);

  procedure CheckError(const AStatus:TStatus);
  const StatusText:Array[TStatus] of String=(
      'Ok',
      'Generic Error',
      'Invalid Parameter',
      'Out Of Memory',
      'Object Busy',
      'Insufficient Buffer',
      'Not Implemented',
      'Win32 Error',
      'Wrong State',
      'Aborted',
      'File Not Found',
      'Value Overflow',
      'Access Denied',
      'Unknown Image Format',
      'Font Family Not Found',
      'Font Style Not Found',
      'Not TrueType Font',
      'Unsupported Gdiplus Version',
      'Gdiplus Not Initialized',
      'Property Not Found',
      'Property Not Supported');

  begin
    if AStatus<>Ok then
       Raise Exception.Create('GDI+ Error: '+IntToStr(Ord(AStatus))+' '+StatusText[AStatus]);
  end;

begin
  FGraphics:=TGPGraphics.Create(AHandle);

  CheckError(FGraphics.GetLastStatus);

  DoSetAntiAlias(FAnti);
  DoSetAntialiasText;

  FGraphics.SetPageUnit(UnitPixel);
end;

function TGDIPlusCanvas.GetGraphics:TGPGraphics;

  function BufferHandle:TTeeCanvasHandle;
  begin
    if not Assigned(Bitmap) then
       Bitmap:=TBitmap.Create;

    if TeeBitsperPixel(Bitmap.Canvas.Handle)=32 then
       Bitmap.PixelFormat:=pf32Bit
    else
       Bitmap.PixelFormat:=TeePixelFormat;

    result:=Bitmap.Canvas.Handle;
  end;

begin
  if not Assigned(FGraphics) then
     if UseBuffer then
        RecreateGraphics(BufferHandle)
     else
     if ReferenceCanvas<>nil then
        RecreateGraphics(Handle);

  if not Assigned(FGPFont) then
     DoChangedFont;

  result:=FGraphics;
end;

procedure TGDIPlusCanvas.DoSetAnti(const Value:Boolean);
begin
  if ICachedAntialias<>Value then
  begin
    if Value then
       FGraphics.SetSmoothingMode({$IFDEF D16}SmoothingModeHighQuality{$ELSE}QualityModeHigh{$ENDIF})
    else
       FGraphics.SetSmoothingMode({$IFDEF D16}SmoothingModeHighSpeed{$ELSE}QualityModeLow{$ENDIF});

    ICachedAntialias:=Value;
  end;
end;

procedure TGDIPlusCanvas.DoSetAntiAlias(const Value:Boolean);
begin
  ICachedAntialias:=not Value; // Force DoSetAnti

  DoSetAnti(Value);

  if Value then
  begin
    FGraphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);
    //FGraphics.SetCompositingQuality(CompositingQualityHighQuality); // Gamma?
  end
  else
  begin
    FGraphics.SetPixelOffsetMode(PixelOffsetModeNone);
    //FGraphics.SetCompositingQuality(CompositingQualityHighSpeed);
  end;
end;

procedure TGDIPlusCanvas.DoSetAntiAliasText;
begin
  SetTextHint(GetAntiText);
end;

procedure TGDIPlusCanvas.ApplyTransform(const Transform:TTeeTransform);
var tmpScale,
    tmpRotate,
    tmpUseOrigin : Boolean;
begin
  if Transform.Clip then
     ClipRectangle(Transform.Bounds);

  if (Transform.Translate.X<>0) or (Transform.Translate.Y<>0) then
     FGraphics.TranslateTransform(Transform.Translate.X,Transform.Translate.Y);

  tmpScale:=(Transform.Scale.X<>1) or (Transform.Scale.Y<>1);
  tmpRotate:=(Transform.Rotate<>0);
  tmpUseOrigin:=tmpRotate or tmpScale;

  if tmpUseOrigin then
     FGraphics.TranslateTransform(Transform.Origin.X,Transform.Origin.Y);

  if tmpRotate then
     FGraphics.RotateTransform(Transform.Rotate);

  if tmpScale then
     FGraphics.ScaleTransform(Transform.Scale.X,Transform.Scale.Y);

  if tmpUseOrigin then
     FGraphics.TranslateTransform(-Transform.Origin.X,-Transform.Origin.Y);
end;

function TGDIPlusCanvas.BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock;
var l : Integer;
begin
  // Push transform
  l:=Length(ITransform);
  SetLength(ITransform,l+1);

  if Assigned(Transform) and Transform.Enabled then
  begin
    ITransform[l]:=TGPMatrix.Create;
    Graphics.GetTransform(ITransform[l]);

    ApplyTransform(Transform);
  end
  else
    ITransform[l]:=nil;

  result:=Visual;
end;

procedure TGDIPlusCanvas.EndEntity;
var l : Integer;
begin
  // Pop transform
  l:=Length(ITransform);

  if l>0 then
  begin
    if Assigned(ITransform[l-1]) then
    begin
      Graphics.SetTransform(ITransform[l-1]);
      FreeAndNil(ITransform[l-1]);

      UnClipRectangle;
    end;

    SetLength(ITransform,l-1);
  end;
end;

procedure TGDIPlusCanvas.SetScale(const Value:Single);
begin
  if FScale<>Value then
  begin
    FScale:=Value;

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

procedure TGDIPlusCanvas.SetTextHint(const AHint:TGDIPlusFontQuality);
begin
  case AHint of
      gpfBest: FGraphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
   gpfDefault: FGraphics.SetTextRenderingHint(TextRenderingHintSystemDefault);
 gpfClearType: FGraphics.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
  else
    FGraphics.SetTextRenderingHint(TextRenderingHintSingleBitPerPixelGridFit);
  end;
end;

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

  procedure CheckGraphics;
  var tmpDC : HDC;
      tmpOk : Boolean;
  begin
    if Assigned(FGraphics) then
       if UseBuffer then
          // In "BufferedDisplay" mode, FGraphics should be always recreated, (see ShowImage)
          FreeAndNil(FGraphics)
       else
       begin
         // There's a chance the Canvas Handle has not changed since last time:
         tmpDC:=FGraphics.GetHDC;
         try
           tmpOk:=tmpDC=Handle;
         finally
           FGraphics.ReleaseHDC(tmpDC);
         end;

         if not tmpOk then
            FreeAndNil(FGraphics);
       end;

    // Now check if FGraphics is nil, recreate it again:
    GetGraphics;
  end;

var tmpDest : TCanvas;
    tmpR : TRect;
begin
  // No longer necessary. FGraphics is always destroyed in "UseBuffer" mode
  (*
  if UseBuffer and Assigned(Bitmap) then
  begin
    RectSize(UserRect,tmpW,tmpH);

    if (tmpW<>Bitmap.Width) or (tmpH<>Bitmap.Height) then
       FreeAndNil(FGraphics);
  end;
  *)

  // Note about TMetafile. Pending to obtain antialias:
  // http://stackoverflow.com/questions/1422949/emf-with-forced-antialiasing
  // using: 'GdipConvertToEmfPlus'

  tmpR:=UserRect;

  if FScale<>1 then
  begin
    tmpR.Right:=Round((tmpR.Right-tmpR.Left)*FScale);
    tmpR.Bottom:=Round((tmpR.Bottom-tmpR.Top)*FScale);
  end;

  result:=inherited InitWindow(DestCanvas,A3DOptions,ABackColor,Is3D,tmpR);

  // Reset origin coordinates
  FX:=0;
  FY:=0;
  FZ:=0;

  CheckGraphics;

  // Reset XOR canvas, so it'll be prepared during all painting process

  // First step, set IXorCanvas ReferenceCanvas depending on our UseBuffer setting:
  if UseBuffer then
  begin
    tmpDest:=Bitmap.Canvas;

    // TeeEraseBack can be used to force clearing the GDI+ background,
    // even it should be already filled at TCustomTeePanelExtended.FillPanelRect
    // during the painting process:

    if TeeEraseBack then
       FGraphics.Clear(ABackColor);
  end
  else
     tmpDest:=DestCanvas;

  // Unfortunately, this cannot be delayed on-demand. It must be done here:
  IXORCanvas.InitWindow(tmpDest,A3DOptions,ABackColor,Is3D,tmpR);

  if Assigned(FTransform) and FTransform.Enabled then
  begin
    FillRect(tmpR); // <-- necessary in this case, with Transform active
    ApplyTransform(FTransform);
  end;
end;

Procedure TGDIPlusCanvas.ShowImage(const DestCanvas,DefaultCanvas:TCanvas; const UserRect:TRect);
var tmp : TGPImage;
begin
  if (FScale>1) and UseBuffer and Assigned(Bitmap) then
  begin
    tmp:=TGPBitmap.Create(Bitmap.Handle,Bitmap.Palette);
    try
      FGraphics.DrawImage(tmp,MakeRect(UserRect));
    finally
      tmp.Free;
    end;
  end;

  inherited;

  // When using a bitmap buffer, recreate Graphics again using Canvas.Handle instead.
  // This allows painting outside the main Paint method (ie: during mouse move events)
  if UseBuffer then
  begin
    FreeAndNil(FGraphics);

    {$IFDEF TEEOCX}
    GetGraphics; //rev. MSOffice 2013
    {$ELSE}
    if View3DOptions.Parent.Parent<>nil then
       RecreateGraphics(Handle);
    {$ENDIF}

    // Reset IXorCanvas to use the control DestCanvas to allow painting over it:
    IXORCanvas.ReferenceCanvas:=DestCanvas;
  end;
end;

Procedure TGDIPlusCanvas.SetUseBuffer(Value:Boolean);
begin
  if UseBuffer<>Value then
  begin
    inherited;
    FreeAndNil(FGraphics);

    // Force a new FGraphics, to allow painting on non-buffered Canvas at any time.
    if Assigned(View3DOptions) and Assigned(ReferenceCanvas) then
       RecreateGraphics(Handle);
  end;
end;

procedure TGDIPlusCanvas.Arc(const Left, Top, Right, Bottom:Integer; const StartAngle,EndAngle:Single);
var tmp : Integer;
Begin
  if HasPen(TeeRect(Left,Top,Right,Bottom)) then
  begin
    if Top>Bottom then tmp:=Bottom else tmp:=Top;

    FGraphics.DrawArc(FGPPen,Left,tmp,Abs(Right-Left),Abs(Bottom-Top),-StartAngle,-(EndAngle-StartAngle))
  end;
End;

procedure TGDIPlusCanvas.ArcTo(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer; const ClockWise:Boolean=False);
var tmpStart,
    tmpEnd : Single;
begin
  if HasPen(TeeRect(X1,Y1,X2,Y2)) then
  begin
    CalcArcAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4,tmpStart,tmpEnd);
    FGraphics.DrawArc(FGPPen,X1,Y1,X2-X1,Y2-Y1,tmpStart,tmpEnd);
  end;
end;

procedure TGDIPlusCanvas.Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer);
var StartAngle,
    EndAngle   : Single;
begin
  if Pen.Style<>psClear then
  begin
    CalcArcAngles(Left,Top,Right,Bottom,StartX,StartY,EndX,EndY,StartAngle,EndAngle);
    Arc(Left,Top,Right,Bottom,StartAngle,EndAngle);
  end;
end;

Procedure TGDIPlusCanvas.ClipEllipse(Const Rect:TRect; Inverted:Boolean=False);
var {$IFDEF D6}
    p : TGPGraphicsPath;
    {$ELSE}
    //Region : HRgn;
    //r : TGPRegion;
    {$ENDIF}

    tmpR : TRect;
begin
  tmpR := TeeRect(Rect.Left,Rect.Top,Rect.Right,Rect.Bottom);

  {$IFNDEF D6}
  // No workaround for BCB5 dcc32 AV compiling TGPGraphicsPath.Create and
  // TGPRegion.Create(Region).

  (*
  Region:=CreateEllipticRgnIndirect(tmpR);

  r:=TGPRegion.Create(Region);
  try
    if Inverted then
       FGraphics.SetClip(r,CombineModeExclude)
    else
       FGraphics.IntersectClip(r);

  finally
    DeleteObject(Region);
    r.Free;
  end;
  *)
  {$ELSE}

  p:=TGPGraphicsPath.Create;
  try
    p.AddEllipse(MakeRect(tmpR));

    PushClipRect;

    if Inverted then
       FGraphics.SetClip(p,CombineModeExclude)
    else
       FGraphics.SetClip(p,CombineModeIntersect);
  finally
    p.Free;
  end;
  {$ENDIF}

//DONT:  inherited;
end;

Procedure TGDIPlusCanvas.ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                                     DiffRegion:Boolean=False);
var Region : HRgn;
    r : TGPRegion;
begin
  Region:=CreatePolygonRgn(Points,NumPoints,ALTERNATE);

  r:=TGPRegion.Create(Region);
  try
    PushClipRect;

    if DiffRegion then
       FGraphics.SetClip(r,CombineModeExclude)
    else
       FGraphics.IntersectClip(r);

  finally
    DeleteObject(Region);
    r.Free;
  end;

//DONT:  inherited;
end;

procedure TGDIPlusCanvas.PushClipRect;
var tmpL : Integer;
begin
  tmpL:=Length(IOldClipRect);
  SetLength(IOldClipRect,tmpL+1);

  FGraphics.GetVisibleClipBounds(IOldClipRect[tmpL]);
end;

procedure TGDIPlusCanvas.ClipRectangle(Const Rect:TRect);
var tmpR : TRect;
begin
  PushClipRect;

  tmpR:=TeeRect(Rect.Left,Rect.Top-1,Rect.Right,Rect.Bottom+1);

  FGraphics.IntersectClip(MakeRect(tmpR));
end;

Procedure TGDIPlusCanvas.ClipRectangle(Const Rect:TRect; RoundX,RoundY:Integer);
var p : TGPGraphicsPath;
begin
  if (RoundX>0) or (RoundY>0) then
  begin
    p:=RoundRectPath(Rect,RoundX,RoundY);
    try
      PushClipRect;

      Graphics.SetClip(p,CombineModeIntersect);
    finally
      p.Free;
    end;
  end
  else
     ClipRectangle(Rect);
end;

procedure TGDIPlusCanvas.DoChangedBrush(const ABrush:TTeeBrush);
begin
  if (ABrush.BackColor=clNone) or (ABrush.Style=bsClear) then
     BackMode:=cbmTransparent
  else
  if ABrush.Style<>bsSolid then
  begin
    BackMode:=cbmOpaque;
    BackColor:=ABrush.BackColor;
  end;
end;

procedure TGDIPlusCanvas.DoChangedPen;
begin
  FreeAndNil(FGPPenFill);
  FreeAndNil(FGPPen);
end;

procedure TGDIPlusCanvas.CreateGPPen(const AColor:Cardinal);
var APen : TTeePen;
begin
  APen:=Pen;

  FGPPen:=TGPPen.Create(AColor);

  with FGPPen do
  begin
    SetWidth(APen.Width);

    if APen.SmallDots then
       SetDashStyle(DashStyleDot)
    else
    begin
      case APen.Style of
            psSolid: SetDashStyle(DashStyleSolid);
             psDash: SetDashStyle(DashStyleDash);
              psDot: SetDashStyle(DashStyleDot);
          psDashDot: SetDashStyle(DashStyleDashDot);
       psDashDotDot: SetDashStyle(DashStyleDashDotDot);
      end;
    end;

    case APen.EndStyle of
        esRound: SetLineCap(LineCapRound, LineCapRound, DashCapRound);
       esSquare: SetLineCap(LineCapSquare, LineCapSquare, DashCapTriangle);
    else
      SetLineCap(LineCapFlat, LineCapFlat, DashCapFlat);
    end;

    case APen.JoinStyle of
      jsRound: SetLineJoin(LineJoinRound);
      jsBevel: SetLineJoin(LineJoinBevel);
    else
         SetLineJoin(LineJoinMiter);
    end;

  end;

  // GdipSetPenDashArray
end;

function TeeRectInflate(const R:TRect; const Off:Single):TRectF; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result.Left:=R.Left-Off;
  result.Top:=R.Top-Off;
  result.Right:=R.Right+Off;
  result.Bottom:=R.Bottom+Off;
end;

function TeeRectInflate(const R:TRectF; const Off:Single):TRectF; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result.Left:=R.Left-Off;
  result.Top:=R.Top-Off;
  result.Right:=R.Right+Off;
  result.Bottom:=R.Bottom+Off;
end;

function TGDIPlusCanvas.HasPen:Boolean;
begin
  result:=Pen.Visible and (Pen.Style<>psClear);

  if result then
  begin
    if not Assigned(FGPPen) then
       CreateGPPen(GDIPColor(Pen.Color));

    if Pen.Width=0 then
       DoSetAnti(False)
    else
       DoSetAnti(FAnti);
  end;
end;

function TGDIPlusCanvas.HasPen(const R:TRect):Boolean;
var tmpR : TRectF;
begin
  result:=HasPen;

  if result and Pen.IsGradientVisible then
  begin
    FGPPenFill.Free;

    tmpR:=TeeRectInflate(R,Pen.Width*0.5);

    if (Pen.Width=0) or ((Pen.Width mod 2)=1) then
       tmpR.Top:=tmpR.Top-1;

    FGPPenFill:=GBrush(Pen.Fill,tmpR);
    FGPPen.SetBrush(FGPPenFill);
  end;
end;

function TGDIPlusCanvas.HasPen(const Bounds:TGPRectF):Boolean;
var R : TRectF;
begin
  result:=HasPen;

  if result and Pen.IsGradientVisible then
  begin
    FGPPenFill.Free;

    R.Left:=Bounds.X;
    R.Top:=Bounds.Y;
    R.Right:=R.Left+Bounds.Width;
    R.Bottom:=R.Top+Bounds.Height;

    FGPPenFill:=GBrush(Pen.Fill,TeeRectInflate(R,Pen.Width*0.5));
    FGPPen.SetBrush(FGPPenFill);
  end;
end;

function RectFToRect(const R:TRectF):TRect;
begin
  with result do
  begin
    Left:=Round(R.Left);
    Top:=Round(R.Top);
    Right:=Round(R.Right);
    Bottom:=Round(R.Bottom);
  end;
end;

function IsEmpty(const R:TRectF):Boolean; overload;
begin
  result:=(R.Left=R.Right) and (R.Top=R.Bottom);
end;

function IsEmpty(const R:TRect):Boolean; overload;
begin
  result:=(R.Left=R.Right) and (R.Top=R.Bottom);
end;

function TGDIPlusCanvas.HasPen(const Points:Array of TPoint; FixCurve:Boolean=False):Boolean;
var tmpR : TRect;
    tmpOff : Single;
begin
  result:=HasPen;

  if result and Pen.IsGradientVisible then
  begin
    FGPPenFill.Free;

    tmpR:=RectFToRect(Pen.Fill.Gradient.CustomTargetRectangle);
    if IsEmpty(tmpR) then
       tmpR:=PolygonBounds(Points);

    if FixCurve then
       tmpOff:=Pen.Width
    else
       tmpOff:=Pen.Width*0.5;

    FGPPenFill:=GBrush(Pen.Fill,TeeRectInflate(tmpR,tmpOff));
    FGPPen.SetBrush(FGPPenFill);
  end
end;

function TGDIPlusCanvas.HasPen(const Points:Array of TPointFloat):Boolean;
var tmpR : TRectF;
begin
  result:=HasPen;

  if result and Pen.IsGradientVisible then
  begin
    FGPPenFill.Free;

    tmpR:=Pen.Fill.Gradient.CustomTargetRectangle;
    if IsEmpty(tmpR) then
       tmpR:=PolygonBounds(Points);

    FGPPenFill:=GBrush(Pen.Fill,TeeRectInflate(tmpR,Pen.Width*0.5));
    FGPPen.SetBrush(FGPPenFill);
  end
end;

procedure TGDIPlusCanvas.DoChangedFont;

  function GetPrivateFont(const AName: String):TGPFontFamily;
  var families: Array of TGPFontFamily;
      found: Integer;
      t: Integer;
      name: String;
      count: Integer;
  begin
     result:=nil;

     if (AName='DS-Digital') or (AName='Elektra') then
     begin
        if not Assigned(IPrivateFonts) then
        begin
          IPrivateFonts:=TGPPrivateFontCollection.Create;
          DoAddFonts;
        end;

        for t := 0 to Length(families) - 1 do
            families[t].Free;

        Finalize(families);

        count:=IPrivateFonts.GetFamilyCount;
        SetLength(families, count);

        for t := 0 to count - 1 do
            families[t]:=TGPFontFamily.Create;

        IPrivateFonts.GetFamilies(count, families, found);

        for t := 0 to count - 1 do
        begin
          families[t].GetFamilyName(name);

          if name=AName then
          begin
            result:=families[t];
            break;
          end;
       end;
     end;
  end;

var tmpFontStyle : TFontStyle;
    tmpPrivateFont: TGPFontFamily;
    tmpSize: Single;
    tmpQuality : TGDIPlusFontQuality;
begin
//  inherited; <-- do not call

  if not Assigned(Font) then
     Exit;

  tmpSize:=Max(0.1,Font.SizeFloat);

  // For Metafile and printer canvas:
  if Metafiling and Assigned(ReferenceCanvas) then
{$IFDEF TEEOCX}
    tmpSize:=(tmpSize*Font.PixelsPerInch)/Graphics.GetDpiX;
{$ELSE}
     tmpSize:=(tmpSize*Font.PixelsPerInch)/ReferenceCanvas.Font.PixelsPerInch;
{$ENDIF}

  // Try to preserve from cache:

  if (not Assigned(FGPFont)) or (Font.Name<>IOldFont.Name) or
     (tmpSize<>IOldFont.Size) or (Font.Style<>IOldFont.Style) then
  begin
    FGPFont.Free;

    tmpFontStyle:=0;

    if Font.Style<>[] then // optimization
    begin
      if fsBold in Font.Style then
         tmpFontStyle:=tmpFontStyle or FontStyleBold;

      if fsItalic in Font.Style then
         tmpFontStyle:=tmpFontStyle or FontStyleItalic;

      if fsUnderline in Font.Style then
         tmpFontStyle:=tmpFontStyle or FontStyleUnderline;

      if fsStrikeOut in Font.Style then
         tmpFontStyle:=tmpFontStyle or FontStyleStrikeout;
    end;

    // Special font (LCD DS-Digital & Elektra fonts)

    tmpPrivateFont:=GetPrivateFont(Font.Name);

    if Assigned(tmpPrivateFont) then
    begin
      try
        FGPFont:=TGPFont.Create(tmpPrivateFont,tmpSize,tmpFontStyle,UnitPoint)
      finally
        FreeAndNil(tmpPrivateFont);
      end;
    end
    else
      FGPFont:=TGPFont.Create(Font.Name,tmpSize,tmpFontStyle,UnitPoint);

    // Cache font:
    IOldFont.Name:=Font.Name;
    IOldFont.Size:=tmpSize;
    IOldFont.Style:=Font.Style;
  end;

  // Text Antialias,
  // when "default": disable for small font sizes < 10 (for better look),
  // set cleartype for sizes up to 20, and set "best" for sizes > 20

  if Assigned(FGraphics) then // <-- Protection
  case Font.Quality of
    fqBest      : SetTextHint(gpfBest);
    fqClearType : SetTextHint(gpfClearType);
    fqNormal    : SetTextHint(gpfNormal);
  else
    begin
      tmpQuality:=GetAntiText;

      if tmpQuality=gpfDefault then
      begin
        if tmpSize<20 then
           SetTextHint(gpfClearType)  // <-- small font sizes look better with "ClearType"
        else
           SetTextHint(gpfBest);  // <-- bigger font sizes look better with "Best"
      end
      else
        SetTextHint(tmpQuality);
    end;
  end;
end;

function TGDIPlusCanvas.ApplyTransparency(const Value:Byte):Byte;
const
  OneInv255=1.0/255.0;
begin
  result:=Round((100-Value)*2.55*Transparency*OneInv255);
end;

function TGDIPlusCanvas.BeginBlending(const R: TRect; Transparency: TTeeTransparency):TTeeBlend;
begin
  PushTransp;
  Self.Transparency:=ApplyTransparency(Transparency);
  result:=nil;
end;

procedure TGDIPlusCanvas.EndBlending(const Blend:TTeeBlend);
begin
  PopTransp;
end;

// These are dummy empty methods (it makes sense only for old GDI)

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

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

Function TGDIPlusCanvas.GetTextAlign:TCanvasTextAlign;
begin
  result:=FTextAlign;
end;

function TGDIPlusCanvas.IsAliased:Boolean;
begin
  Result:=ICachedAntialias;
end;

function TGDIPlusCanvas.CanvasStyle:TCanvasStyle;
begin
  Result:=csGDIPlus;
end;

procedure TGDIPlusCanvas.SetAntiAlias(const Value:Boolean);
begin
  if Assigned(FGraphics) then
     DoSetAntiAlias(FAnti and Value);

  // Force creating FGPPen to make it ready:
  HasPen;
end;

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

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

procedure TGDIPlusCanvas.SetInterCharSize(ASize:Integer);
begin
  FInterCharSize:=ASize;
end;

//procedure SetPenMode(const APen:TTeePen); override;

procedure TGDIPlusCanvas.SetTextAlign(const Value:TCanvasTextAlign);
begin
  FTextAlign:=Value;
end;

function TGDIPlusCanvas.CreatePath(const Points:TPointFloatArray; const Types:TPathPointTypes):TTeeBasePath;
var L : Integer;
    po : PGPPointF;
    ptypes : PByte;
begin
  result:=TTeeGDIPlusPath.Create(Self);

  L:=Length(Points);

  if L>0 then
  begin
    TTeeGDIPlusPath(result).IPath.Free;

    po:=@Points[0];
    ptypes:=@Types[0];

    TTeeGDIPlusPath(result).IPath:=TGPGraphicsPath.Create(po,ptypes,L);
  end;
end;

procedure TGDIPlusCanvas.Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean; const Z:Single);
begin
  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if IsGradientVisible then
    begin
      if Length(IGradientPolygon)>0 then
         FGPBrush:=GBrush(Brush,IGradientPolygon)
      else
         FGPBrush:=GBrush(Brush,Points);
    end
    else
      FGPBrush:=GBrush;

    FGraphics.FillClosedCurve(FGPBrush,PGPPoint(@Points[0]),Length(Points),
                              FillModeAlternate, Tension);
  end;

  if HasPen(Points,True) then
     if Closed then
        FGraphics.DrawClosedCurve(FGPPen,PGPPoint(@Points[0]),Length(Points),Tension)
     else
        FGraphics.DrawCurve(FGPPen,PGPPoint(@Points[0]),Length(Points),
                           Tension);
end;

const SingleOne:Single=1;

Procedure TGDIPlusCanvas.DoHorizLine(const X0,X1,Y:Integer);
begin
  if HasPen(TeeRect(X0,Y,X1,Y)) then
  begin
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(X0,Y,X1,Y)
    else
       FGraphics.DrawLine(FGPPen,SingleOne*X0,Y,X1,Y);
  end;

  FX:=X1;
  FY:=Y;
end;

function TGDIPlusCanvas.DonutPath( XCenter,YCenter,XRadius,YRadius:Integer;
                                   const StartAngle,EndAngle,HolePercent:Double):TGPGraphicsPath;
var tmpStartAngle : Single;
    tmpEndAngle   : Single;
    tmpXRadius    : Single;
    tmpYRadius    : Single;
begin
  tmpStartAngle:=360-StartAngle*HalfDivPi;
  tmpEndAngle:=360-EndAngle*HalfDivPi;

  result:=TGPGraphicsPath.Create;

  if HolePercent>0 then
  begin
    result.AddArc({$IFDEF LCL}1.0*{$ENDIF}XCenter-XRadius,YCenter-YRadius,
                  2*XRadius,2*YRadius,tmpStartAngle,tmpEndAngle-tmpStartAngle);

    tmpXRadius:=HolePercent*XRadius*0.01;
    tmpYRadius:=HolePercent*YRadius*0.01;

    result.AddArc(XCenter-tmpXRadius,YCenter-tmpYRadius,
                  2*tmpXRadius,2*tmpYRadius,tmpEndAngle,tmpStartAngle-tmpEndAngle);
    result.CloseFigure;
  end
  else
    result.AddPie({$IFDEF FPC}1.0*{$ENDIF}XCenter-XRadius,  // <-- resolve overload
                  YCenter-YRadius,
                  2*XRadius,
                  2*YRadius,
                  tmpEndAngle,
                  tmpStartAngle-tmpEndAngle);
end;

procedure TGDIPlusCanvas.Donut( XCenter,YCenter,XRadius,YRadius:Integer;
                                Const StartAngle,EndAngle,HolePercent:Double);
var P    : TGPGraphicsPath;
    tmpR : TRect;
begin
  P:=DonutPath(XCenter,YCenter,XRadius,YRadius,StartAngle,EndAngle,HolePercent);
  try
    tmpR:=RectFromCenter(XCenter,YCenter,XRadius,YRadius);

    if Brush.Style<>bsClear then
    begin
      FGPBrush.Free;
      FGPBrush:=GBrush(tmpR);
      FGraphics.FillPath(FGPBrush,P);
    end;

    if HasPen(tmpR) then
       FGraphics.DrawPath(FGPPen,P);
  finally
    P.Free;
  end;
end;

Procedure TGDIPlusCanvas.DoVertLine(const X,Y0,Y1:Integer);
begin
  if HasPen(TeeRect(X,Y0,X,Y1)) then
  begin
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(X,Y0,X,Y1)
    else
       FGraphics.DrawLine(FGPPen,SingleOne*X,Y0,X,Y1);
  end;

  FX:=X;
  FY:=Y1;
end;

procedure TGDIPlusCanvas.DrawBlendImage(const AImage:TGPImage; const Dest:TGPRect);
var tmpAttr : TGPImageAttributes;
begin
  tmpAttr:=CreateImgAttributes(Transparency);
  FGraphics.DrawImage(AImage,Dest,0,0,AImage.GetWidth,AImage.GetHeight,UnitPixel,tmpAttr);
  tmpAttr.Free;
end;

class procedure TGDIPlusCanvas.ResizeBitmap(const Source,Dest:TBitmap);
var tmpGraphics : TGPGraphics;
    tmpDC       : HDC;
    tmpImage    : TGPImage;
    tmpDestRect : TRect;
begin
  tmpDC:=Dest.Canvas.Handle;

  tmpGraphics:=TGPGraphics.Create(tmpDC);
  try
    tmpImage:=TGPBitmap.Create(Source.Handle, Source.Palette);
    try
      tmpDestRect:=TeeRect(Dest.Width,Dest.Height);

      tmpGraphics.SetInterpolationMode(InterpolationModeHighQuality);

      tmpGraphics.DrawImage(tmpImage,MakeRect(tmpDestRect));
    finally
      tmpImage.Free;
    end;
  finally
    tmpGraphics.Free;
  end;
end;

function BitmapFromIcon(const AIcon:TIcon):TBitmap;
begin
  result:=TBitmap.Create;
  TeeSetBitmapSize(result,AIcon.Width,AIcon.Height);
  DrawIconEx(result.Canvas.Handle, 0,0, AIcon.Handle, 0, 0, 0, 0, DI_NORMAL);
end;

procedure TGDIPlusCanvas.Draw(X,Y:Integer; const AGraphic:TGraphic);

  procedure DoDrawImage(const AImage:TGPImage);
  begin
    if Transparency<255 then
       DrawBlendImage(AImage,MakeRect(X,Y,X+AGraphic.Width,Y+AGraphic.Height))
    else
       FGraphics.DrawImage(AImage,X,Y);
  end;

  procedure DoDraw(const ABitmap:TBitmap);
  var tmp : TGPImage;
  begin
    tmp:=TGPBitmap.Create(ABitmap.Handle,ABitmap.Palette);
    DoDrawImage(tmp);
    tmp.Free;
  end;

  procedure DrawIcon;
  var tmp : TBitmap;
  begin
    tmp:=BitmapFromIcon(TIcon(AGraphic));
    try
      DoDraw(tmp);
    finally
      tmp.Free;
    end;
  end;

  procedure DrawMetafile;
  var tmp : TGPImage;
  begin
    tmp:=TGPMetafile.Create(TMetaFile(AGraphic).Handle,False);
    DoDrawImage(tmp);
    tmp.Free;
  end;

var tmpB : TBitmap;
begin
  if AGraphic is TBitmap then
     DoDraw(TBitmap(AGraphic))
  else
  if AGraphic is TMetafile then
     DrawMetafile
  else
  if AGraphic is TIcon then
     DrawIcon
  else
  begin
    tmpB:=TranspBitmap(AGraphic,X,Y);
    DoDraw(tmpB);
    tmpB.Free;
  end;
end;

{$IFDEF TEECACHEIMAGES}
function TGDIPlusCanvas.FindCachedImage(const AGraphic:TGraphic):TGPImage;
var t : Integer;
begin
  for t:=0 to Length(ICachedImage)-1 do
  if ICachedImage[t].Graphic=AGraphic then
  begin
    result:=ICachedImage[t].Image;
    exit;
  end;

  result:=nil;
end;
{$ENDIF}

procedure TGDIPlusCanvas.StretchDraw(const ARect:TRect; const AGraphic:TGraphic);
{$IFDEF TEECACHEIMAGES}
var
  tmpGP : TGPImage;
{$ENDIF}

  procedure DoDrawImage(const AImage:TGPImage);
  {$IFDEF TEECACHEIMAGES}
  var l : Integer;
  {$ENDIF}
  begin
    if StretchDrawQuality=sqHigh then
       FGraphics.SetInterpolationMode(InterpolationModeHighQuality)
    else
       FGraphics.SetInterpolationMode(InterpolationModeNearestNeighbor);

    if Transparency<255 then
       DrawBlendImage(AImage,MakeRect(ARect))
    else
       FGraphics.DrawImage(AImage,MakeRect(ARect));

    {$IFDEF TEECACHEIMAGES}
    if not Assigned(tmpGP) then // add to cache
    begin
      l:=Length(ICachedImage);
      SetLength(ICachedImage,l+1);
      ICachedImage[l].Graphic:=AGraphic;
      ICachedImage[l].Image:=AImage;
    end;
    {$ENDIF}
  end;

  procedure DoDraw(const ABitmap:TBitmap);
  var tmp : TGPBitmap;
      tmpC : Cardinal;
      x,y : Integer;
      tmpTransp : Cardinal;
  begin
    if ABitmap.Transparent then
    begin
      // Very slow operation.
      // Transparent "ABitmap" should be converted before arriving here.

      tmp:=TGPBitmap.Create(ABitmap.Width, ABitmap.Height);

      if ABitmap.TransparentMode=tmAuto then
         tmpTransp:=ABitmap.Canvas.Pixels[0,ABitmap.Height-1]
      else
         tmpTransp:=(ABitmap.TransparentColor {$IFNDEF FMX}and (not $02000000){$ENDIF});

      for y:=0 to ABitmap.Height-1 do
          for x:=0 to ABitmap.Width-1 do
          begin
            tmpC:=ABitmap.Canvas.Pixels[x,y];

            if tmpC=tmpTransp then
               tmp.SetPixel(x,y,$00FFFFFF)
            else
               tmp.SetPixel(x,y,GDIPColor(tmpC,$FF))
          end;
    end
    else
      tmp:=TGPBitmap.Create(ABitmap.Handle, ABitmap.Palette);

    DoDrawImage(tmp);

    {$IFNDEF TEECACHEIMAGES}
    tmp.Free;
    {$ENDIF}
  end;

  procedure DrawIcon;
  var tmp : TBitmap;
  begin
    tmp:=BitmapFromIcon(TIcon(AGraphic));
    DoDraw(tmp);

    {$IFNDEF TEECACHEIMAGES}
    tmp.Free;
    {$ENDIF}
  end;

  procedure DrawMetafile;
  var tmp : TGPImage;
  begin
    tmp:=TGPMetafile.Create(TMetafile(AGraphic).Handle,False);
    DoDrawImage(tmp);

    {$IFNDEF TEECACHEIMAGES}
    tmp.Free;
    {$ENDIF}
  end;

var tmpB : TBitmap;
begin
  if not Assigned(AGraphic) then
     Exit;
     
  {$IFDEF TEECACHEIMAGES}
  tmpGP:=FindCachedImage(AGraphic);

  if Assigned(tmpGP) then
     DoDrawImage(tmpGP)
  else
  {$ENDIF}

  if AGraphic is TBitmap then
     DoDraw(TBitmap(AGraphic))
  else
  if AGraphic is TMetafile then
     DrawMetafile
  else
  if AGraphic is TIcon then
     DrawIcon
  else
  begin
    tmpB:=TranspBitmap(AGraphic,ARect);
    DoDraw(tmpB);
    tmpB.Free;
  end;
end;

function TeeRect(const R:TRectF):TGPRectF; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=R.Left;
  result.Y:=R.Top;
  result.Width:=R.Right-R.Left;
  result.Height:=R.Bottom-R.Top;
end;

procedure TGDIPlusCanvas.Ellipse(const X1, Y1, X2, Y2: Integer);
var tmpRect : TRectF;
    tmpR    : TRect;
    tmpHasPen : Boolean;
begin
  tmpR:=TeeRect(X1,Y1,X2,Y2);

  tmpHasPen:=HasPen(tmpR);

  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if tmpHasPen and (Pen.Width>1) then
    begin
      tmpRect:=TeeRectInflate(tmpR,-Pen.Width*0.5);
      FGPBrush:=GBrush(Brush,tmpRect);
      FGraphics.FillEllipse(FGPBrush,TeeRect(tmpRect));
    end
    else
    begin
      FGPBrush:=GBrush(tmpR);
      FGraphics.FillEllipse(FGPBrush,X1,Y1,X2-X1,Y2-Y1);
    end;
  end;

  if tmpHasPen then
     if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
     begin
       CheckXorCanvas;

       IXORCanvas.AssignBrush(Brush);
       IXORCanvas.AssignVisiblePen(Pen);
       IXORCanvas.Ellipse(X1,Y1,X2,Y2);
     end
     else
       FGraphics.DrawEllipse(FGPPen,X1,Y1,X2-X1,Y2-Y1);
end;

procedure TGDIPlusCanvas.FillRect(const Rect: TRect);
begin
  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;
    FGPBrush:=GBrush(Rect);

    FGraphics.FillRectangle(FGPBrush,MakeRect(Rect));
  end;
end;

Procedure TGDIPlusCanvas.Line(const X0,Y0,X1,Y1:Integer);
begin
  if HasPen(OrientRectangle(X0,Y0,X1,Y1)) then
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(X0,Y0,X1,Y1)
    else
       Graphics.DrawLine(FGPPen,X0,Y0,X1,Y1);  // not "FGraphics", AV in TeeGauges DrawValueLine

  FX:=X1;
  FY:=Y1;
end;

procedure TGDIPlusCanvas.SetFastLine(const Value:Boolean);
begin
  // Activating "FastLine" means no anti-alias:
  SetAntiAlias(not Value);
end;

procedure TGDIPlusCanvas.FastLineTo(const X,Y:Integer);
begin
  if Assigned(FGPPen) then
     {$IFDEF D16}
     FGraphics.DrawLine(FGPPen,FX,FY,X,Y);
     {$ELSE}
     FGraphics.DrawLineFast(FGPPen,FX,FY,X,Y);
     {$ENDIF}

  FX:=X;
  FY:=Y;
end;

procedure TGDIPlusCanvas.LineTo(const X,Y:Integer);
begin
  if HasPen(TeeRect(FX,FY,X,Y)) then
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(FX,FY,X,Y)
    else
       FGraphics.DrawLine(FGPPen,FX,FY,X,Y);

  FX:=X;
  FY:=Y;
end;

procedure TGDIPlusCanvas.LineTo3D(const X,Y,Z:Integer);
begin
  LineTo(Calculate3DPosition(x,y,z));
end;

procedure TGDIPlusCanvas.MoveTo(const X,Y:Integer);
begin
  FX:=X;
  FY:=Y;
end;

procedure TGDIPlusCanvas.MoveTo3D(const X,Y,Z:Integer);
begin
  FX:=X;
  FY:=Y;
  FZ:=Z;
  Calc3DPos(FX,FY,FZ);
end;

procedure TGDIPlusCanvas.FixAngles(const StartAngle,EndAngle:Double; out AStart,ASweep:Single);
var tmpEnd : Double;
begin
  tmpEnd:=EndAngle;
  if tmpEnd=0 then tmpEnd:=360;

  AStart:=(360-StartAngle);

  if tmpEnd<StartAngle then
     ASweep:=AStart+EndAngle
  else
     ASweep:=(tmpEnd-StartAngle);

  AStart:=AStart-ASweep;
end;

procedure TGDIPlusCanvas.Pie(const R:TRect; const StartAngle,EndAngle:Double);
var
  tmpStart : Single;
  tmpSweep : Single;
  tmpR     : TGPRect;
begin
  FixAngles(StartAngle,EndAngle,tmpStart,tmpSweep);

  tmpR:=MakeRect(R);

  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if IsGradientVisible then
    begin
      if Length(IGradientPolygon)>0 then
         FGPBrush:=GBrush(Brush,IGradientPolygon)
      else
         FGPBrush:=GBrush(R);
    end
    else
      FGPBrush:=GBrush;

    FGraphics.FillPie(FGPBrush,tmpR,tmpStart,tmpSweep);
  end;

  if HasPen(R) then
  Begin
    if tmpSweep <> 360 then
      FGraphics.DrawPie(FGPPen,tmpR,tmpStart,tmpSweep)
    else
      FGraphics.DrawEllipse(FGPPen,tmpR);
  end;
end;

procedure TGDIPlusCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
var StartAngle,
    EndAngle   : Single;
begin
  CalcArcAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4,StartAngle,EndAngle);
  Pie(TeeRect(X1,Y1,X2,Y2),StartAngle,EndAngle);
end;

procedure TGDIPlusCanvas.Path(const P:TGPGraphicsPath);
var tmp : Integer;
    Points : TPointFloatArray;
begin
  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if IsGradientVisible then
    begin
      tmp:=P.GetPointCount;
      SetLength(Points,tmp);

      P.GetPathPoints(PGPPointF(@Points[0]),tmp);

      FGPBrush:=GBrush(Brush,Points);
      Points:=nil;
    end
    else
      FGPBrush:=GBrush;

    FGraphics.FillPath(FGPBrush,P);
  end;

  if HasPen(Points) then
     FGraphics.DrawPath(FGPPen,P);
end;

Procedure TGDIPlusCanvas.Polygon({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
begin
  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if IsGradientVisible then
    begin
      if Length(IGradientPolygon)>0 then
         FGPBrush:=GBrush(Brush,IGradientPolygon)
      else
         FGPBrush:=GBrush(Brush,Points);
    end
    else
      FGPBrush:=GBrush;

    FGraphics.FillPolygon(FGPBrush,PGPPoint(@Points[0]),Length(Points));
  end;

  if HasPen(Points) then
     if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
     begin
       if Assigned(IXORCanvas.ReferenceCanvas) then // <-- fix TeeMaps demo AV at startup
       begin
         IXORCanvas.AssignBrush(Brush);
         IXORCanvas.AssignVisiblePen(Pen);
         IXORCanvas.Polygon(Points);
       end;
     end
     else
        FGraphics.DrawPolygon(FGPPen,PGPPoint(@Points[0]),Length(Points));
end;

procedure TGDIPlusCanvas.PolygonFloat(const Points:Array of TPointFloat);
begin
  if Brush.Style<>bsClear then
  begin
    FGPBrush.Free;

    if IsGradientVisible then
    begin
      if Length(IGradientPolygon)>0 then
         FGPBrush:=GBrush(Brush,IGradientPolygon)
      else
         FGPBrush:=GBrush(Brush,Points);
    end
    else
      FGPBrush:=GBrush;

    FGraphics.FillPolygon(FGPBrush,PGPPointF(@Points[0]),Length(Points));
  end;

  if HasPen(Points) then
     FGraphics.DrawPolygon(FGPPen,PGPPointF(@Points[0]),Length(Points));
end;

Procedure TGDIPlusCanvas.Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
begin
  if HasPen(Points) then
     {$IFDEF D6}
     FGraphics.DrawLines(FGPPen,PGPPoint(@Points[0]),Length(Points));
     {$ENDIF}
end;

procedure TGDIPlusCanvas.PolylineFloat(const Points:Array of TPointFloat);
begin
  if HasPen(Points) then
     FGraphics.DrawLines(FGPPen,PGPPointF(@Points[0]),Length(Points));
end;

procedure TGDIPlusCanvas.DrawSmoothShadow(const P:TGPGraphicsPath; const AColor:Cardinal; const ABalance:Single);
var tmpBrush  : TGPPathGradientBrush;
    tmpColors : Array[0..2] of Cardinal;
    tmpPositions : Array[0..2] of Single;
begin
  if Metafiling then
  begin
    // Disable Transparency (GDI+ with Metafile displays ugly brush patterns)
    tmpColors[0]:=GDIPColor(AColor,255);
    tmpColors[1]:=tmpColors[0];
  end
  else
  begin
    tmpColors[0]:=aclTransparent;
    tmpColors[1]:=GDIPColor(AColor);
  end;

  tmpColors[2]:=tmpColors[1];

  tmpPositions[0]:=0;
  tmpPositions[1]:=ABalance;
  tmpPositions[2]:=1;

  tmpBrush:=TGPPathGradientBrush.Create(p);
  try
    tmpBrush.SetInterpolationColors(@tmpColors,@tmpPositions,3);

    //tmpBrush.SetGammaCorrection(True);
    //tmpBrush.SetBlendBellShape(10,1);

    {$IFNDEF OSX} // Pending: CrossVCL
    Graphics.FillPath(tmpBrush,P);
    {$ENDIF}
  finally
    tmpBrush.Free;
  end;
end;

// Reset IXorCanvas (for example when coming from a Metafile NonBufferDraw call)
procedure TGDIPlusCanvas.SetCanvas(const ACanvas:TCanvas);
begin
  inherited;

  if IsAvailable then
     GetGraphics; // <-- To ensure FGraphics is recreated (for example: we can get here from NonBufferDraw)

  if Assigned(IXORCanvas) then
     IXORCanvas.ReferenceCanvas:=ACanvas;
end;

// Pending: StartAngle:Integer=0, EndAngle:Integer=360; for partial ellipse/donut
procedure TGDIPlusCanvas.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);
var tmpP : TGPGraphicsPath;
    tmpB : TGPBrush;
    tmpStart,
    tmpSweep : Single;
begin
  if Length(P)>0 then
  begin
    tmpP:=TGPGraphicsPath.Create;
    tmpP.AddLines(PGPPoint(@P[0]),Length(P));
    tmpP.CloseFigure;
  end
  else
  if RoundSize<>0 then
     tmpP:=RoundRectPath(Rect,RoundSize,RoundSize)
  else
  if Ellipse then
  begin
    if DonutPercent>0 then
       with Rect do
       tmpP:=DonutPath((Left+Right) div 2,
                       (Top+Bottom) div 2,
                       (Right-Left) div 2,
                       (Bottom-Top) div 2,StartAngle*TeePiStep,EndAngle*TeePiStep,DonutPercent)
    else
    begin
      tmpP:=TGPGraphicsPath.Create;

      with Rect do
      if EndAngle-StartAngle=360 then
         tmpP.AddEllipse(Left,Top,Right-Left,Bottom-Top)
      else
      begin
        FixAngles(StartAngle,EndAngle,tmpStart,tmpSweep);
        tmpP.AddPie(Left,Top,Right-Left,Bottom-Top,tmpStart,tmpSweep);
      end;
    end;
  end
  else
    // Plain Rectangle, set some corner rounding for smooth effect:
    tmpP:=RoundRectPath(Rect,4,4);

  try
    if Shadow.Smooth then
       // Avoid Range Check exception using Max(0,Color):
       DrawSmoothShadow(tmpP,Max(0,Shadow.Color),0.1+Shadow.SmoothBlur*0.002)
    else
    begin
      tmpB:=TGPSolidBrush.Create(GDIPColor(Shadow.Color,ApplyTransparency(Shadow.Transparency)));
      Graphics.FillPath(tmpB,tmpP);
      tmpB.Free;
    end;

  finally
    tmpP.Free;
  end;

 // inherited;
end;

procedure TGDIPlusCanvas.XORRectangle(const R:TRect);
begin
  CheckXorCanvas;

  IXORCanvas.AssignBrush(Brush);
  IXORCanvas.AssignVisiblePen(Pen);
  IXORCanvas.Rectangle(R);
end;

function TeeGPRectInflate(const R:TRect; const Off:Single):TGPRectF; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=R.Left-Off;
  result.Y:=R.Top-Off;
  result.Width:=R.Right+Off-result.X;
  result.Height:=R.Bottom+Off-result.Y;
end;

procedure TGDIPlusCanvas.Rectangle(const X0,Y0,X1,Y1:Integer);
var R: TRect;
    tmpRect : TRectF;
begin
  R:=OrientRectangle(X0,Y0,X1,Y1);

  if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
     XORRectangle(R)
  else
  begin
    if Brush.Style<>bsClear then
    begin
      FGPBrush.Free;

      if Pen.Visible and (Pen.Style<>psClear) and (Pen.Width>1) then
      begin
        tmpRect:=TeeRectInflate(R,-Pen.Width*0.5);
        FGPBrush:=GBrush(Brush,tmpRect);
        FGraphics.FillRectangle(FGPBrush,TeeRect(tmpRect));
      end
      else
      begin
        FGPBrush:=GBrush(R);
        FGraphics.FillRectangle(FGPBrush,R.Left,R.Top,R.Right-R.Left,R.Bottom-R.Top);
      end;
    end;

    if HasPen(R) then
       FGraphics.DrawRectangle(FGPPen,R.Left,R.Top,R.Right-R.Left,R.Bottom-R.Top);
  end;
end;

class function TGDIPlusCanvas.RoundRectPath(R:TRect; x,y:Integer):TGPGraphicsPath;
var dx,dy : Integer;
    offX,offY : Integer;
begin
  offX := X div 2;
  offY := Y div 2;

  X:=offX*2;
  if X<1 then X:=1;

  Y:=offY*2;
  if Y<1 then Y:=1;

  result:=TGPGraphicsPath.Create;

  dx := (R.Right-R.Left);
  dy := (R.Bottom-R.Top);

  if X>Abs(dx) then
     X:=Abs(dx);

  if Y>Abs(dy) then
     Y:=Abs(dy);

  result.AddArc(R.Right-X, R.Top, x, y, 270, 90);
  result.AddArc(R.Right-X, R.Bottom-Y, x, y, 0, 90);
  result.AddArc(R.Left, R.Bottom-Y, x, y, 90, 90);
  result.AddArc(R.Left, R.Top, x, y, 180, 90);

  result.CloseFigure;
end;

procedure TGDIPlusCanvas.RoundRect(X1,Y1,X2,Y2,X3,Y3:Integer);
var tmp : TGPGraphicsPath;
    R   : TRect;
begin
  R:=TeeRect(X1,Y1,X2,Y2);

  if not FAnti then
  begin
    Dec(R.Right);
    Dec(R.Bottom);
  end;

  tmp:=RoundRectPath(R,x3,y3);
  try
    if Brush.Style<>bsClear then
    begin
      FGPBrush.Free;
      FGPBrush:=GBrush(R);
      FGraphics.FillPath(FGPBrush,tmp);
    end;

    if HasPen(R) then
       FGraphics.DrawPath(FGPPen,tmp);
  finally
    tmp.Free;
  end;
end;

procedure TGDIPlusCanvas.RoundRectWithZ(const R:TRect; X,Y,Z:Integer);
begin
  if View3DOptions.Orthogonal then
     RoundRect(CalcRect3D(R,Z),X,Y)
  else
     RectangleWithZ(R,Z);
end;

Procedure TGDIPlusCanvas.TextOut(X,Y:Integer; const Text:String);
begin
  TextOut(1.0*X,Y,Text);
end;

function TGDIPlusCanvas.CalcTextRect(const Origin:TGPPointF; const Text:String):TRect;
var tmp : TPointFloat;
begin
  tmp:=TextSize(Text);
  result:=TeeRect(Round(Origin.X),Round(Origin.Y),Round(Origin.X+tmp.X),Round(Origin.Y+tmp.Y));
end;

function TGDIPlusCanvas.TextBrush(const AColor:TColor; const Origin:TGPPointF; const Text:String):TGPBrush;
begin
  Brush.Color:=AColor;

  if IsGradientVisible then
     result:=GBrush(CalcTextRect(Origin,Text))
  else
     result:=GBrush;
end;

Procedure TGDIPlusCanvas.TextOut(X,Y:Single; const Text:String);
var
  Origin : TGPPointF;

  procedure InternalTextOut(const P: TGPPointF;
                            const AColor:TColor);
  begin
    FGPBrush.Free;
    FGPBrush:=TextBrush(AColor,Origin,Text);

    {$IFDEF D12}
    FGraphics.DrawString(Text,ElementToCharIndex(Text,Length(Text)),FGPFont,P,FGPBrush);
    {$ELSE}
    if SysLocale.FarEast then
       FGraphics.DrawString(Text,ByteToCharIndex(Text,Length(Text)),FGPFont,P,FGPBrush)
    else
       FGraphics.DrawString(Text,Length(Text),FGPFont,P,FGPBrush);

// DEBUG:
//    FGraphics.DrawRectangle(FGPPen,X,Y,TextWidth(Text),TextHeight(Text));

    {$ENDIF}
  end;

  procedure TryDrawShadow(const AShadow:TTeeShadow; AFactor:Integer);
  var tmpPoint : TGPPointF;
      tmpRect  : TRect;
      horiz, vert : Integer;

    procedure GetShadowSize(out H,V:Integer);
    begin
      H:=AShadow.HorizSize;
      V:=AShadow.VertSize;

      case AShadow.Direction of
        sdLeftTop    : begin H:=-H; V:=-V; end;
        sdLeftBottom : H:=-H;
        sdRightTop   : V:=-V;
      end;
    end;

    function GetOffsetDirection(Val : Integer) : Integer;
    Begin
      if Val < 0 then
        Result := 2 * Val-3
      else
        Result := 2 * Val+3;
    end;

  begin
    if Assigned(AShadow) then
    with AShadow do
    if Visible and (Size<>0) then
    begin
      GetShadowSize(horiz,vert);
      tmpPoint:=Origin;
      tmpPoint.X:=tmpPoint.X+horiz;
      tmpPoint.Y:=tmpPoint.Y+vert;

      Gradient.Visible:=False;

      InternalTextOut(tmpPoint,Color);

      if Smooth and (SmoothBlur<>0) and Assigned(Bitmap) then
      begin
        // Pending Alternative: Use IGdipBlur (IGdipEffect)

        with TBlurFilter.Create(nil) do
        try
          RGBA:=True;
          Steps:=SmoothBlur;
          tmpRect:=CalcTextRect(Origin,Text);
          OffsetRect(tmpRect,AFactor*(horiz),AFactor*(vert));
          Apply(Bitmap,tmpRect);
        finally;
          Free;
        end;
      end;
    end;
  end;

  procedure DrawOutline;
  var outPath : TGPGraphicsPath;
      tmpFamily : TGpFontFamily;
      tmpBounds: TGPRectF;
  begin
    outPath:=TGPGraphicsPath.Create;
    try
      tmpFamily:=TGPFontFamily.Create;

      FGPFont.GetFamily(tmpFamily);

      outPath.AddString(Text, Length(Text), tmpFamily,
        FGPFont.GetStyle,
        FGPFont.GetSize* Graphics.GetDpiY/72,
        Origin,
        nil);

      AssignVisiblePen(Font.OutLine);

      outPath.GetBounds(tmpBounds);

      if HasPen(tmpBounds) then
         Graphics.DrawPath(FGPPen, outPath);

      FGPBrush.Free;
      FGPBrush:=TextBrush(Font.Color,Origin,Text);
      FGraphics.FillPath(FGPBrush, outPath);

      tmpFamily.Free;
    finally
      outPath.Free;
    end;
  end;

var
  matrix  : TGPMatrix;
  point   : TGPPointF;
  tmp     : TCanvasTextAlign;
  tmpBack : TCanvasBackMode;
  tmpP    : TPointFloat;

  {$IFDEF TEECANVASDEBUG}
  R : TGPRectF;
  {$ENDIF}
begin
  tmp:=TextAlign;

  if tmp=TA_RIGHT then
     X:=X-InternalTextWidth(Text,True)
  else
  if tmp=TA_CENTER then
     X:=X-(0.5*InternalTextWidth(Text,True))
  else
  if tmp=TA_CENTER+TA_BOTTOM then
  begin
    tmpP:=TextSize(Text);

    X:=X-tmpP.X*0.5;
    Y:=Y-tmpP.Y;
  end;

  // DEBUG:
  //FGraphics.DrawLine(FGPPen,X,Y,X+TextWidth(Text),Y);

  Origin.X:=X;
  Origin.Y:=Y;

  matrix:=nil;

  if (ITextRotation<>0) and (ITextRotation<>360) then
  begin
    matrix:=TGPMatrix.Create;

    if IUseTextCenter then
    begin
      point.X:=ITextCenter.X+1;
      point.Y:=ITextCenter.Y;
    end
    else
    if tmp=TA_CENTER then
    begin
      point.X:=X+TextWidth(Text)*0.5;
      point.Y:=Y;
    end
    else
    begin
      point.X:=X;
      point.Y:=Y;
    end;

    if FGraphics.GetTransform(matrix) = Ok then
       if matrix.RotateAt(ITextRotation,point) = Ok then
          FGraphics.MultiplyTransform(matrix);
  end;

  tmpBack:=BackMode;

  if BackMode<>cbmTransparent then
     FillRect(CalcTextRect(Origin,Text));

  TryDrawShadow(Font.Shadow,1);
  TryDrawShadow(Font.Emboss,-1);

  Brush.Assign(Font.Brush);

  if Font.OutLine.Visible then
     DrawOutline
  else
     InternalTextOut(Origin,Font.Color);

  Gradient.Visible:=False;
  Brush.Image:=nil;

  BackMode:=tmpBack;

  if Assigned(matrix) then
     if FGraphics.ResetTransform = Ok then
        FreeAndNil(matrix);

  {$IFDEF TEECANVASDEBUG}
  Pen.Color:=clRed;
  Pen.Style:=psSolid;
  Brush.Style:=bsClear;
  R.X:=X;
  R.Y:=Y;

  with TextSize(Text) do
  begin
    R.Width:=X;
    R.Height:=Y;
  end;

  if HasPen(R) then
     FGraphics.DrawRectangle(FGPPen,R);
  {$ENDIF}
end;

Procedure TGDIPlusCanvas.TextOut3D(x,y,z:Integer; const Text:String; AllowHTML:boolean);
begin
  Calc3DPos(x,y,z);

  {
  if AllowHtml then
     HtmlTextOut(Self,x,y,Text)
  else
  }
     TextOut(X,Y,Text);
end;

procedure TGDIPlusCanvas.RotateLabel(x,y:Integer; const St:String; RotDegree:Double;
                                     AllowHTML:Boolean);
begin
  ITextRotation:=360-RotDegree;

  if AllowHtml then
     HtmlTextOut(Self,x,y,St)
  else
     TextOut(X,Y,St);

  ITextRotation:=0;
end;

type
  TFontAccess=class(TTeeFont);

const
  TeeZeroPoint:TGPPointF=(X:0; Y:0);

Function TGDIPlusCanvas.TextSize(const St:String):TPointFloat;

  procedure ApplyShadow(const AShadow:TTeeShadow);
  begin
    if Assigned(AShadow) and AShadow.Visible then
    begin
      result.X:=result.X+Abs(AShadow.HorizSize);
      result.Y:=result.Y+Abs(AShadow.VertSize);
    end;
  end;

var tmpBox : TGPRectF;
begin
  FGraphics.MeasureString(St,Length(St),FGPFont,TeeZeroPoint, {TGPStringFormat.GenericTypographic,} tmpBox);
  result.X:=tmpBox.Width;
  result.Y:=tmpBox.Height;

  ApplyShadow(TFontAccess(Font).FShadow);
  ApplyShadow(TFontAccess(Font).FEmboss);
end;

Function TGDIPlusCanvas.InternalTextWidth(const St:String; TypoGraphic:Boolean=False):Integer;

  procedure ApplyShadow(const AShadow:TTeeShadow);
  begin
    if Assigned(AShadow) and AShadow.Visible then
       Inc(result,Abs(AShadow.HorizSize));
  end;

var tmpBox : TGPRectF;
begin
  if TypoGraphic then
     FGraphics.MeasureString(St,Length(St),FGPFont,TeeZeroPoint,TGPStringFormat.GenericTypographic,tmpBox)
  else
     FGraphics.MeasureString(St,Length(St),FGPFont,TeeZeroPoint,tmpBox);

  result:=Round(tmpBox.Width{+0.5});

  ApplyShadow(TFontAccess(Font).FShadow);
  ApplyShadow(TFontAccess(Font).FEmboss);
end;

Function TGDIPlusCanvas.TextWidth(const St:String):Integer;
begin
  result:=InternalTextWidth(St);
end;

Function TGDIPlusCanvas.TextHeight(const St:String):Integer;

  procedure ApplyShadow(const AShadow:TTeeShadow);
  begin
    if Assigned(AShadow) and AShadow.Visible then
       Inc(result,Abs(AShadow.VertSize));
  end;

var tmpBox : TGPRectF;
begin
  FGraphics.MeasureString(St,Length(St),FGPFont,TeeZeroPoint, {TGPStringFormat.GenericTypographic,} tmpBox);
  result:=Round(tmpBox.Height+0.5);

  ApplyShadow(TFontAccess(Font).FShadow);
  ApplyShadow(TFontAccess(Font).FEmboss);
end;

Procedure TGDIPlusCanvas.HorizLine3D(Left,Right,Y,Z:Integer);
var tmpY : Integer;
begin
  tmpY:=Y;
  Calc3DPos(Left,tmpY,Z);
  Calc3DPos(Right,Y,Z);

  if HasPen(TeeRect(Left,tmpY,Right,tmpY)) then
  begin
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(Left,tmpY,Right,Y)
    else
       FGraphics.DrawLine(FGPPen,Left,tmpY,Right,Y);
  end;

  FX:=Right;
  FY:=Y;
end;

Procedure TGDIPlusCanvas.LineWithZ(X0,Y0,X1,Y1,Z:Integer);
begin
  Calc3DPos(X0,Y0,Z);
  Calc3DPos(X1,Y1,Z);

  if HasPen(OrientRectangle(X0,Y0,X1,Y1)) then
     FGraphics.DrawLine(FGPPen,X0,Y0,X1,Y1);

  FX:=X1;
  FY:=Y1;
end;

procedure TGDIPlusCanvas.UnClipRectangle;
var tmpL : Integer;
begin
  tmpL:=High(IOldClipRect);

  if tmpL>-1 then
  begin
    FGraphics.SetClip(IOldClipRect[tmpL]);
    SetLength(IOldClipRect,tmpL);
  end
  else
    FGraphics.ResetClip;
end;

// Special case. For example Gauge series at gallery changes its Value, forcing
// a call to draw the gauge line, which can end up here, *before* the chart has
// been displayed at least once.
procedure TGDIPlusCanvas.CheckXorCanvas;
begin
  if not Assigned(IXorCanvas.ReferenceCanvas) then
     IXorCanvas.ReferenceCanvas:=ReferenceCanvas;
end;

Procedure TGDIPlusCanvas.XORLine(const X0,Y0,X1,Y1:Integer);
begin
  CheckXorCanvas;
  IXORCanvas.AssignVisiblePen(Pen);
  IXORCanvas.Line(X0,Y0,X1,Y1);
end;

Procedure TGDIPlusCanvas.VertLine3D(X,Top,Bottom,Z:Integer);
var tmpX : Integer;
begin
  tmpX:=X;
  Calc3DPos(tmpX,Top,Z);
  Calc3DPos(X,Bottom,Z);

  if HasPen(TeeRect(tmpX,Top,X,Bottom)) then
  begin
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(tmpX,Top,X,Bottom)
    else
       FGraphics.DrawLine(FGPPen,tmpX,Top,X,Bottom);
  end;

  FX:=X;
  FY:=Bottom;
end;

Procedure TGDIPlusCanvas.ZLine3D(X,Y,Z0,Z1:Integer);
var tmpX : Integer;
    tmpY : Integer;
begin
  tmpX:=X;
  tmpY:=Y;
  Calc3DPos(tmpX,tmpY,Z0);
  Calc3DPos(X,Y,Z1);

  if HasPen(TeeRect(tmpX,tmpY,X,Y)) then
    if (Pen.Mode=pmXor) or (Pen.Mode=pmNotXor) then
       XORLine(tmpX,tmpY,X,Y)
    else
       FGraphics.DrawLine(FGPPen,tmpX,tmpY,X,Y);

  FX:=X;
  FY:=Y;
end;

function InvertedGradient(const AGradient:TTeeGradient):Boolean;
begin
  case AGradient.Direction of
    gdRightLeft,
    gdBottomTop,
    gdFromBottomLeft,
    gdDiagonalDown  : result:=True;
  else
    result:=False;
  end;
end;

function TGDIPlusCanvas.GradientToColor(const AColor:TTeeGradientColor):Cardinal;
begin
  with AColor do
       result:=TGDIPlusCanvas.GDIPColor(Color,ApplyTransparency(Transparency));
end;

procedure TGDIPlusCanvas.GetGradientStartEnd(const AGradient:TTeeGradient; out Color0,Color1:TGPColor);
var L : Integer;
begin
  L:=AGradient.Colors.Count-1;

  if InvertedGradient(AGradient) then
  begin
    Color0:=GradientToColor(AGradient.Colors[0]);
    Color1:=GradientToColor(AGradient.Colors[L]);
  end
  else
  begin
    Color0:=GradientToColor(AGradient.Colors[L]);
    Color1:=GradientToColor(AGradient.Colors[0]);
  end;
end;

function TGDIPlusCanvas.GetColorBlend(const AGradient:TTeeGradient; out ACount:Integer):TColorBlend;
var t : Integer;
    tmp : TTeeGradientColor;
begin
  ACount:=AGradient.Colors.Count;

  if InvertedGradient(AGradient) then
    for t:=0 to ACount-1 do
    begin
      tmp:=AGradient.Colors[t];
      IBlendPositions[t]:=tmp.Offset;
      IBlendColors[t]:=GradientToColor(tmp);
    end
  else
    for t:=0 to ACount-1 do
    begin
      tmp:=AGradient.Colors[ACount-t-1];
      IBlendPositions[t]:=1-tmp.Offset;
      IBlendColors[t]:=GradientToColor(tmp);
    end;

  result.Colors:=@IBlendColors;
  result.Positions:=@IBlendPositions;
end;

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

Function TGDIPlusCanvas.GetSupportsXORMode:Boolean;
begin
  result:=True;
end;

// If we are inside a DLL library, gdiPlusToken might be zero and GDIPAPI
// TGPGraphics instance will raise an Access Violation.
// The solution for dlls is, for the caller EXE, to initialize GDI+ like:
(*
begin
  StartupInput.DebugEventCallback := nil;
  StartupInput.SuppressBackgroundThread := False;
  StartupInput.SuppressExternalCodecs   := False;
  StartupInput.GdiplusVersion := 1;

  GdiplusStartup(gdiplusToken, @StartupInput, nil);
end.
*)

// Trick: Try to create a solid brush.
// If it does not succeed, it means GDI+ dll has not been initialized.
class function TGDIPlusCanvas.IsAvailable:Boolean;
var tmp : GPBrush;
begin
  result:={$IFDEF D20}(@GdipCreateSolidFill<>nil){$ELSE}Assigned(@GdipCreateSolidFill){$ENDIF} and
          (GdipCreateSolidFill(0,tmp)<>GdiplusNotInitialized);

  if result then
     GdipDeleteBrush(tmp)
end;

{ TTeeGDIPlus }

Constructor TTeeGDIPlus.Create(AOwner:TComponent);
begin
  inherited;

  FAntialias:=True;
  FAntiAliasText:=gpfDefault;
  FScale:=1;
end;

Destructor TTeeGDIPlus.Destroy;
begin
  RemoveTag;
  TeePanel:=nil;
  inherited;
end;

Function TTeeGDIPlus.GetCanvas:TGDIPlusCanvas;
begin
  if Assigned(FTeePanel) and (FTeePanel.Canvas is TGDIPlusCanvas) then
     result:=TGDIPlusCanvas(FTeePanel.Canvas)
  else
     result:=nil;
end;

type
  TCanvasAccess=class(TTeeCanvas3D);

Procedure TTeeGDIPlus.Activate;
var tmpCanvas : TGDIPlusCanvas;
begin
  if Assigned(FTeePanel) then
    if FActive then
    begin
      tmpCanvas:=TGDIPlusCanvas.Create;

      tmpCanvas.UseBuffer:=FTeePanel.Canvas.UseBuffer;

      FTeePanel.Canvas:=tmpCanvas;

      ResetCanvas;

      TCanvasAccess(tmpCanvas).Tag:=Self;

      tmpCanvas.GetGraphics;
    end
    else
    begin
      RemoveTag;

      if FTeePanel.Canvas is TGDIPlusCanvas then
         FTeePanel.Canvas:=TTeeCanvas3D.Create;
    end;
end;

Procedure TTeeGDIPlus.SetActive(const Value:Boolean);
begin
  if FActive<>Value then
  begin
    FActive:=Value;
    Activate;
  end;
end;

Procedure TTeeGDIPlus.SetScale(const Value:Single);
begin
  if FScale<>Value then
  begin
    FScale:=Value;
    ResetCanvas;
  end;
end;

function TTeeGDIPlus.IsScaleStored:Boolean;
begin
  result:=FScale<>1;
end;

procedure TTeeGDIPlus.RemoveTag;
begin
  if Assigned(FTeePanel) and (FTeePanel.Canvas is TGDIPlusCanvas) then
     with TCanvasAccess(FTeePanel.Canvas) do
     if Tag=Self then
        Tag:=nil;
end;

Procedure TTeeGDIPlus.ResetCanvas;
begin
  if Assigned(FTeePanel) and (FTeePanel.Canvas is TGDIPlusCanvas) then
  begin
    Canvas.AntiAlias:=FAntiAlias;
    Canvas.AntiAliasText:=FAntiAliasText;
    Canvas.Scale:=FScale;
  end;
end;

Procedure TTeeGDIPlus.SetAntialias(const Value:Boolean);
begin
  if FAntialias<>Value then
  begin
    FAntialias:=Value;
    ResetCanvas;
  end;
end;

Procedure TTeeGDIPlus.SetAntialiasText(const Value:TGDIPlusFontQuality);
begin
  if FAntialiasText<>Value then
  begin
    FAntialiasText:=Value;
    ResetCanvas;
  end;
end;

Procedure TTeeGDIPlus.SetTeePanel(const Value:TCustomTeePanel);
begin
  if FTeePanel<>Value then
  begin
    if Assigned(FTeePanel) then
    begin
      FTeePanel.RemoveFreeNotification(Self);

      if FTeePanel.Canvas is TGDIPlusCanvas then
         FTeePanel.Canvas:=TTeeCanvas3D.Create;
    end;

    FTeePanel:=Value;

    if Assigned(FTeePanel) then
       FTeePanel.FreeNotification(Self)
    else
       FActive:=False;

    Activate;
  end;
end;

procedure TTeeGDIPlus.Notification( AComponent: TComponent;
                            Operation: TOperation);
begin
  inherited;

  if (Operation=opRemove) and Assigned(FTeePanel) and (AComponent=FTeePanel) then
     TeePanel:=nil;
end;

{ TTeeGDIPlusPath }

Constructor TTeeGDIPlusPath.Create(const ACanvas:TGDIPlusCanvas);
begin
  inherited Create;

  ICanvas:=ACanvas;
  IPath:=TGPGraphicsPath.Create;
end;

Destructor TTeeGDIPlusPath.Destroy;
begin
  IPath.Free;
  inherited;
end;

procedure TTeeGDIPlusPath.ArcTo(const X,Y,Width,Height,StartAngle,SweepAngle:Single);
begin
  IPath.AddArc(X,Y,Width,Height,StartAngle,SweepAngle);
end;

procedure TTeeGDIPlusPath.Close;
begin
  IPath.CloseFigure;
end;

procedure TTeeGDIPlusPath.Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean);
begin
  if Closed then
     IPath.AddClosedCurve(PGPPoint(@Points[0]),Length(Points),Tension)
  else
     IPath.AddCurve(PGPPoint(@Points[0]),Length(Points),Tension);

  with Points[Length(Points)-1] do
  begin
    Self.FX:=X;
    Self.FY:=Y;
  end;
end;

procedure TTeeGDIPlusPath.Draw(const ACanvas: TTeeCanvas);
begin
  TGDIPlusCanvas(ACanvas).Path(IPath);
end;

procedure TTeeGDIPlusPath.GetPoints(out Types:TPathPointTypes; out Points:TPointFloatArray; out Bounds:TRectF);
var C : Integer;
    Point0 : PGPPointF;
    PTypes : PByte;
    tmpR : TGPRectF;
begin
  C:=IPath.GetPointCount;

  if C>0 then
  begin
    SetLength(Points,C);
    Point0:=@Points[0];

    SetLength(Types,C);
    PTypes:=@Types[0];

    IPath.GetPathPoints(Point0,C);
    IPath.GetPathTypes(PTypes,C);
  end;

  IPath.GetBounds(tmpR);

  Bounds.Left:=tmpR.X;
  Bounds.Top:=tmpR.Y;
  Bounds.Right:=tmpR.X+tmpR.Width;
  Bounds.Bottom:=tmpR.Y+tmpR.Height;
end;

procedure TTeeGDIPlusPath.LineTo(const X, Y: Single);
begin
  IPath.AddLine(FX,FY,X,Y);
  FX:=X;
  FY:=Y;
end;

procedure TTeeGDIPlusPath.MoveTo(const X, Y: Single);
begin
  FX:=X;
  FY:=Y;
end;

procedure TTeeGDIPlusPath.TextOut(const X,Y:Single; const Text:String);
var tmp : TGPFontFamily;
begin
  tmp:=TGPFontFamily.Create;
  try
    ICanvas.FGPFont.GetFamily(tmp);
    IPath.AddString(Text,Length(Text),tmp,0,-ICanvas.Font.Height {ICanvas.FGPFont.GetSize*(ICanvas.FGraphics)},
          MakePoint(X,Y),nil);
  finally
    tmp.Free;
  end;
end;

initialization
  TeeActivateGroup;

  {$IFDEF D6}
  {$IFDEF FMX}
  GroupDescendentsWith(TTeeGDIPlus,TBaseObject);
  {$ELSE}
  GroupDescendentsWith(TTeeGDIPlus,TControl);
  {$ENDIF}
  {$ENDIF}

  RegisterClass(TTeeGDIPlus);

  TeeRenderClasses.Add(TGDIPlusCanvas);
finalization
  TeeRenderClasses.Remove(TGDIPlusCanvas);
end.

