{**********************************************}
{   TeeChart and TeeTree TCanvas3D component   }
{                                              }
{   Copyright (c) 1999-2020 by Steema Software }
{        All Rights Reserved                   }
{**********************************************}
unit TeCanvas;
{$I TeeDefs.inc}

interface

{$IFDEF FPC}
// FPC 2.5.1 for iOS, InternalError 200602041 at:
// http://src.gnu-darwin.org/ports/lang/fpc/work/fpc-2.2.0/compiler/dbgdwarf.pas.html
//{$D-}
{$ENDIF}

{$DEFINE TEEWINDOWS}
{$DEFINE PIEFLOATS}
{$DEFINE PIELIST}

{$IFDEF FMX}
{$IFDEF D17} // XE3 and up
{$DEFINE TEECACHEDLAYOUT} // 4x speedup on mobile.
{$ENDIF}
{$ENDIF}

// Unicode support from D6 and up,

{$IFNDEF D12}
{$IFDEF BCB}
 {$IFDEF D10}
   {$DEFINE TEEUNICODE}
 {$ENDIF}
{$ELSE}
  {$IFDEF D6}
   {$DEFINE TEEUNICODE}
  {$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFDEF FMX}
{$UNDEF TEEUNICODE}
{$ENDIF}

{$IFNDEF LCL}
{$DEFINE IGNOREPALETTE}
{$ENDIF}

{$IFNDEF LCL}
{$IFDEF TEEVCL}
{$DEFINE USESYSLOCALE}
{$ENDIF}
{$ENDIF}

{$IFDEF FMX}

{$IFDEF D21}
{$IFDEF MSWINDOWS}
{$DEFINE HASCANVASD2D}
{$DEFINE HASCANVASGDIP}
{$ENDIF}
{$ENDIF}

{$IFDEF D18}
{$IFNDEF MACOS64}
{$DEFINE HASCANVASGPU}
{$ENDIF}
{$ENDIF}

{$IFDEF D24}
{$IFDEF MACOS}
{$IFNDEF IOS}
{$DEFINE HASCANVASMAC}
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFDEF D25}
{$IFDEF ANDROID}
{$DEFINE HASCANVASANDROID}
{$ENDIF}
{$ENDIF}

{$ENDIF}

Uses {$IFNDEF LINUX}
     {$IFDEF TEEWINDOWS}
     {$IFNDEF FMX}
     Windows,
     {$IFDEF TEEVCL}
     Messages,
     {$ENDIF}
     {$ENDIF}
     {$ENDIF}
     {$ENDIF}

     {$IFDEF TEEVCL}
     Classes, SysUtils,
     {$ENDIF}

     {$IFDEF D18}
     System.Generics.Collections,
     {$ENDIF}

     {$IFDEF D20}
     System.Math.Vectors,
     {$ENDIF}

     {$IFDEF FMX}
       FMX.Types, FMX.Controls,
       {$IFDEF D18}
       FMX.StdCtrls,
       {$ENDIF}
       FMX.ListBox, FMX.Edit,
       Types, System.UITypes, System.UIConsts,

       {$IFDEF D161}
         FMX.Platform,
       {$ENDIF}

       {$IFDEF D20}
         FMX.Platform, // IFMXSystemFontService
       {$ENDIF}

       {$IFDEF D19}
       FMX.Graphics,
       {$ENDIF}

       {$IFDEF TEECACHEDLAYOUT}
       {$IFDEF D18}
       FMX.TextLayout,
       {$ELSE}
       FMX.Text,
       {$ENDIF}
       {$ENDIF}

     {$ELSE}

     {$IFDEF D16}
     System.UITypes,
     {$ENDIF}

     Controls, StdCtrls, Graphics, Dialogs,

     {$IFDEF D6}
     Types,
     {$ENDIF}

     {$ENDIF}

     {$IFNDEF FMX}
     {$IFDEF TEEVCL}
     Forms,
     {$ENDIF}
     {$ENDIF}

     {$IFDEF BLOCKS}
     Interfaces,
     {$ENDIF}

     {$IFDEF LCL}
     LMessages, LCLType,
     {$ENDIF}

     TypInfo
     {$IFDEF AXUNICODE}
     , TeeUnicode
     {$ENDIF}
     ;

type
{$IFDEF FMX}
  TPoint=TPointF;
  TPointFloat=TPointF;
  TRect=TRectF;
{$ELSE}
{$IFDEF D16}
  TRectF=System.Types.TRectF;
{$ELSE}
  TRectF=record
    Left,
    Top,
    Right,
    Bottom : Single;
  end;
{$ENDIF}
{$ENDIF}

  TDoubleArray=Array of Double;


Const
  TeePiStep:Double      = Pi/180.0;
  {$NODEFINE TeePiStep}  { for C++ Builder }

  HalfDivPi             = 180/Pi;
  {$NODEFINE HalfDivPi}  { for C++ Builder }

  TeeDefaultPerspective = 15;
  TeeMinAngle           = 270;
  TeeCheckBoxSize=11; { for TChart Legend }

  {$IFNDEF D6}
  clMoneyGreen = TColor($C0DCC0);
  clSkyBlue    = TColor($F0CAA6);
  clCream      = TColor($F0FBFF);
  clMedGray    = TColor($A4A0A0);
  {$ENDIF}

  {$IFNDEF TEEWINDOWS}
  DEFAULT_CHARSET = 1;
  ANTIALIASED_QUALITY = 4;
  TA_LEFT = 0;

  bs_Solid=0;
  {$ENDIF}

  {$IFNDEF TEEVCL}
  clGray=0;
  clDkGray=0;
  clWhite=$FFFFFF;
  clSilver=0;
  clDefault=-1;
  clNone=-2;
  clBlack=0;
  clYellow=$FFFF;

  pf24Bit=0;
  pfDevice=1;

  CM_MOUSELEAVE=10000;
  CM_SYSCOLORCHANGE=10001;
  {$ENDIF}

  TeeZeroRect  : TRect=( Left:0; Top:0; Right:0; Bottom:0 );
  TeeZeroRectF : TRectF=( Left:0; Top:0; Right:0; Bottom:0 );

  TeeZeroPoint : TPoint = (x: 0; y: 0);

  {$IFDEF LCL}
  DC_BRUSH = 18;
  DC_PEN = 19;
  {$ENDIF}

  TeeFontElektra='Elektra';  // Do not localize
  TeeFontDigital='DS-Digital';  // Do not localize

var
  TeeDefaultConePercent : Integer=0;
  NumCirclePoints :Integer = 180;

  TeeCheckPenWidth      : Boolean=False; { Fix for HP Laserjet printers bug }
  {$IFDEF TEEOCX}
  ColorPickerClosed     : Boolean=True;
  {$ENDIF}

type
  {$IFNDEF TEEWINDOWS}
     TPoint=packed record
       X,Y: Integer;
     end;

     TRect = packed record
       case Integer of
         0: (Left, Top, Right, Bottom: Integer);
         1: (TopLeft, BottomRight: TPoint);
     end;

     HDC=Integer;
     HPen=Integer;
     HRgn=Integer;

     COLORREF = LongWord;

     TLogBrush=packed record
       lbStyle:Integer;
       lbColor:TColor;
       lbHatch:Integer;
     end;
  {$ENDIF}

  {$IFNDEF TEEVCL}
     {$DEFINE TEECANVASLOCKS}

     TNotifyEvent = procedure(Sender: TObject) of object;

     TPersistent = class(TObject)
     public
       Procedure Assign(Source:TPersistent); virtual;
     end;

     TComponent=class(TPersistent);

     TCreateParams=class
     end;

     TGraphicObject=class(TPersistent)
     private
       FOnChange : TNotifyEvent;
     public
       procedure Changed;
       property OnChange:TNotifyEvent read FOnChange write FOnChange;
     end;

     TPenStyle=(psSolid,psDash,psDot,psDashDot,psDashDotDot,psClear,psInsideFrame);

     TPenMode = (pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy,
      pmMergePenNot, pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge,
      pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor);

     TPen=class(TGraphicObject)
     private
       FColor : TColor;
       FStyle : TPenStyle;
       FWidth : Integer;
       FMode : TPenMode;
       FOnChange : TNotifyEvent;
     public
       Handle : HPen;
       property Color: TColor read FColor write FColor;
       property Mode:TPenMode read FMode write FMode;
       property Style: TPenStyle read FStyle write FStyle;
       property Width: Integer read FWidth write FWidth;
     end;

     TGraphic=class(TPersistent)
     public
       Palette : Integer;
       Width, Height : Integer;
       Empty : Boolean;
     end;

     PByteArray = ^TByteArray;
     TByteArray = array[0..32767] of Byte;

     TCanvas=class;

     TBitmap=class(TGraphic)
     private
     public
       PixelFormat : Integer;
       IgnorePalette : Boolean;
       Canvas : TCanvas;
       ScanLine:Array of Pointer;
     end;

     TPicture=class(TGraphicObject)
     public
       Graphic:TGraphic;
       Bitmap:TBitmap;
     end;

     TBrushStyle=(bsClear, bsSolid,bsDiagonal);

     TBrush=class(TGraphicObject)
     private
       FColor : TColor;
       FBitmap : TBitmap;
       FStyle : TBrushStyle;
     public
       Handle : HBrush;

       property Bitmap:TBitmap read FBitmap write FBitmap;
       property Color: TColor read FColor write FColor;
       property Style: TBrushStyle read FStyle write FStyle;
     end;

     TFontStyle=(fsBold, fsItalic, fsUnderline, fsStrikeThrough);
     TFontStyles=set of TFontStyle;

     TFont=class(TGraphicObject)
     private
       FColor : TColor;
       FHeight : Integer;
       FName : String;
       FStyle : TFontStyles;
       FSize: Integer;
       FSet   : Integer;
     public
       PixelsPerInch : Integer;
       Handle : HFont;

       property CharSet : Integer read FSet write FSet;
       property Color: TColor read FColor write FColor;
       property Height : Integer read FHeight write FHeight;
       property Name : String read FName write FName;
       property Size:Integer read FSize write FSize;
       property Style:TFontStyles read FStyle write FStyle;
     end;

     TCanvas=class(TObject)
     private
       FOnChange:TNotifyEvent;
       FOnChanging:TNotifyEvent;
       function GetPixel(X, Y: Integer): TColor;
       procedure SetPixel(X, Y: Integer; Value: TColor);
     public
       Pen:TPen;
       Brush:TBrush;
       Font:TFont;
       Handle : HDC;
       procedure CopyRect(const Dest: TRect; const Canvas: TCanvas; const Source: TRect);
       function TextExtent(const Text:String): TSize;
       property OnChange:TNotifyEvent read FOnChange write FOnChange;
       property OnChanging:TNotifyEvent read FOnChange write FOnChange;
       procedure Draw(X, Y: Integer; const Graphic: TGraphic);
       procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic);
       property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
     end;

     TStrings=class(TComponent);

     TCursor=Integer;

     TFiler=class
     end;

     TCustomPanel=class(TComponent)
     protected
       procedure AssignTo(Dest: TPersistent); virtual;
       procedure CreateParams(var Params: TCreateParams); virtual;
       Procedure DefineProperties(Filer:TFiler); virtual; // obsolete
     public
       Constructor Create(AOwner: TComponent); virtual;
     end;

     TMouseButton=(mbLeft,mbMiddle,mbRight);
     TBorderStyle=(bsSingle);
  {$ENDIF}

  TFilterRegion=class(TPersistent)
  private
    FHeight : Integer;
    FLeft   : Integer;
    FTop    : Integer;
    FWidth  : Integer;

    function GetRectangle:TRect;
    procedure SetRectangle(const Rect:TRect);
  public
    procedure Assign(Source:TPersistent); override;

    property Rectangle:TRect read GetRectangle write SetRectangle;
  published
    property Height:Integer read FHeight write FHeight default 0;
    property Left:Integer read FLeft write FLeft default 0;
    property Top:Integer read FTop write FTop default 0;
    property Width:Integer read FWidth write FWidth default 0;
  end;

  TFloatRect=packed record
    Left   : Double;
    Top    : Double;
    Right  : Double;
    Bottom : Double;
  end;

  TRGB=packed record
    Blue  : Byte;
    Green : Byte;
    Red   : Byte;
  end;

  TRGBA=packed record
    {$IFDEF FMX}
    Blue  : Byte;
    Green : Byte;
    Red   : Byte;
    Alpha : Byte;
    {$ELSE}
    Red   : Byte;
    Green : Byte;
    Blue  : Byte;
    Alpha : Byte;
    {$ENDIF}
  end;

  TRGBs=packed Array[0..0] of TRGB;
  PRGBs=^TRGBs;

  TRGBAs=packed Array[0..0] of TRGBA;
  PRGBAs=^TRGBAs;

  {$IFNDEF FMX}
  TRGBArray=Array of PRGBs;
  {$ENDIF}

  TRGBAArray=Array of PRGBAs;

  IFormCreator=interface
    function AddCheckBox(const PropName,ACaption:String; OnChange:TNotifyEvent=nil):TCheckBox;
    function AddColor(const PropName,ACaption:String):TButton;
    function AddCombo(const PropName:String):TComboBox;
    function AddInteger(const PropName,ACaption:String; AMin,AMax:Integer):TEdit;
    function AddScroll(const PropName:String; AMin,AMax:Integer):TScrollBar;
  end;

  TRectI={$IFDEF FMX}System.Types.TRect{$ELSE}TRect{$ENDIF};

  TTeeFilter=class(TCollectionItem)
  private
    FEnabled : Boolean;
    FRegion  : TFilterRegion;

    function GetRegion:TFilterRegion;
    procedure SetEnabled(const Value:Boolean);
    procedure SetRegion(const Value:TFilterRegion);
  protected
    AllowRegion : Boolean;
    FChanged    : TNotifyEvent;
    ReuseLines  : Boolean;

    procedure CalcLines(const Bitmap:TBitmap);
    function Empty:Boolean;
  public
    RGBA           : Boolean;

    {$IFNDEF FMX}
    Lines          : TRGBArray;
    {$ENDIF}
    LinesRGBA      : TRGBAArray;

    Constructor Create(Collection:TCollection); override;
    Destructor Destroy; override;

    class procedure ApplyTo(const Bitmap:TBitmap); overload;
    procedure Apply(const Bitmap: TBitmap); overload;
    procedure Apply(const Bitmap: TBitmap; const R:TRectI); overload; virtual;

    procedure Assign(Source:TPersistent); override;

    procedure CreateEditor(Creator:IFormCreator; AChanged:TNotifyEvent); virtual;
    procedure PrepareForGallery; dynamic;
    class function Description:String; virtual;
  published
    property Enabled:Boolean read FEnabled write SetEnabled default True;
    property Region:TFilterRegion read GetRegion write SetRegion;
  end;

  TFilterClass=class of TTeeFilter;

  TFilterItems=class(TOwnedCollection)
  private
    FOnChange : TNotifyEvent;

    Function Get(Index:Integer):TTeeFilter;
    Procedure Put(Index:Integer; Const Value:TTeeFilter);
  protected
    {$IFDEF D6}
    procedure Notify(Item: TCollectionItem; Action: {$IFDEF D18}Classes.{$ENDIF}TCollectionNotification); override;
    {$ENDIF}
    procedure Update(Item: TCollectionItem); override;
  public
    Changed : Boolean;

    function Add(Filter:TFilterClass):TTeeFilter;
    procedure ApplyTo(const ABitmap:TBitmap);

    procedure Assign(Source:TPersistent); override;

    property Item[Index:Integer]:TTeeFilter read Get write Put; default;
    property OnChange:TNotifyEvent read FOnChange write FOnChange;
  end;

  TConvolveFilter=class(TTeeFilter)  // abstract class
  protected
    BufferBitmap : TBitmap;
    BufferFilter : TTeeFilter;
    KeepBuffer   : Boolean;
    Rect         : TRect;

    procedure CreateBuffer(AWidth,AHeight:Integer);
    procedure FreeBuffer;
  public
    Weights : packed Array[-1..1,-1..1] of Single;

    procedure Apply(const Bitmap:TBitmap; const R:TRectI); override;
  end;

  TBlurFilter=class(TConvolveFilter)
  private
    FAmount : Integer;
    FSteps  : Integer;
  public
    Constructor Create(Collection:TCollection); override;

    procedure Apply(const Bitmap:TBitmap; const R:TRectI); override;
    class function Description: String; override;

    procedure CreateEditor(Creator:IFormCreator; AChanged:TNotifyEvent); override;
  published
    property Amount:Integer read FAmount write FAmount default 1;
    property Steps:Integer read FSteps write FSteps default 1;
  end;

{$IFDEF FMX}
const
  TA_LEFT   = 0;
  TA_RIGHT  = 2;
  TA_CENTER = 6;
  TA_TOP    = 0;
  TA_BOTTOM = 8;

  {$IFDEF TEEWINDOWS}
  {$EXTERNALSYM TA_LEFT}
  {$EXTERNALSYM TA_RIGHT}
  {$EXTERNALSYM TA_CENTER}
  {$EXTERNALSYM TA_TOP}
  {$EXTERNALSYM TA_BOTTOM}
  {$ENDIF}

{$ENDIF}

type
  {$IFDEF FMX}
  TColor=TAlphaColor;

  TPicture=class(TBitmap)
  private
    function GetBitmap:TBitmap;
    procedure SetBitmap(const Value:TBitmap);
  public
    constructor Create; reintroduce;
    property Graphic:TBitmap read GetBitmap write SetBitmap;
    property Bitmap:TBitmap read GetBitmap write SetBitmap;
  end;

  TGraphic=TBitmap;

  TPenStyle = (psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear,
               psInsideFrame, psUserStyle, psAlternate);

const
  bsSolid=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  bsClear=TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF};

  clDkGray=claDarkgray;
  clNone=claNull;
  clDefault=TColor($00FFFFFF);
  pf32Bit=0;

type
  TColorDialogOption=(cdExtended);
  TColorDialogOptions=set of TColorDialogOption;

  {$ELSE}
  {$IFDEF LCL}
  TColorDialogOption=(cdExtended);
  TColorDialogOptions=set of TColorDialogOption;
  {$ENDIF}
  {$ENDIF}

  TTeePicture=class(TPicture)
  private
    FFilters : TFilterItems;
    IBitmap  : TBitmap;

    procedure DoReadFilters(Reader: TReader);
    procedure DoWriteFilters(Writer: TWriter);
    function FiltersStored:Boolean;
    function GetFilters:TFilterItems;
    procedure SetFilters(const Value:TFilterItems);
  protected
    procedure DefineProperties(Filer:TFiler); override;
  public
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;
    class procedure AssignGraphic(const ABitmap:TBitmap; const AGraphic:TGraphic);

    function Filtered:TGraphic;
    procedure Repaint;
    function Valid:Boolean;

    class procedure ReadFilters(const Reader:TReader; const Filters:TFilterItems);
    class procedure WriteFilters(const Writer: TWriter; const Filters:TFilterItems);
  published
    property Filters:TFilterItems read GetFilters write SetFilters
                                  stored False;
  end;

     TTeeCanvas=class;

     {$IFDEF LCL}
     TPointFloat=packed record
       X,Y : Single;
     end;
     {$ENDIF}

     TPointFloatArray=Array of TPointFloat;
     TPathPointTypes=Array of Byte;

     // Cross-platform VCL/FMX Path
     TTeeBasePath=class {$IFDEF D11}abstract{$ENDIF}
     public
       procedure ArcTo(const X,Y,Width,Height,StartAngle,SweepAngle:Single); virtual; abstract;
       procedure Close; virtual; abstract;
       procedure Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean); virtual; abstract;
       procedure Draw(const ACanvas:TTeeCanvas); virtual; abstract;
       procedure GetPoints(out Types:TPathPointTypes; out Points:TPointFloatArray; out Bounds:TRectF); virtual; abstract;
       procedure LineTo(const X,Y:Single); overload; virtual; abstract;
       procedure LineTo(const P:TPoint); overload;
       procedure MoveTo(const X,Y:Single); overload; virtual; abstract;
       procedure MoveTo(const P:TPoint); overload;
       procedure TextOut(const X,Y:Single; const Text:String); virtual; abstract;
     end;

     TTeeCornerStyle=(cosRound,cosBevel,cosNone);

     TTeeCorner=class(TPersistent)
     private
       FStyle : TTeeCornerStyle;

       IChanged : TNotifyEvent;
       procedure SetStyle(const Value:TTeeCornerStyle);
     public
       Constructor Create(const Changed:TNotifyEvent);
       procedure Assign(Source:TPersistent); override;
     published
       property Style:TTeeCornerStyle read FStyle write SetStyle default cosRound;
     end;

     TTeeCorners=class(TPersistent)
     private
       FLeftBottom,
       FLeftTop,
       FRightBottom,
       FRightTop: TTeeCorner;

       procedure SetLeftBottom(const Value:TTeeCorner);
       procedure SetLeftTop(const Value:TTeeCorner);
       procedure SetRightBottom(const Value:TTeeCorner);
       procedure SetRightTop(const Value:TTeeCorner);
     protected
       function GetPath(const ACanvas:TTeeCanvas; const R:TRect; X,Y:Integer):TTeeBasePath;
     public
       Constructor Create(const Changed:TNotifyEvent=nil);
       Destructor Destroy; override;

       procedure Assign(Source:TPersistent); override;
     published
       property LeftBottom:TTeeCorner read FLeftBottom write SetLeftBottom;
       property LeftTop:TTeeCorner read FLeftTop write SetLeftTop;
       property RightBottom:TTeeCorner read FRightBottom write SetRightBottom;
       property RightTop:TTeeCorner read FRightTop write SetRightTop;
     end;

     TCoordinate={$IFDEF FMX}Single{$ELSE}Integer{$ENDIF};

     TTeeBrush=class;

     {$IFDEF FMX}
     TPenMode = (pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy,
      pmMergePenNot, pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge,
      pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor);
     {$ENDIF}

     TPenEndStyle=(esRound,esSquare,esFlat);
     TPenJoinStyle=(jsRound,jsBevel,jsMitter);

     TTeePen=class({$IFDEF FMX}TPersistent{$ELSE}TPen{$ENDIF})
     private
       {$IFDEF FMX}
       FMode  : TPenMode; // not used in FMX
       FStyle : TPenStyle;
       FWidth : Single;
       FOnChange : TNotifyEvent;
       {$ENDIF}

       FEndStyle   : TPenEndStyle;
       FJoinStyle  : TPenJoinStyle;
       FSmallDots  : Boolean;
       FSmallSpace : Integer;
       FVisible    : Boolean;

       Function IsEndStored:Boolean;
       Function IsJoinStored:Boolean;
       Function IsVisibleStored:Boolean;
       function IsWidthStored:Boolean;

       {$IFDEF FMX}
       function GetColor: TColor;
       procedure SetColor(const Value: TColor);
       procedure SetStyle(const Value: TPenStyle);
       procedure SetWidth(const Value: Single);
       {$ENDIF}

       procedure SetEndStyle(const Value: TPenEndStyle);
       procedure SetJoinStyle(const Value: TPenJoinStyle);
       Procedure SetSmallDots(const Value:Boolean);
       Procedure SetSmallSpace(const Value:Integer);
       Procedure SetVisible(const Value:Boolean);

     protected
       DefaultEnd : TPenEndStyle;
       DefaultJoin : TPenJoinStyle;
       DefaultVisible : Boolean;
       DefaultWidth : TCoordinate;

       FFill : TTeeBrush;

       {$IFDEF FMX}
       procedure Changed;
       {$ENDIF}

       function GetPenBrush:TTeeBrush;
       procedure InitColor(const AColor:TColor);
       procedure InitEndStyle(const Value:TPenEndStyle);
       procedure SetPenBrush(const Value:TTeeBrush);
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent=nil); {$IFDEF LCL}reintroduce;{$ENDIF}
       Destructor Destroy; override;

       function IsGradientVisible:Boolean;

       Procedure Assign(Source:TPersistent); override;
       function HalfWidth:Integer;
       procedure Hide;
       procedure Show;

       {$IFDEF FMX}
       property OnChange:TNotifyEvent read FOnChange write FOnChange;
       {$ENDIF}
     published
       {$IFDEF FMX}
       property Color : TColor read GetColor write SetColor default TAlphaColors.Black;
       property Mode  : TPenMode read FMode write FMode default pmCopy;
       property Style : TPenStyle read FStyle write SetStyle default psSolid;
       property Width : Single read FWidth write SetWidth stored IsWidthStored;
       {$ELSE}
       property Width stored IsWidthStored nodefault;
       {$ENDIF}

       property EndStyle:TPenEndStyle read FEndStyle write SetEndStyle stored IsEndStored;
       property Fill:TTeeBrush read GetPenBrush write SetPenBrush;
       property JoinStyle:TPenJoinStyle read FJoinStyle write SetJoinStyle stored IsJoinStored;
       property SmallDots:Boolean read FSmallDots write SetSmallDots default False;
       property SmallSpace:Integer read FSmallSpace write SetSmallSpace default 0;
       property Visible:Boolean read FVisible write SetVisible stored IsVisibleStored;
     end;

     TTeeHiddenPen=class(TTeePen)
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent);
     published
       property Visible default False;
     end;

     TDottedGrayPen=class(TTeePen)
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent);
     published
       property Color default {$IFDEF FMX}TAlphaColors.Gray{$ELSE}clGray{$ENDIF};
       property Style default psDot;
     end;

     TDarkGrayPen=class(TTeePen)
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent);
     published
       property Color default {$IFDEF FMX}claDarkGray{$ELSE}clDkGray{$ENDIF};
     end;

     TWhitePen=class(TTeePen)
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent);
     published
       property Color default {$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};
     end;

     TTeeGradient=class; // forward declaration


    THatchStyle = (
        hsHorizontal,                  // = 0,
        hsVertical,                    // = 1,
        hsForwardDiagonal,             // = 2,
        hsBackwardDiagonal,            // = 3,
        hsCross,                       // = 4,
        hsDiagonalCross,               // = 5,
        hs05Percent,                   // = 6,
        hs10Percent,                   // = 7,
        hs20Percent,                   // = 8,
        hs25Percent,                   // = 9,
        hs30Percent,                   // = 10,
        hs40Percent,                   // = 11,
        hs50Percent,                   // = 12,
        hs60Percent,                   // = 13,
        hs70Percent,                   // = 14,
        hs75Percent,                   // = 15,
        hs80Percent,                   // = 16,
        hs90Percent,                   // = 17,
        hsLightDownwardDiagonal,       // = 18,
        hsLightUpwardDiagonal,         // = 19,
        hsDarkDownwardDiagonal,        // = 20,
        hsDarkUpwardDiagonal,          // = 21,
        hsWideDownwardDiagonal,        // = 22,
        hsWideUpwardDiagonal,          // = 23,
        hsLightVertical,               // = 24,
        hsLightHorizontal,             // = 25,
        hsNarrowVertical,              // = 26,
        hsNarrowHorizontal,            // = 27,
        hsDarkVertical,                // = 28,
        hsDarkHorizontal,              // = 29,
        hsDashedDownwardDiagonal,      // = 30,
        hsDashedUpwardDiagonal,        // = 31,
        hsDashedHorizontal,            // = 32,
        hsDashedVertical,              // = 33,
        hsSmallConfetti,               // = 34,
        hsLargeConfetti,               // = 35,
        hsZigZag,                      // = 36,
        hsWave,                        // = 37,
        hsDiagonalBrick,               // = 38,
        hsHorizontalBrick,             // = 39,
        hsWeave,                       // = 40,
        hsPlaid,                       // = 41,
        hsDivot,                       // = 42,
        hsDottedGrid,                  // = 43,
        hsDottedDiamond,               // = 44,
        hsShingle,                     // = 45,
        hsTrellis,                     // = 46,
        hsSphere,                      // = 47,
        hsSmallGrid,                   // = 48,
        hsSmallCheckerBoard,           // = 49,
        hsLargeCheckerBoard,           // = 50,
        hsOutlinedDiamond,             // = 51,
        hsSolidDiamond                // = 52
     );

     TTeeBrush=class(TBrush)
     private
       FBackColor : TColor;
       FGradient  : TTeeGradient;
       FImage     : TTeePicture;
       FHatchStyle : THatchStyle;

       DefaultColor : TColor;

       function GetGradient:TTeeGradient;
       Function GetImage:TTeePicture;

       {$IFNDEF FMX}
       function HasGraphic:Boolean;
       {$ENDIF}

       function IsColorStored:Boolean;

       procedure SetBackColor(const Value:TColor);
       procedure SetGradient(const Value:TTeeGradient);
       procedure SetHatchStyle(const Value: THatchStyle);
       procedure SetImage(const Value:TTeePicture); {$IFDEF LCL}reintroduce;{$ENDIF}

       {$IFDEF FMX}
       function GetKind:TBrushKind;
       function GetOnChanged:TNotifyEvent;
       procedure SetKind(const Value:TBrushKind);
       procedure SetOnChanged(const Value:TNotifyEvent);
       {$ENDIF}
     protected
       {$IFDEF FMX}
       procedure Changed;
       {$ENDIF}

       function HasImage:Boolean; {$IFDEF D9}inline;{$ENDIF}
       function HasGradient:Boolean;

       procedure InitColor(const Value:TColor);
     public
       Constructor Create(const OnChangeEvent:TNotifyEvent=nil); virtual; {$IFDEF LCL}reintroduce;{$ENDIF}
       Destructor Destroy; override;

       Procedure Assign(Source:TPersistent); override;
       Procedure Clear;

       {$IFDEF FMX}
       property Style:TBrushKind read GetKind write SetKind;
       property OnChange:TNotifyEvent read GetOnChanged write SetOnChanged;
       {$ENDIF}
     published
       property BackColor:TColor read FBackColor write SetBackColor default clNone;
       property Color stored IsColorStored;
       property Gradient:TTeeGradient read GetGradient write SetGradient;
       property HatchStyle:THatchStyle read FHatchStyle write SetHatchStyle default hsHorizontal;
       property Image:TTeePicture read GetImage write SetImage stored HasImage;
     end;

     TChartBrush=TTeeBrush; // <-- TChartBrush is deprecated

     {$IFDEF FMX}
     TBrushHelper=class helper for TBrush
     private
       function GetStyle:TBrushKind;
       procedure SetStyle(const Value:TBrushKind);
     public
       property Style:TBrushKind read GetStyle write SetStyle;
     end;
     {$ENDIF}

     TCanvasZoomText=(ztManual, ztNo, ztAuto);

     TTeeView3DScrolled=procedure(IsHoriz:Boolean) of object;
     TTeeView3DChangedZoom=procedure(NewZoom:Integer) of object;

     TView3DOptions = class(TPersistent)
     private
       FElevation   : Double;
       FFontZoom    : Integer;
       FHorizOffset : Double;
       FOrthogonal  : Boolean;
       FOrthoAngle  : Integer;
       FPerspective : Integer;
       FRotation    : Double;
       FTilt        : Double;
       FVertOffset  : Double;
       FZOffset     : Double;
       FZoom        : Double;
       FZoomText    : TCanvasZoomText;

       FOnScrolled  : TTeeView3DScrolled;
       FOnChangedZoom:TTeeView3DChangedZoom;

       {$IFDEF TEEVCL}
       FParent      : {$IFDEF FMX}TControl{$ELSE}TWinControl{$ENDIF};
       {$ENDIF}

       function GetElevation:Integer;
       function GetHorizOffset:Integer;
       function GetRotation:Integer;
       function GetTilt:Integer;
       function GetVertOffset:Integer;
       function GetZoom:Integer;

       Procedure SetBooleanProperty(Var Variable:Boolean; Value:Boolean);
       Procedure SetElevationInteger(const Value:Integer);
       Procedure SetElevation(const Value:Double);
       Procedure SetFontZoom(Value:Integer);
       Procedure SetHorizInteger(const Value:Integer);
       Procedure SetHorizOffset(const Value:Double);
       Procedure SetIntegerProperty(Var Variable:Integer; Value:Integer);
       Procedure SetOrthoAngle(Value:Integer);
       Procedure SetOrthogonal(Value:Boolean);
       Procedure SetPerspective(Value:Integer);
       Procedure SetRotationInteger(const Value:Integer);
       procedure SetRotation(const Value:Double); overload;
       Procedure SetTilt(const Value:Double);
       Procedure SetTiltInteger(Value:Integer);
       Procedure SetVertInteger(const Value:Integer);
       Procedure SetVertOffset(const Value:Double);
       procedure SetZOffset(const Value: Double);
       Procedure SetZoomInteger(const Value:Integer);
       Procedure SetZoom(const Value:Double);
       Procedure SetZoomText(const Value:TCanvasZoomText);
     protected
       {$IFDEF FMX}
       // Internal. Used to notify the Parent control in FMXTee.Procs.pas unit
       FOnInvalidate : TNotifyEvent;
       {$ENDIF}

       function CalcOrthoRatio:Double;
     public
       Constructor Create(const {$IFDEF TEEVCL}AParent:{$IFDEF FMX}TControl{$ELSE}TWinControl{$ENDIF}{$ENDIF});

       function BackFaced:Boolean;
       Procedure Repaint;
       Procedure Assign(Source:TPersistent); override;

       {$IFDEF TEEVCL}
       property Parent:{$IFDEF FMX}TControl{$ELSE}TWinControl{$ENDIF} read FParent write FParent;
       {$ENDIF}

       property ElevationFloat:Double read FElevation write SetElevation;
       property HorizOffsetFloat:Double read FHorizOffset write SetHorizOffset;
       property RotationFloat:Double read FRotation write SetRotation;
       property TiltFloat:Double read FTilt write SetTilt;
       property VertOffsetFloat:Double read FVertOffset write SetVertOffset;
       property ZOffset:Double read FZOffset write SetZOffset;
       property ZoomFloat:Double read FZoom write SetZoom;

       property OnChangedZoom:TTeeView3DChangedZoom read FOnChangedZoom
                                                    write FOnChangedZoom;
       property OnScrolled:TTeeView3DScrolled read FOnScrolled write FOnScrolled;
     published
       property Elevation:Integer read GetElevation write SetElevationInteger default 345;
       property FontZoom:Integer read FFontZoom write SetFontZoom default 100;
       property HorizOffset:Integer read GetHorizOffset write SetHorizInteger default 0;
       property OrthoAngle:Integer read FOrthoAngle write SetOrthoAngle default 45;
       property Orthogonal:Boolean read FOrthogonal write SetOrthogonal default True;
       property Perspective:Integer read FPerspective
                                    write SetPerspective default TeeDefaultPerspective;
       property Rotation:Integer read GetRotation write SetRotationInteger default 345;
       property Tilt:Integer read GetTilt write SetTiltInteger default 0;
       property VertOffset:Integer read GetVertOffset write SetVertInteger default 0;
       property Zoom:Integer read GetZoom write SetZoomInteger default 100;
       property ZoomText:TCanvasZoomText read FZoomText write SetZoomText default ztManual;
     end;

     TTeeTransparency=0..100;

     TTeeBlend=class
     private
       FBitmap : TBitmap;

       {$IFNDEF FMX}
       FCanvas : TTeeCanvas;
       FRect   : TRect;

       IValidSize : Boolean;
       {$ENDIF}
     public
       Constructor Create(const ACanvas:TTeeCanvas; Const R:TRect);
       Destructor Destroy; override;

       Procedure DoBlend(Transparency:TTeeTransparency);
       Procedure SetRectangle(const R:TRect);

       property Bitmap:TBitmap read FBitmap;
     end;

     TPointArray=Array of TPoint;

     TFourPoints=Array[0..3] of {$IFDEF FMX}TPointF{$ELSE}TPoint{$ENDIF};
     TFourPointFloats=Array[0..3] of TPointFloat;

     TCanvas3D=class;

     TShadowDirection=(sdLeftTop,sdLeftBottom,sdRightTop,sdRightBottom,sdCenter);

     TTeeShadow=class(TPersistent)
     private
       FClip         : Boolean;
       FColor        : TColor;
       FDirection    : TShadowDirection;
       FHorizSize    : Integer;
       FSmooth       : Boolean;
       FSmoothBlur   : Integer;
       FTransparency : TTeeTransparency;
       FVertSize     : Integer;
       FVisible      : Boolean;

       IOnChange     : TNotifyEvent;
       IBlend        : TTeeBlend;
       ITransp       : Byte;

       procedure Changed;

       {$IFNDEF FMX}
       {$IFNDEF IOS}
       {$IFNDEF ANDROID}
       procedure InternalDrawSmooth(const ACanvas:TCanvas3D; const Rect:TRect;
                            Ellipse:Boolean;
                            RoundSize:Integer;
                            const P:Array of TPoint;
                            DonutPercent:Integer=0;
                            const StartAngle:Double=0;
                            const EndAngle:Double=0);
       {$ENDIF}
       {$ENDIF}
       {$ENDIF}

       procedure FinishBlending(const ACanvas:TTeeCanvas);
       function GetSize: Integer;
       function IsColorStored: Boolean;
       function IsHorizStored: Boolean;
       function IsVertStored: Boolean;
       function IsVisibleStored: Boolean;
       Function PrepareCanvas(const ACanvas:TCanvas3D; const R:TRect; Angle:Integer=0):Boolean;
       Procedure SetClip(const Value:Boolean);
       Procedure SetColor(Value:TColor);
       procedure SetDirection(const Value: TShadowDirection);
       Procedure SetHorizSize(Value:Integer);
       Procedure SetIntegerProperty(Var Variable:Integer; Const Value:Integer);
       procedure SetSize(const Value: Integer);
       procedure SetSmooth(const Value: Boolean);
       procedure SetSmoothBlur(const Value: Integer);
       procedure SetTransparency(Value: TTeeTransparency);
       Procedure SetVertSize(Value:Integer);
       Procedure SetVisible(Value:Boolean);

     protected
       DefaultColor   : TColor;
       DefaultSize    : Integer;
       DefaultVisible : Boolean;

       procedure InitValues(AVisible:Boolean; AColor:TColor; ASize:Integer);
       function OffsetRectangle(const R:TRect):TRect;
       Procedure RectToFourPoints(const ARect:TRect; const Angle:Double; out P:TFourPoints);
     public
       BlurSize : Integer;
       ZPosition : Double;

       Constructor Create(AOnChange:TNotifyEvent);

       Procedure Assign(Source:TPersistent); override;

       procedure Draw(const ACanvas:TCanvas3D; const P:Array of TPoint); overload;
       procedure Draw(const ACanvas:TCanvas3D; const P:Array of TPoint; Count:Integer); overload;
       procedure Draw(const ACanvas:TCanvas3D; Const Rect:TRect; RoundSize:Integer=0; Angle:Integer=0); overload;
       procedure DrawEllipse(const ACanvas:TCanvas3D; Const Rect:TRect; DonutPercent:Integer=0;
                             const StartAngle:Double=0; const EndAngle:Double=360); virtual;

       procedure Hide;
       function ShouldDraw:Boolean;
       procedure Show;

       property Size:Integer read GetSize write SetSize;
     published
       property Clip:Boolean read FClip write SetClip default False;
       property Color:TColor read FColor write SetColor stored IsColorStored;
       property Direction:TShadowDirection read FDirection write SetDirection default sdRightBottom;
       property HorizSize:Integer read FHorizSize write SetHorizSize stored IsHorizStored;
       property Smooth:Boolean read FSmooth write SetSmooth default True;
       property SmoothBlur:Integer read FSmoothBlur write SetSmoothBlur default 0;
       property Transparency:TTeeTransparency read FTransparency write SetTransparency default 50;
       property VertSize:Integer read FVertSize write SetVertSize stored IsVertStored;
       property Visible:Boolean read FVisible write SetVisible stored IsVisibleStored;
     end;

     TTeeEmboss=class(TTeeShadow)
     public
       Constructor Create(AOnChange:TNotifyEvent);
     published
       property Direction default sdLeftTop;
     end;

     TTeeGradientColor=class(TCollectionItem)
     private
       FColor  : TColor;
       FOffset : Double;

       {$IFNDEF FMX}
       FTransp : TTeeTransparency;
       {$ENDIF}

       function GetTransp:TTeeTransparency;
       function IsTranspStored:Boolean;
       procedure SetTransp(const Value:TTeeTransparency);
     public
       procedure Assign(Source:TPersistent); override;
     published
       property Color  : TColor read FColor write FColor default 0;
       property Offset : Double read FOffset write FOffset;
       property Transparency : TTeeTransparency read GetTransp write SetTransp stored IsTranspStored;
     end;

     TCustomTeeGradient=class;

     TTeeGradientColors=class(TOwnedCollection)
     private
       Function Get(Index:Integer):TTeeGradientColor;
       Procedure Put(Index:Integer; Const Value:TTeeGradientColor);
     protected
       procedure Update(Item: TCollectionItem); override;
     public
       function Add(const AOffset:Double; const AColor:TColor;
                    ATransparency:TTeeTransparency=0):TTeeGradientColor; overload;
       procedure Repaint;

       property Item[Index:Integer]:TTeeGradientColor read Get write Put; default;
     end;

     TGradientDirection = (gdTopBottom, gdBottomTop,
                           gdLeftRight, gdRightLeft,
                           gdFromCenter, gdFromTopLeft,
                           gdFromBottomLeft, gdRadial,
                           gdDiagonalUp, gdDiagonalDown );

     TSubGradient=class; // forward declared

     TCustomTeeGradient=class(TPersistent)
     private
       FAngle      : Integer;
       FBalance    : Integer;
       FColors     : TTeeGradientColors;
       FDirection  : TGradientDirection;
       FRadialX    : Integer;
       FRadialY    : Integer;
       FStartIsEnd : Boolean;
       FSub        : TSubGradient;
       FVisible    : Boolean;

       FCustomTargetRectangle : TRectF;

       {$IFDEF FMX}
       IBrush : TBrush;
       {$ENDIF}

       Procedure DrawRadial(const Canvas:TTeeCanvas; Rect:TRect);

       {$IFNDEF FMX}
       procedure DrawRotated(const Canvas:TTeeCanvas; const R:TRect);
       {$ENDIF}

       procedure DrawSubGradient(const Canvas:TTeeCanvas; Const Rect:TRect;
                                 RoundRectSize:Integer=0);

       function GetColors:TTeeGradientColors;
       Function GetEndColor:TColor;
       Function GetMidColor:TColor;
       Function GetStartColor:TColor;
       function GetSub:TSubGradient;

       function IsColorsStored:Boolean;
       function IsDirectionStored:Boolean;
       function IsEndStored:Boolean;
       function IsMidStored:Boolean;
       function IsStartStored:Boolean;
       Function IsSubStored:Boolean;
       function IsVisibleStored:Boolean;

       Procedure SetAngle(Value:Integer);
       Procedure SetBalance(Value:Integer);
       procedure SetColors(const Value:TTeeGradientColors);
       Procedure SetDirection(Value:TGradientDirection);
       Procedure SetEndColor(Value:TColor);
       Procedure SetIntegerProperty(Var Variable:Integer; Value:Integer);
       Procedure SetMidColor(Value:TColor);
       procedure SetRadialX(const Value: Integer);
       procedure SetRadialY(const Value: Integer);
       Procedure SetStartColor(Value:TColor);
       procedure SetSub(const Value:TSubGradient);
       Procedure SetVisible(Value:Boolean);
     protected
       IChanged      : TNotifyEvent;
       IDefDirection : TGradientDirection;
       IDefEnd       : TColor;
       IDefMid       : TColor;
       IDefStart     : TColor;
       IDefVisible   : Boolean;
       IMidColor     : TColor;

       procedure DoChanged;
       procedure InitDefaults;
       procedure InitDirection(const ADirection:TGradientDirection);
     public
       Constructor Create(ChangedEvent:TNotifyEvent); virtual;
       Destructor Destroy; override;

       Procedure Assign(Source:TPersistent); override;

       // deprecated:
       Procedure Draw(const Canvas:TTeeCanvas; Const Rect:TRect; RoundRectSize:Integer=0); overload;
       Procedure Draw(const Canvas:TTeeCanvas; var P:TFourPoints); overload;
       Procedure Draw(const Canvas:TCanvas3D; var P:TFourPoints; Z:Integer); overload;
       procedure Draw(const Canvas:TCanvas3D; var P:TPointArray; Z:Integer; Is3D:Boolean); overload;

       procedure ResetColors;

       procedure UseMiddleColor;

       property Changed:TNotifyEvent read IChanged write IChanged;

       { to be published }
       property Angle:Integer read FAngle write SetAngle default 0;

       property CustomTargetRectangle:TRectF read FCustomTargetRectangle write FCustomTargetRectangle stored False;

       property Balance:Integer read FBalance write SetBalance default 50;
       property Colors:TTeeGradientColors read GetColors write SetColors stored IsColorsStored;
       property Direction:TGradientDirection read FDirection write SetDirection stored IsDirectionStored;
       property EndColor:TColor read GetEndColor write SetEndColor stored IsEndStored;
       property MidColor:TColor read GetMidColor write SetMidColor stored IsMidStored;
       property RadialX:Integer read FRadialX write SetRadialX default 0;
       property RadialY:Integer read FRadialY write SetRadialY default 0;
       property StartColor:TColor read GetStartColor write SetStartColor stored IsStartStored;
       property StartIsEnd:Boolean read FStartIsEnd write FStartIsEnd default False;
       property SubGradient:TSubGradient read GetSub write SetSub stored IsSubStored;
       property Visible:Boolean read FVisible write SetVisible stored IsVisibleStored;
     end;

     TSubGradient=class(TCustomTeeGradient)
     private
       FTransparency : TTeeTransparency;
       procedure SetTransparency(const Value:TTeeTransparency);
     published
       property Angle;
       property Balance;
       property Colors;
       property Direction;
       property EndColor;
       property MidColor;
       property RadialX;
       property RadialY;
       property StartColor;
       property Transparency:TTeeTransparency read FTransparency
                                              write SetTransparency default 50;
       property Visible;
     end;

     TTeeGradient=class(TCustomTeeGradient)
     published
       property Angle;
       property Balance;
       property Colors;
       property Direction;
       property EndColor;
       property MidColor;
       property RadialX;
       property RadialY;
       property StartColor;
       property SubGradient;
       property Visible;
     end;

     // Deprecated, see Font.Outline.Gradient property
     // TTeeFontGradient=class(TTeeGradient)

     TFontQuality=(fqBest, fqNormal, fqDefault, fqClearType);

     TTeeFont=class(TFont)
     private
       FBrush         : TTeeBrush;

       {$IFNDEF FMX}
       FSizeDecimals  : Single;
       {$ENDIF}

       FDefault       : Boolean;
       FDepth         : Double;
       FInterCharSize : Integer;
       FOutLine       : TTeeHiddenPen;
       FQuality       : TFontQuality;

       IDefColor : TColor;
       IDefStyle : TFontStyles;

       {$IFDEF FMX}
       procedure Changed;
       function GetColor:TColor;
       procedure SetColor(const Value:TColor);
       function GetName:String;
       procedure SetName(const Value:String);
       procedure DoReadHeight(Reader: TReader);

       {$IFNDEF D24}
       procedure DoReadStyleExt(AStream: TStream);
       {$ENDIF}

       {$ENDIF}

       Function GetEmboss: TTeeEmboss;
       function GetGradient: TTeeGradient; // was: TTeeFontGradient
       function GetOutLine: TTeeHiddenPen;
       function GetPicture: TTeePicture;
       Function GetShadow: TTeeShadow;

       Function IsColorStored:Boolean;
       function IsDepthStored:Boolean;

       Function IsHeightStored:Boolean;
       Function IsNameStored:Boolean;
       Function IsStyleStored:Boolean;

       procedure SetBrush(const Value:TTeeBrush);
       procedure SetDefault(const Value:Boolean);
       procedure SetDepth(const Value:Double);
       Procedure SetEmboss(const Value:TTeeEmboss);
       procedure SetGradient(const Value: TTeeGradient); // was: TTeeFontGradient
       Procedure SetInterCharSize(Value:Integer);
       Procedure SetOutLine(const Value:TTeeHiddenPen);
       Procedure SetPicture(const Value:TTeePicture);
       Procedure SetQuality(const Value:TFontQuality);
       Procedure SetShadow(const Value:TTeeShadow);

       function GetSizeFloat:Single;
       procedure SetSizeFloat(const Value:Single);
       function IsSizeFloatStored:Boolean;
     protected
       FEmboss   : TTeeEmboss;
       FShadow   : TTeeShadow;

       {$IFDEF FMX}
       procedure DefineProperties(Filer:TFiler); override;
       {$ENDIF}

       procedure InitColor(const Value:TColor);
       procedure InitStyle(const Value:TFontStyles);
     public
       Constructor Create(ChangedEvent:TNotifyEvent=nil); {$IFDEF LCL}reintroduce;{$ENDIF}
       Destructor Destroy; override;

       Procedure Assign(Source:TPersistent); override;
     published
       property DefaultFont:Boolean read FDefault write SetDefault default False;

       {$IFDEF FMX}
       property Color:TColor read GetColor write SetColor stored IsColorStored;
       property Name:String read GetName write SetName stored False;
       property Family stored IsNameStored;
       property Size stored IsHeightStored;
       {$ELSE}
       property Charset default DEFAULT_CHARSET;
       property Height stored IsHeightStored;
       property Color stored IsColorStored;
       property Name stored IsNameStored;
       {$ENDIF}

       // VCL only. FMX = Size
       property SizeFloat:Single read GetSizeFloat write SetSizeFloat stored IsSizeFloatStored;

       property Brush:TTeeBrush read FBrush write SetBrush;
       property Depth:Double read FDepth write SetDepth stored IsDepthStored;
       property Emboss:TTeeEmboss read GetEmboss write SetEmboss;

       // TTeeFontGradient deprecated (replaced with Outline.Gradient)
       property Gradient:TTeeGradient read GetGradient write SetGradient stored False;

       property InterCharSize:Integer read FInterCharSize
                                      write SetInterCharSize default 0;
       property OutLine:TTeeHiddenPen read GetOutLine write SetOutLine;
       property Picture:TTeePicture read GetPicture write SetPicture stored False;
       property Quality:TFontQuality read FQuality write SetQuality default fqDefault;
       property Shadow:TTeeShadow read GetShadow write SetShadow;
       property Style stored IsStyleStored;
     end;

     TCanvasStretchQuality=( sqHigh, sqLow );

     TCanvasBackMode  = ( cbmNone,cbmTransparent,cbmOpaque );
     TCanvasTextAlign = Integer;

     TTeeCachedObject=class
     end;

    {$IFDEF TEEVALUESINGLE}
    TChartValue=Single;
    {$ELSE}
    {$IFDEF TEEVALUEDOUBLE}
    TChartValue=Double;
    {$ELSE}
    {$IFDEF TEEVALUEEXTENDED}
    TChartValue=Extended;
    {$ELSE}
    TChartValue=Double;  { <-- default }
    {$ENDIF}
    {$ENDIF}
    {$ENDIF}

     TTeeCanvasHandle={$IFDEF FMX}Integer{$ELSE}{$IFDEF TEEOCX}Integer{$ELSE}HDC{$ENDIF}{$ENDIF};

     {$IFNDEF FMX}
     TCanvasStyle = ( csGDI,csGDIPlus,csOpenGL,csHTML5 );
     {$ENDIF}

     TTeeCanvas=class {$IFDEF D11}abstract{$ENDIF}
     private
       {$IFNDEF FMX}
       FBrush      : TTeeBrush;
       FCanvas     : TCanvas;
       FFont       : TTeeFont;
       FPen        : TTeePen;
       {$ENDIF}

       FStretchQuality : TCanvasStretchQuality;

       FMetafiling : Boolean;
       IChangingFont: Boolean;
       ITranspStack : Array of Byte;

       {$IFDEF FMX}

       {$IFDEF HASCANVASD2D}
       FCanvasIsD2D    : Boolean;
       {$ENDIF}
       {$IFDEF HASCANVASGDIP}
       FCanvasIsGDIP   : Boolean;
       {$ENDIF}
       {$IFDEF HASCANVASGPU}
       FCanvasIsGPU    : Boolean;
       {$ENDIF}

       {$IFDEF TEECACHEDLAYOUT}
       ILayout      : TTextLayout;
       {$ENDIF}

       {$IFDEF TEECACHEDLAYOUT}
       function ICachedLayout(const AX,AY:Integer; const Text:String):TTextLayout;
       {$ENDIF}

       {$ENDIF}

       Procedure AssignFontSize(const AFont:TTeeFont; const ASize:Single);

       {$IFDEF AXUNICODE}
       function AXUnicode(const S:String):WideString;
       {$ENDIF}

       procedure ChangedBrush(Sender:TObject);
       procedure ChangedFont(Sender:TObject);
       procedure ChangedPen(Sender:TObject);

       function GetFontHeight:Integer; {$IFDEF D10}inline;{$ENDIF}
       function GetGradient:TTeeGradient;
       Procedure InternalDark(Const AColor:TColor; Quantity:Byte);

       {$IFDEF FMX}
       function DonutPath(const R:TRect; const StartAngle,EndAngle,Percent,Thick:Single):TPathData;
       procedure PrepareGradient(const Source:TTeeGradient; const Dest:TGradient);
       {$ENDIF}
     protected
       {$IFDEF FMX}
       FBrush      : TTeeBrush;
       {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
       FCanvas     : TCanvas;
       FFont       : TTeeFont;
       FPen        : TTeePen;
       {$ENDIF}

       FBounds     : TRect;
       FFontZoom   : Double;

       FSupportsAlpha   : Boolean;
       FSupportsConcave : Boolean;

       IZoomText   : Boolean;

       // Center Point for rotated text
       IUseTextCenter: Boolean;
       ITextCenter : TPoint;

       Transparency : Byte;

       Procedure SetCanvas(const ACanvas:TCanvas); virtual;

       procedure CalcArcAngles(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer;
                               out StartAngle,EndAngle:Single);

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

       procedure FastLineTo(const X,Y:Integer); virtual;
       function GetBackColor:TColor; virtual; abstract;
       Function GetBackMode:TCanvasBackMode; virtual; abstract;

       {$IFNDEF FMX}
       Function GetHandle:TTeeCanvasHandle; virtual; abstract;
       {$ENDIF}

       Function GetIsNoBMPGrid:Boolean; virtual; abstract;
       Function GetMonochrome:Boolean; virtual; abstract;
       Function GetPixel(x,y:Integer):TColor; virtual; abstract;
       Function GetSupportsFullRotation:Boolean; virtual; abstract;
       Function GetSupportsXORMode:Boolean; virtual; abstract;
       Function GetTextAlign:TCanvasTextAlign; virtual; abstract;
       Function GetUseBuffer:Boolean; virtual; abstract;

       procedure InternalDrawCheck(x,y:Integer; Checked:Boolean; const ABackColor:TColor; CheckBox:Boolean=True);
       function IsGradientVisible:Boolean;
       Function IsAliased:Boolean; virtual;
       
       {$IFNDEF FMX}
       function CanvasStyle:TCanvasStyle; virtual; abstract;
       {$ENDIF}

       procedure PopTransp;
       procedure PushTransp;

       procedure SetAntiAlias(const Value:Boolean); virtual;
       Procedure SetBackColor(const Value:TColor); virtual; abstract;
       Procedure SetBackMode(const Value:TCanvasBackMode); virtual; abstract;
       procedure SetFastLine(const Active:Boolean); virtual; abstract;
       procedure SetInterCharSize(ASize:Integer); virtual;
       Procedure SetMonochrome(Value:Boolean); virtual; abstract;
       procedure SetPenMode(const APen:TTeePen); virtual;
       procedure SetPixel(X, Y: Integer; Value: TColor); virtual; abstract;
       procedure SetTextAlign(const Value:TCanvasTextAlign); virtual; abstract;
       Procedure SetUseBuffer(Value:Boolean); virtual; abstract;

       {$IFDEF FMX}
       function Opacity:Single;
       {$ENDIF}

       {$IFNDEF FMX}
       function Tessellate({$IFDEF D6}const{$ENDIF} X,Y: Array of TChartValue):TPointFloatArray; virtual;
       {$ENDIF}

       {$IFDEF TEEUNICODE}
       function TextExtent(const Text:WideString):TSize;
       {$ENDIF}

     public
       Constructor Create;

       Destructor Destroy; override;

       Procedure AssignBrush(const ABrush:TTeeBrush); overload; {$IFDEF D10}inline;{$ENDIF}
       Procedure AssignBrush(const ABrush:TTeeBrush; ABackColor:TColor); overload; {$IFDEF D10}inline;{$ENDIF}
       Procedure AssignBrush(const ABrush:TTeeBrush; AColor,ABackColor:TColor); overload; virtual;

       Procedure AssignBrushColor(const ABrush:TTeeBrush;
                                  AColor,ABackColor:TColor); {$IFDEF D11}deprecated 'Please use AssignBrush method.';{$ENDIF} // Do not localize

       procedure AssignVisiblePen(const APen:TTeePen); {$IFDEF D10}inline;{$ENDIF}
       procedure AssignVisiblePenColor(const APen:TTeePen; AColor:TColor); virtual;

       procedure AssignFont(const AFont:TTeeFont);

       Procedure ResetState;

       Function BeginBlending(const R:TRect; Transparency:TTeeTransparency):TTeeBlend; virtual;
       procedure EndBlending(const Blend:TTeeBlend); virtual;

       { Colors }
       class function ColorAlpha(const AColor:TColor):Byte; {$IFDEF D9}static;{$ENDIF} // Returns "A" from RGBA color
       class function ColorFrom(const AColor:TColor; const Alpha:Byte):TColor; {$IFDEF D9}static;{$ENDIF} // Returns AColor with Alpha

       { 2d }
       procedure Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer); overload; virtual; abstract;
       procedure Arc(const Left, Top, Right, Bottom:Integer; const StartAngle,EndAngle:Single); overload; virtual;
       procedure Arc(const R:TRectI; const StartAngle,EndAngle:Single); overload;

       procedure ArcTo(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer; const ClockWise:Boolean=False); virtual;

       Procedure Arrow( Filled:Boolean; const ArrowPercent:Double;
                        const FromPoint,ToPoint:TPoint;
                        ArrowWidth,ArrowHeight:Integer); overload;

       {$IFDEF FMX}
       procedure Donut( const R:TRect; const StartAngle,EndAngle,HolePercent:Double); overload; virtual;
       {$ENDIF}

       procedure Donut( XCenter,YCenter,XRadius,YRadius:Integer;
                        Const StartAngle,EndAngle,HolePercent:Double); overload; virtual; abstract;
       procedure Draw(X, Y: Integer; const Graphic: TGraphic); virtual; abstract;
       procedure Ellipse(const R:TRect); overload; {$IFDEF D9}inline;{$ENDIF}
       procedure Ellipse(const X1, Y1, X2, Y2: TCoordinate); overload; virtual; abstract;
       procedure FillRect(const Rect: TRect); virtual; abstract;
       procedure Frame3D( var Rect: TRect; TopColor,BottomColor: TColor;
                          Width: Integer); virtual;

       function GetArrowPoints(const ArrowPercent:Single;
                               const FromPoint,ToPoint:TPoint;
                               ArrowWidth,
                               ArrowHeight:Integer):TPointArray; overload;

       class function IsConvexPolygon(const P:TPointArray):Boolean; overload;{$IFDEF D9}static;{$ENDIF}
       {$IFNDEF FMX}
       class function IsConvexPolygon(const P:TPointFloatArray):Boolean; overload;{$IFDEF D9}static;{$ENDIF}
       {$ENDIF}

       procedure LineTo(const X,Y:TCoordinate); overload; virtual; abstract;
       procedure LineTo(const P:TPoint); overload;
       {$IFNDEF FMX}
       procedure LineTo(const P:TPointFloat); overload; virtual;
       {$ENDIF}

       procedure MoveTo(const X,Y:TCoordinate); overload; virtual; abstract;
       procedure MoveTo(const P:TPoint); overload;
       {$IFNDEF FMX}
       procedure MoveTo(const P:TPointFloat); overload; virtual;
       {$ENDIF}

       procedure Pie(const R:TRect; const StartAngle,EndAngle:Double); overload; virtual; abstract;
       procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); overload; virtual; abstract;
       procedure Rectangle(const R:TRect); overload; {$IFDEF D9}inline;{$ENDIF}
       procedure Rectangle(const X0,Y0,X1,Y1:TCoordinate); overload; virtual; abstract;
       procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer); overload; virtual; abstract;
       procedure RoundRect(const R:TRect; X,Y:Integer; const ACorners:TTeeCorners=nil); overload;
       procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic); overload; virtual; abstract;
       {$IFDEF AXUNICODE}
       Procedure TextOut(const X,Y:Integer; const Text:WideString); overload;
       {$ENDIF}
       Procedure TextOut(const X,Y:TCoordinate; const Text:String; AllowHtml:Boolean); overload;
       Procedure TextOut({$IFDEF FMX}const{$ENDIF} X,Y:TCoordinate; const Text:String); overload; virtual; abstract;

       Function TextHeight(Const St:String):Integer; overload; virtual;
       Function TextSize(const St:String):TPointFloat; overload; virtual;
       Function TextWidth(Const St:String):Integer; overload; virtual;

       {$IFDEF TEEUNICODE}
       Function TextHeight(Const St:WideString):Integer; overload; virtual;
       Function TextSize(const St:WideString):TPointFloat; overload; virtual;
       Function TextWidth(Const St:WideString):Integer; overload; virtual;
       {$ENDIF}

       { 2d extra }
       procedure ClipRectangle(Const Rect:TRect); overload; virtual; abstract;
       Procedure ClipRectangle(Const Rect:TRect; RoundX,RoundY:Integer); overload; virtual;
       Procedure ClipEllipse(Const Rect:TRect; Inverted:Boolean=False); virtual;
       Procedure ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                             DiffRegion:Boolean=False); virtual;

       // Returns modified Points array with sorted points that define
       // biggest convex polygon.
       // Written by Peter Bone : peterbone@hotmail.com
       class function ConvexHull(var Points : TPointArray) : Boolean; {$IFDEF D9}static;{$ENDIF}

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

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

       Procedure DoHorizLine(const X0,X1,Y:TCoordinate); virtual;

       Procedure DoRectangle(Const Rect:TRect); {$IFDEF D11}deprecated 'Please use Rectangle method.';{$ENDIF} // Do not localize

       Procedure DoVertLine(const X,Y0,Y1:TCoordinate); virtual;

       procedure DrawCheckBox(x,y:Integer; Checked:Boolean; const ABackColor:TColor; CheckBox:Boolean=True); virtual;

       procedure EraseBackground(const Rect: TRect); virtual; abstract;

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

       Procedure Invalidate; virtual; abstract;

       Procedure Line(const X0,Y0,X1,Y1 : TCoordinate);  overload; virtual;
       Procedure Line(const FromPoint,ToPoint:TPoint); overload; {$IFDEF D9}inline;{$ENDIF}
       Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); overload; virtual; abstract;

       {$IFNDEF FMX}
       procedure PolylineFloat(const Points:Array of TPointFloat); overload; virtual; abstract;
       {$ENDIF}

       Procedure Polygon({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); virtual; abstract;

       procedure PolygonConcave({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); overload; virtual;

       {$IFDEF PIEFLOATS}
       {$IFNDEF FMX}
       procedure PolygonFloat(const Points:Array of TPointFloat); virtual; abstract;
       {$ENDIF}
       {$ENDIF}

       procedure RotateLabel( x,y:Integer; Const St:String;
                              RotDegree:Double;
                              AllowHTML:Boolean=False); virtual; abstract;

       class function SystemColor(const AColor:TColor):TColor; {$IFDEF D9}static;{$ENDIF}

       procedure UnClipRectangle; virtual; abstract;

       // properties
       property BackColor:TColor read GetBackColor write SetBackColor;
       property BackMode:TCanvasBackMode read GetBackMode write SetBackMode;
       property Bounds:TRect read FBounds;
       property Brush:TTeeBrush read FBrush;
       property Font:TTeeFont read FFont;
       property FontHeight:Integer read GetFontHeight;

       property FontZoom:Double read FFontZoom write FFontZoom; // % of zoom of all font sizes

       property Gradient:TTeeGradient read GetGradient;

       {$IFNDEF FMX}
       property Handle:TTeeCanvasHandle read GetHandle;
       {$ENDIF}

       property IsNoBMPGrid:Boolean read GetIsNoBMPGrid;
       property Metafiling:Boolean read FMetafiling write FMetafiling;
       property Monochrome:Boolean read GetMonochrome write SetMonochrome;
       property Pen:TTeePen read FPen;
       property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;

       property ReferenceCanvas:TCanvas read FCanvas write SetCanvas;

       property StretchDrawQuality:TCanvasStretchQuality read FStretchQuality write FStretchQuality default sqHigh;

       property SupportsAlpha:Boolean read FSupportsAlpha;
       property SupportsConcave:Boolean read FSupportsConcave;
       property SupportsFullRotation:Boolean read GetSupportsFullRotation;
       property SupportsXORMode:Boolean read GetSupportsXORMode;

       property TextAlign:TCanvasTextAlign read GetTextAlign write SetTextAlign;
       property UseBuffer:Boolean read GetUseBuffer write SetUseBuffer;
     end;

     // 2D
     TFloatPoint=packed record
       X : Single;
       Y : Single;
     end;

     {$IFDEF D17}

     {$IFDEF D20}
     TPoint3DFloat=System.Math.Vectors.TPoint3D;
     {$ELSE}
     TPoint3DFloat=System.Types.TPoint3D;
     {$ENDIF}

     {$ELSE}

     {$IFNDEF BLOCKS}
     // 3D
     TPoint3DFloat=packed record
       X : Single;
       Y : Single;
       Z : Single;
     end;
     {$ENDIF}

     {$ENDIF}

     TPoint3DArray=Array of TPoint3DFloat;

     TFloatXY=class(TPersistent)
     private
       FX : Single;
       FY : Single;

       function IsXStored:Boolean;
       function IsYStored:Boolean;
       procedure SetSingleProperty(var Variable:Single; const Value:Single);
     protected
       FOnChange : TNotifyEvent;

       IDefaultX : Single;
       IDefaultY : Single;

       Procedure SetX(Const Value:Single);
       Procedure SetY(Const Value:Single);
     public
       Procedure Assign(Source:TPersistent); override;
       function Point:TPointFloat;

       property OnChange:TNotifyEvent read FOnChange write FOnChange;
     published
       property X:Single read FX write SetX stored IsXStored;
       property Y:Single read FY write SetY stored IsYStored;
     end;

     TFloatXYZ=class(TFloatXY)
     private
       FZ : Single;

       function IsZStored:Boolean;
     protected
       IDefaultZ : Single;

       Procedure SetZ(Const Value:Single);
     public
       Procedure Assign(Source:TPersistent); override;
       function Point:TPoint3DFloat;
     published
       property Z:Single read FZ write SetZ stored IsZStored;
     end;

     TPoint3D         =packed record x,y,z:Integer; end;
     TTrianglePoints3D=Array[0..2] of TPoint3D;
     TTriangleColors3D=Array[0..2] of TColor;
     TTriangleIndexes =packed record Point0,Point1,Point2:Integer; end;

     TSurfaceVertex=record
       P0,P1,
       N0,N1 : TPoint3DFloat;
       C0,C1 : TColor;
       Flag  : Boolean;
     end;

     TTeeCanvasCalcPoints=procedure(x,z:Integer; CalcColors:Boolean;
                                    out V:TSurfaceVertex) of object;

     TTeeCanvasTriPoints=procedure(Index: Integer; out Points:TTrianglePoints3D;
                                   out Colors:TTriangleColors3D;
                                   out Indexes:TTriangleIndexes) of object;

     {$IFNDEF BLOCKS}
     TTeeCanvasSurfaceStyle=(tcsSolid,tcsWire,tcsDot);
     {$ENDIF}

     TCanvas3DPlane=(cpX,cpY,cpZ);

     TTrianglePoints=packed Array[0..2] of TPoint;

     {$IFNDEF D6}
     IInterface=IUnknown;
     {$ENDIF}

    TVisualBlock=class;

    TPointBlock=class(TPersistent)
    private
      FOnChanged : TNotifyEvent;
    protected
      IOwner : TVisualBlock;
      procedure DoChanged;
    end;

    TPointXYFloat=class(TPointBlock)
    private
      IDefault : Double;

      function GetX: Single; {$IFDEF D10}inline;{$ENDIF}
      function GetY: Single; {$IFDEF D10}inline;{$ENDIF}

      function IsXStored:Boolean;
      function IsYStored:Boolean;

      procedure ReadXZero(Reader: TReader);
      procedure ReadYZero(Reader: TReader);

      procedure SetX(const Value: Single);
      procedure SetY(const Value: Single);

      procedure WriteXZero(Writer: TWriter);
      procedure WriteYZero(Writer: TWriter);
    protected
      procedure DefineProperties(Filer:TFiler); override;
      procedure InitDefault(const Value:Double);
    public
      Point  : TPointFloat;

      Constructor Create(const AOwner:TVisualBlock=nil; const AValue:Single=0;
                         const ChangedEvent:TNotifyEvent=nil); virtual;

      procedure Assign(Source:TPersistent); override;
    published
      property X:Single read GetX write SetX stored IsXStored;
      property Y:Single read GetY write SetY stored IsYStored;
    end;

    TPointXYZFloat=class(TPointBlock)
    private
      IDefault : Single;

      function GetValue: Single;
      function GetX:Single; {$IFDEF D10}inline;{$ENDIF}
      function GetY:Single; {$IFDEF D10}inline;{$ENDIF}
      function GetZ:Single; {$IFDEF D10}inline;{$ENDIF}

      function IsXStored:Boolean;
      function IsYStored:Boolean;
      function IsZStored:Boolean;

      procedure ReadZZero(Reader: TReader);

      procedure SetValue(const Value: Single);
      procedure SetX(const Value: Single);
      procedure SetY(const Value: Single);
      procedure SetZ(const Value: Single);

      procedure WriteZZero(Writer: TWriter);
    protected
      procedure DefineProperties(Filer:TFiler); override;
      procedure InitDefault(const Value:Single);
    public
      Point   : TPoint3DFloat;

      Constructor Create(const AOwner:TVisualBlock=nil; const AValue:Double=0;
                         const ChangedEvent:TNotifyEvent=nil); virtual;

      procedure Assign(Source:TPersistent); override;

      function AsRound:TPoint3D;
      procedure SetPoint(const X,Y,Z:Single); overload;
      procedure SetPoint(const Value:TPoint3DFloat); overload;
    published
      property Value:Single read GetValue write SetValue stored False;
      property X:Single read GetX write SetX stored IsXStored;
      property Y:Single read GetY write SetY stored IsYStored;
      property Z:Single read GetZ write SetZ stored IsZStored;
    end;

    TVisualBlock=class(TComponent)
    private
      FLocation : TPointXYZFloat;
      FSize     : TPointXYZFloat;

      procedure SetLocation(const Value:TPointXYZFloat);
      procedure SetSize(const Value: TPointXYZFloat);
    protected
      {$IFNDEF BLOCKS}
      IsCloneable : Boolean; // Experimental
      {$ENDIF}

      procedure AddPathPoint(const X,Y:Single; const AColor:TColor=clDefault); virtual;
      procedure ClearPath; virtual;
      procedure SetBounds(const R:TRect; const Z0,Z1:Single;
                          const Height:TCoordinate);
      procedure SetFormatting(const APen:TTeePen; const ABrush:TTeeBrush;
                              SetBrushImage:Boolean;
                              AColor:TColor; ATransparency:Byte); virtual; abstract;
      procedure SizeChanged(Sender:TObject); virtual;
    public
      {$IFDEF BLOCKS}
      IsCloneable : Boolean; // Experimental
      {$ENDIF}

      Constructor Create(AOwner: TComponent); override;
      Destructor Destroy; override;

      procedure Assign(Source: TPersistent); override;
      procedure Repaint; virtual; abstract;
      function Editor(const AOwner:TComponent; Embeddable:Boolean=False):TControl; virtual; abstract;
    published
      property Location:TPointXYZFloat read FLocation write SetLocation;
      property Size:TPointXYZFloat read FSize write SetSize;
    end;

    TEdgeStyle=(edFlat,edCurved,edNone);

    TTeeTransform=class
    public
      Enabled   : Boolean;
      Origin    : TPoint3DFloat;
      Translate : TPoint3DFloat;
      Rotate    : Single;
      Scale     : TPoint3DFloat;
      Clip      : Boolean;
      Bounds    : TRect;
    end;

    {$IFDEF FMX}
    {$DEFINE FMXTRANSFORMS}
    {$ENDIF}

     TCanvas3D=class(TTeeCanvas {$IFNDEF FPC}, IInterface{$ENDIF})
     private
       {$IFDEF FMXTRANSFORMS}
       type TActiveMatrix=record
         Active : Boolean;
         Matrix : TMatrix;
       end;
     private
       {$ENDIF}

       IDisabledRotation : Integer;

       {$IFDEF FMXTRANSFORMS}
       ITransform : Array of TActiveMatrix;
       {$ENDIF}

       function Calc3DPointsFloat(const Points:Array of TPointFloat; Z:Integer):TPointFloatArray;
     protected
       F3DOptions    : TView3DOptions;
       FIsNoBMPGrid  : Boolean;  // bitmap avoidance TGLCanvas, PDFCanvas
       FIsOrthogonal : Boolean;
       FTransform    : TTeeTransform; // global panel transform
       FXCenter      : Integer;
       FYCenter      : Integer;
       FZCenter      : Integer;
       GradientZ     : Integer;

       SupportsID    : Boolean;

       INoFixAlign   : Boolean; // For TIsoSurfaceSeries and GDI+

       function BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock; virtual;
       procedure EndEntity; virtual;
       procedure DrawLighting(AEdgeStyle: TEdgeStyle; ZPos: Integer;
                              const Z0BevelRect, Z1BevelRect, Z0DonutRect: TRect;
                              IStartAngle, IEndAngle: Integer;
                              const Middle0: TPoint;
                              IsDonut: Boolean); virtual;
       procedure CalcPieAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer; out Theta,Theta2:Single);
       function CheckFastBrush:Boolean; virtual;
       Function GetPixel3D(X,Y,Z:Integer):TColor; virtual; abstract;
       Function GetSupports3DText:Boolean; virtual; abstract;
       procedure SetPixel3D(X,Y,Z:Integer; Value: TColor); virtual; abstract;

       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); virtual;

       // IInterface
       {$IFDEF FPC}
       function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; cdecl;
       function _AddRef: Integer; cdecl;
       function _Release: Integer; cdecl;
       {$ELSE}
       function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
       function _AddRef: Integer; stdcall;
       function _Release: Integer; stdcall;
       {$ENDIF}

       {$IFDEF FMX}
       property Brush:TTeeBrush read FBrush write FBrush;
       property Pen:TTeePen read FPen write FPen;
       property Font:TTeeFont read FFont write FFont;
       {$ENDIF}
     public
       RotationCenter : TPoint3DFloat;

       Constructor Create; virtual;

       class function Description:String; virtual; { bcos BCB, do not make abstract }
       class function EditorClass:String; virtual;

       // 3D
       Function CalcRect3D(Const R:TRect; Z:Integer):TRect;
       Procedure Calculate2DPosition(Var x,y:Integer; z:Integer); overload; virtual; abstract;
       Function Calculate3DPosition(const P:TPoint3D):TPoint; overload; {$IFDEF 10}inline;{$ENDIF}
       Function Calculate3DPosition(P:TPoint; z:Integer):TPoint; overload; {$IFDEF 10}inline;{$ENDIF}
       Function Calculate3DPosition(x,y,z:Integer):TPoint; overload; virtual; abstract;

       {$IFDEF FMX}
       Function Calculate3DPosition(const x,y,z:Single):TPoint; overload; virtual; abstract;
       {$ENDIF}

       {$IFDEF PIEFLOATS}
       {$IFNDEF FMX}
       procedure Calculate3DPosition(var P:TPointFloat; const zPos:Single); overload; virtual; abstract;
       {$ENDIF}
       {$ENDIF}

       function Calc3DPoints(const Points:Array of TPoint; z:Integer):TPointArray;

       procedure FourPointsFromRect(Const R:TRect; Z:Integer; out P:TFourPoints);

       Function RectFromRectZ(Const R:TRect; Z:Integer):TRect;

       Function PointFromCircle(const Rect:TRect; const Degrees:Double;
                           zPos:Integer=0; ClockWise:Boolean=False): {$IFDEF PIEFLOATS}TPointFloat{$ELSE}TPoint{$ENDIF};

       class Function PointFromSpiral(const Rect:TRect; const Degrees,Twist:Double): TPoint; {$IFDEF D9}static;{$ENDIF}

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

       class function IsAvailable:Boolean; virtual;

       Procedure Assign(const Source:TCanvas3D); virtual;
       procedure AssignFont3D(const AFont:TTeeFont);

       Procedure Projection(MaxDepth:Integer; const ABounds,Rect:TRect); virtual; abstract;
       Procedure ShowImage( const DestCanvas,DefaultCanvas:TCanvas;
                            Const UserRect:TRect); virtual; abstract;
       Function ReDrawBitmap:Boolean; virtual; abstract;
       function RenderToBitmap(var ABitmap:TBitmap; const R:TRect):Boolean; virtual;

       procedure Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY, Z: Integer); overload;

       Procedure Arrow( Filled:Boolean; Const FromPoint,ToPoint:TPoint;
                        ArrowWidth,ArrowHeight,Z:Integer); overload;
       Procedure Arrow( Filled:Boolean; Const FromPoint,ToPoint:TPoint;
                        ArrowWidth,ArrowHeight,Z0,Z1:Integer;
                        const ArrowPercent:Double); overload; virtual; abstract;

       procedure ClipCube(Const Rect:TRect; MinZ,MaxZ:Integer); virtual; abstract;
       procedure Cone( Vertical:Boolean; Left,Top,Right,Bottom,Z0,Z1:Integer;
                       Dark3D:Boolean; ConePercent:Integer); virtual; abstract;
       Procedure Cube( Left,Right,Top,Bottom,Z0,Z1:Integer;
                       DarkSides:Boolean=True; RoundSize:Integer=0); overload; virtual; abstract;
       Procedure Cube( Left,Right,Top,Bottom,Z0,Z1,BevelSize:Integer;
                       DarkSides:Boolean=True; RoundSize:Integer=0); overload; virtual; abstract;
       Procedure Cube( const R:TRect; Z0,Z1:Integer;
                       DarkSides:Boolean=True; RoundSize:Integer=0); overload;

       function CubeHull(const Rect:TRect; MinZ,MaxZ:Integer):TPointArray;

       procedure Cylinder( Vertical:Boolean; Left,Top,Right,Bottom,Z0,Z1:Integer;
                           Dark3D:Boolean=True); virtual; abstract;

       procedure Diamond(const R:TRect; Z0,Z1:Integer); overload;
       procedure Diamond(const Left,Top,Right,Bottom:TCoordinate; Z0,Z1:Integer); overload; virtual;

       procedure DisableRotation; virtual;
       procedure EllipseWithZ(const Rect:TRect; Z: Integer); overload;
       procedure EllipseWithZ(const X1, Y1, X2, Y2:TCoordinate;
                             const Z:Integer); overload; virtual; abstract;
       procedure EnableRotation; virtual;
       Procedure HorizLine3D({$IFDEF FMX}const{$ENDIF} Left,Right,Y,Z:TCoordinate); overload; virtual;
       procedure RoundRect(Const R:TRect; X,Y,Z0,Z1:Integer); overload; virtual;
       Procedure VertLine3D({$IFDEF FMX}const{$ENDIF} X,Top,Bottom,Z:TCoordinate); overload; virtual;
       Procedure ZLine3D({$IFDEF FMX}const{$ENDIF} X,Y,Z0,Z1:TCoordinate); virtual;

       procedure FrontPlaneBegin; virtual;
       procedure FrontPlaneEnd; virtual;

       function GetArrowPoints(const ArrowPercent:Single;
                               const FromPoint,ToPoint:TPoint;
                               ArrowWidth,ArrowHeight,
                               Z:Integer):TPointArray; overload;

       Procedure LineWithZ({$IFDEF FMX}const{$ENDIF} X0,Y0,X1,Y1,Z:TCoordinate);  overload; virtual;
       Procedure LineWithZ(const FromPoint,ToPoint:TPoint; Z:Integer); overload;

       procedure MoveTo3D(const X,Y,Z:TCoordinate); overload; virtual;
       procedure MoveTo3D(const P:TPoint3D); overload;
       {$IFNDEF FMX}
       procedure MoveTo3D(const X,Y,Z:Single); overload; virtual;
       {$ENDIF}

       procedure LineTo3D(const X,Y,Z:TCoordinate); overload; virtual;
       procedure LineTo3D(const P:TPoint3D); overload;
       {$IFNDEF FMX}
       procedure LineTo3D(const X,Y,Z:Single); overload; virtual;
       {$ENDIF}

       procedure Pie3D( XCenter,YCenter,XRadius,YRadius,Z0,Z1:Integer;
                        Const StartAngle,EndAngle:Double;
                        DarkSides,DrawSides:Boolean;
                        DonutPercent:Integer=0;
                        Gradient:TCustomTeeGradient=nil;
                        BevelPercent:Integer=0;
                        EdgeStyle:TEdgeStyle=edNone;
                        Transparency:TTeeTransparency=0;
                        BevelBright:Integer=0;
                        BevelBorder:Boolean=True;
                        HideSides:Boolean=False); virtual; abstract;

       procedure Plane3D(Const A,B:TPoint; Z0,Z1:Integer); virtual; abstract;
       procedure PlaneWithZ(const P:TFourPoints; Z:Integer); overload;
       procedure PlaneWithZ(P1,P2,P3,P4:TPoint; Z:Integer); overload; virtual; abstract;
       procedure PlaneFour3D(Var Points:TFourPoints; Z0,Z1:Integer); virtual; abstract;
       Procedure Polygon3D(const Points: Array of TPoint3D); overload; virtual; abstract;

       {$IFNDEF FMX}
       procedure Polygon3DConcave({$IFDEF D6}const{$ENDIF} Points: array of TPoint3DFloat; const Invert:Boolean=False); overload; virtual;
       {$ENDIF}

       procedure Polygon3DConcave({$IFDEF D6}const{$ENDIF} Points: array of TPoint3D; const Invert:Boolean=False); overload; virtual;

       procedure PolygonWithZ(const Points:Array of TPoint; Z:Integer); virtual;
       procedure PolygonConcave({$IFDEF D6}const{$ENDIF} Points:Array of TPoint; Z:Integer); overload; virtual;

       {$IFNDEF FMX}
       procedure PolygonFloatWithZ(const Points:Array of TPointFloat; Z:Integer);
       {$ENDIF}

       procedure Polyline(const Points:Array of TPoint; Z:Integer); overload; virtual;
       procedure PolylineWithZ(const Points:Array of TPointFloat; Z:Integer);

       procedure Pyramid( Vertical:Boolean; Left,Top,Right,Bottom,z0,z1:Integer;
                          DarkSides:Boolean=True); virtual; abstract;
       Procedure PyramidTrunc(Const R:TRectF; const StartZ,EndZ,TruncX,TruncZ:Double;
                              TopCover:Boolean=True; BottomCover:Boolean=True); virtual;
       Procedure Rectangle(Const R:TRect; Z:Integer); overload;
       Procedure Rectangle(const X0,Y0,X1,Y1:TCoordinate; Z:Integer); {$IFDEF 10}inline;{$ENDIF} overload;
       Procedure RectangleWithZ(Const Rect:TRect; Z:Integer); virtual; abstract;
       Procedure RectangleY(const Left,Top,Right:TCoordinate;
                            const Z0,Z1:Integer); virtual; abstract;
       Procedure RectangleZ(const Left,Top,Bottom:TCoordinate;
                            const Z0,Z1:Integer); virtual; abstract;
       procedure RotatedEllipse(Left,Top,Right,Bottom,Z:Integer; Const Angle:Double);
       procedure RotateLabel3D( x,y,z:Integer; const St:String;
                                RotDegree:Double;
                                AllowHTML:Boolean=False); virtual; abstract;
       procedure RoundRectWithZ(const R:TRect; X,Y,Z:Integer); virtual;
       procedure Sphere(x,y,z:Integer; Const Radius:Double); overload; virtual; abstract;
       procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic; const Pos:Double;
                             Plane:TCanvas3DPlane=cpZ); overload; virtual;
       procedure Surface3D( Style:TTeeCanvasSurfaceStyle;
                            SameBrush,CreateCached,AddNormals:Boolean;
                            NumXValues,NumZValues:Integer;
                            const CalcPoints:TTeeCanvasCalcPoints;
                            var Cached:TTeeCachedObject); virtual; abstract;
       Procedure TextOut3D(x,y,z:Integer; const Text:String); overload;
       Procedure TextOut3D(x,y,z:Integer; const Text:String; AllowHTML:Boolean); overload; virtual; abstract;
       procedure Triangle3D( const Points:TTrianglePoints3D;
                             const Colors:TTriangleColors3D); virtual; abstract;
       Procedure TrianglePointsTo2D(const P:TTrianglePoints3D; out Result:TTrianglePoints);

       procedure Triangles({$IFDEF D6}const{$ENDIF} P:Array of TPoint3D;
                           CreateCached:Boolean;
                           var Cached:TTeeCachedObject); virtual;

       procedure TriangleWithZ(const P1,P2,P3:TPoint; Z:Integer); virtual; abstract;

       procedure TriSurface(NumTriangles:Integer; CreateCached,AddNormals:Boolean;
                            const Blend:TTeeBlend; Transparency:TTeeTransparency;
                            const CalcPoints:TTeeCanvasTriPoints;
                            var Cached:TTeeCachedObject); virtual;

       property Pixels3D[X,Y,Z:Integer]:TColor read GetPixel3D write SetPixel3D;
       property Supports3DText:Boolean read GetSupports3DText;
       property View3DOptions:TView3DOptions read F3DOptions write F3DOptions;
       property XCenter:Integer read FXCenter write FXCenter;
       property YCenter:Integer read FYCenter write FYCenter;
       property ZCenter:Integer read FZCenter write FZCenter;
     end;

     TCirclePoints=Array of TPointFloat;

     TTeeCanvas3D=class(TCanvas3D)
     private
       s1,
       s2,
       s3,
       c1,
       c2,
       c3              : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};

       c2s3            : Double;
       c2c3            : Double;
       tempXX          : Double;
       tempYX          : Double;
       tempXZ          : Double;
       tempYZ          : Double;

       FWas3D          : Boolean;

       FBitmap         : TBitmap;

       FMonochrome     : Boolean;

       {$IFDEF FMX}
       FTextAlign      : TCanvasTextAlign;
       FX              : Single;
       FY              : Single;
       {$ENDIF}

       IFastLine       : TTeeCanvasHandle;
       IHasPerspec     : Boolean;
       IHasTilt        : Boolean;
       IOrthoX         : Double;
       IOrthoY         : Double;
       IZoomPerspec    : Double;

       procedure DirectTextOut(AHandle:TTeeCanvasHandle; x,y:Integer; const Text:String);

       procedure GetCirclePoints(var P:TCirclePoints; const X1, Y1, X2, Y2: TCoordinate); overload;

       procedure InternalCylinder(Vertical:Boolean; Left,Top,Right,Bottom,
                            Z0,Z1:Integer; Dark3D:Boolean; ConePercent:Integer);

       {$IFDEF FMX}
       procedure PrepareBrush;
       {$ELSE}
       procedure SetFontQuality(var LogRec:TLogFont);
       {$ENDIF}
     protected
       FBufferedDisplay : Boolean;
       FDirty           : Boolean;
       FIs3D            : Boolean;

       IKeepBitmap      : Boolean;
       IPoints          : TFourPoints;
       IZoomFactor      : Double;

       CharacterAngle   : Double;
       XCenterOffset    : Integer;
       YCenterOffset    : Integer;

       Pie3DUsingArcs   : Boolean;
       FontQuality      : TFontQuality; // GDI only

       Tag              : TObject;

       {$IFNDEF FMX}
       function TranspBitmap(const AGraphic:TGraphic; const R:TRect):TBitmap; overload;
       function TranspBitmap(const AGraphic:TGraphic; const X,Y:Integer):TBitmap; overload;
       {$ENDIF}

       function CheckFastBrush:Boolean; override;

       { 2d }
       procedure FastLineTo(const X,Y:Integer); override;

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

       {$IFNDEF FMX}
       Function GetHandle:TTeeCanvasHandle; override;
       function CanvasStyle:TCanvasStyle; override;
       {$ENDIF}

       Function GetMonochrome:Boolean; override;

       Function GetPixel(X, Y: Integer):TColor; override;
       Function GetPixel3D(X,Y,Z:Integer):TColor; override;
       Function GetSupports3DText:Boolean; override;
       Function GetSupportsFullRotation:Boolean; override;
       Function GetSupportsXORMode:Boolean; override;
       Function GetIsNoBMPGrid:Boolean; override;
       Function GetTextAlign:TCanvasTextAlign; override;
       Function GetUseBuffer:Boolean; override;

       Procedure PolygonFour; virtual;

       Procedure SetBackColor(const Value:TColor); override;
       Procedure SetBackMode(const Value:TCanvasBackMode); override;
       Procedure SetBitmap(ABitmap:TBitmap); virtual;
       procedure SetFastLine(const Active:Boolean); override;
       Procedure SetMonochrome(Value:Boolean); override;
       procedure SetPixel(X, Y: Integer; Value: TColor); override;
       procedure SetTextAlign(const Value:TCanvasTextAlign); override;
       Procedure SetUseBuffer(Value:Boolean); override;

       Procedure DeleteBitmap; virtual;
       Procedure TransferBitmap(ALeft,ATop:Integer; const ACanvas:TCanvas); virtual;

       { 3d private }
       Procedure Calc3DTPoint(Var P:TPoint; z:Integer); {$IFDEF D9}inline;{$ENDIF}
       Function Calc3DTPoint3D(Const P:TPoint3D):TPoint; {$IFDEF D9}inline;{$ENDIF}
       Procedure Calc3DPoint(out P:TPoint; x,y,z:Integer); overload; {$IFDEF D10}inline;{$ENDIF}
       Procedure Calc3DPoint(out P:TPoint; x,y,z:{$IFDEF FMX}Single{$ELSE}Double{$ENDIF}); overload;

       { 3d }
       procedure SetPixel3D(X,Y,Z:Integer; Value: TColor); override;

       Procedure Calc3DPos(var x,y:Integer; z:Integer); overload;

       Procedure CalcPerspective(const Rect:TRect);

       Procedure CalcTrigValues;

       {$IFNDEF FMX}
       function GetNewFont(AHandle:TTeeCanvasHandle; ASize:Integer):HFont;
       {$ENDIF}

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

       class function Description:String; override;

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

       Function ReDrawBitmap:Boolean; override;
       function RenderToBitmap(var ABitmap:TBitmap; const R:TRect):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;

       {$IFDEF FMX}
       procedure Donut( const R:TRect; const StartAngle,EndAngle,HolePercent:Double); override;
       {$ENDIF}

       procedure Donut( XCenter,YCenter,XRadius,YRadius:Integer;
                        Const StartAngle,EndAngle,HolePercent:Double); override;
       procedure Draw(X, Y: Integer; const Graphic: TGraphic); override;
       procedure Ellipse(const X1, Y1, X2, Y2: TCoordinate); override;
       procedure EraseBackground(const Rect: TRect); override;
       procedure FillRect(const Rect: TRect); override;
       procedure LineTo(const X,Y:TCoordinate); override;
       procedure MoveTo(const X,Y:TCoordinate); override;
       procedure Pie(const R:TRect; const StartAngle,EndAngle:Double); override;
       procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); override;
       procedure Rectangle(const X0,Y0,X1,Y1:TCoordinate); override;
       procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer); override;
       procedure StretchDraw(const ARect: TRect; const Graphic: TGraphic); override;
       Procedure TextOut({$IFDEF FMX}const{$ENDIF} X,Y:TCoordinate; const Text:String); override;

       { 2d extra }
       procedure ClipRectangle(Const Rect:TRect); override;
       procedure ClipCube(Const Rect:TRect; MinZ,MaxZ:Integer); override;

       procedure DisableRotation; override;
       procedure EnableRotation; override;

       procedure GradientFill(const Gradient:TCustomTeeGradient; const Rect:TRect); overload;

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

       Procedure Invalidate; override;
       Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;

       {$IFNDEF FMX}
       procedure PolylineFloat(const Points:Array of TPointFloat); override;
       procedure PolylineFloat(const Points:Array of TPoint3DFloat); overload; virtual;
       {$ENDIF}

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

       {$IFDEF PIEFLOATS}
       {$IFNDEF FMX}
       procedure PolygonFloat(const Points:Array of TPointFloat); override;
       {$ENDIF}
       {$ENDIF}

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

       procedure UnClipRectangle; override;

       { 3d }
       Procedure Calculate2DPosition(Var x,y:Integer; z:Integer); overload; override;
       Function Calculate3DPosition(x,y,z:Integer):TPoint; override;

       {$IFDEF FMX}
       Function Calculate3DPosition(const x,y,z:Single):TPoint; override;
       {$ENDIF}

       {$IFDEF PIEFLOATS}
       {$IFNDEF FMX}
       Procedure Calculate3DPosition(var P:TPointFloat; const zPos:Single); override;
       {$ENDIF}
       {$ENDIF}

       Procedure Projection(MaxDepth:Integer; const ABounds,Rect:TRect); override;

       Procedure Arrow( Filled:Boolean; Const FromPoint,ToPoint:TPoint;
                        ArrowWidth,ArrowHeight,Z0,Z1:Integer;
                        const ArrowPercent:Double); override;

       procedure Cone( Vertical:Boolean; Left,Top,Right,Bottom,Z0,Z1:Integer;
                       Dark3D:Boolean; ConePercent:Integer); override;
       Procedure Cube(Left,Right,Top,Bottom,Z0,Z1,BevelSize:Integer;
                      DarkSides:Boolean=True; RoundSize:Integer=0); override;
       Procedure Cube(Left,Right,Top,Bottom,Z0,Z1:Integer;
                      DarkSides:Boolean=True; RoundSize:Integer=0); override;
       procedure Cylinder( Vertical:Boolean; Left,Top,Right,Bottom,Z0,Z1:Integer;
                           Dark3D:Boolean=True); override;
       procedure EllipseWithZ(const X1, Y1, X2, Y2: TCoordinate;
                              const Z:Integer); override;
       procedure GetCirclePoints(var P:TCirclePoints; X1, Y1, X2, Y2, Z: Integer); overload;
       Procedure RectangleZ(const Left,Top,Bottom:TCoordinate;
                            const Z0,Z1:Integer); override;
       Procedure RectangleY(const Left,Top,Right:TCoordinate;
                            const Z0,Z1:Integer); override;

       procedure Pie3D( XCenter,YCenter,XRadius,YRadius,Z0,Z1:Integer;
                        Const StartAngle,EndAngle:Double;
                        DarkSides,DrawSides:Boolean; DonutPercent:Integer=0;
                        Gradient:TCustomTeeGradient=nil;
                        BevelPercent:Integer=0;
                        EdgeStyle:TEdgeStyle=edNone;
                        Transparency:TTeeTransparency=0;
                        BevelBright:Integer=0;
                        BevelBorder:Boolean=True;
                        HideSides:Boolean=False); override;

       procedure Plane3D(Const A,B:TPoint; Z0,Z1:Integer); override;
       procedure PlaneWithZ(P1,P2,P3,P4:TPoint; Z:Integer); override;
       procedure PlaneFour3D(Var Points:TFourPoints; Z0,Z1:Integer); override;
       Procedure Polygon3D(const Points: Array of TPoint3D); override;
       procedure Pyramid( Vertical:Boolean; Left,Top,Right,Bottom,z0,z1:Integer;
                          DarkSides:Boolean=True); override;
       Procedure RectangleWithZ(Const Rect:TRect; Z:Integer); override;
       procedure Sphere(x,y,z:Integer; Const Radius:Double); overload; override;

       procedure Surface3D( Style:TTeeCanvasSurfaceStyle;
                            SameBrush,CreateCached,AddNormals:Boolean;
                            NumXValues,NumZValues:Integer;
                            const CalcPoints:TTeeCanvasCalcPoints;
                            var Cached:TTeeCachedObject); override;

       Procedure TextOut3D(x,y,z:Integer; const Text:String; AllowHtml:Boolean); overload; override;

       procedure Triangle3D( Const Points:TTrianglePoints3D;
                             Const Colors:TTriangleColors3D); override;
       procedure TriangleWithZ(Const P1,P2,P3:TPoint; Z:Integer); override;

       property Bitmap:TBitmap read FBitmap write SetBitmap;
     end;

     TTeeNeutralCanvas=class(TTeeCanvas3D)
     private
       FBackColor : TColor;
       FBackMode  : TCanvasBackMode;
       FTextAlign : TCanvasTextAlign;

       ITranspStack : Array of Single;
     protected
       ITransp      : Single; // Current Transparency (top of stack)

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

       procedure SetBackColor(const Value:TColor); override;
       procedure SetBackMode(const Value:TCanvasBackMode); override;
       procedure SetInterCharSize(ASize:Integer); override;
       procedure SetPenMode(const APen:TTeePen); override;
       procedure SetTextAlign(const Value:TCanvasTextAlign); override;
     public
       Constructor Create; override;

       function BeginBlending(const R:TRect; Transparency:TTeeTransparency):TTeeBlend; override;
       Procedure ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                             DiffRegion:Boolean=False); override;
       procedure EndBlending(const Blend:TTeeBlend); override;
       Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;
     end;

     TCanvas3DClass=class of TCanvas3D;

     {$IFNDEF BLOCKS}
     TTeeFontStyle=(fsNormal, fsOutline, fsBitmap);
     {$ENDIF}

{$IFNDEF FMX}

{$IFDEF TEEVCL}
type
  TButtonGetColorProc=function:TColor of object;

  TTeeButton = class(TButton)
  private
    FSymbolWidth : Integer;
    FSymbolVisible : Boolean;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
    {$ENDIF}
    {$ENDIF}

    procedure SetSymbolVisible(Const Value:Boolean);
    procedure SetSymbolWidth(const Value:Integer);
  protected
    Instance : TObject;
    Info     : PPropInfo;

    Procedure DrawSymbol(const ACanvas:TTeeCanvas); virtual; // abstract;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;

    {$IFDEF TEEWINDOWS}
    procedure PaintWindow(DC: HDC); override;
    {$ENDIF}

    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    {$ENDIF}
    {$ENDIF}

    function SymbolRectangle:TRect;
  public
    constructor Create(AOwner: TComponent); override;
    Procedure LinkProperty(AInstance:TObject; Const PropName:String);
  published
    { Published declarations }

    {$IFNDEF FMX}property Height default 25;{$ENDIF}
    property SymbolVisible:Boolean read FSymbolVisible write SetSymbolVisible default True;
    property SymbolWidth:Integer read FSymbolWidth write SetSymbolWidth default 0;
    {$IFNDEF FMX}property Width default 75;{$ENDIF}
  end;

  TButtonColor = class(TTeeButton)
  private
    FFullOpen      : Boolean;
    FSymbolColor   : TColor;

    Function GetSymbolColor : TColor;
    procedure SetSymbolColor(Const Value:TColor);
  protected
    procedure DrawSymbol(const ACanvas:TTeeCanvas); override;
  public
    GetColorProc : TButtonGetColorProc;

    Constructor Create(AOwner:TComponent); override;

    procedure Click; override;
    property SymbolColor:TColor read GetSymbolColor write SetSymbolColor;
  published
    property FullOpen:Boolean read FFullOpen write FFullOpen default True;
  end;

  TComboFlat=class(TComboBox)
  private
    {$IFNDEF FMX}
    Inside: Boolean;

    {$IFNDEF LCL}
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
    {$ENDIF}

    {$ENDIF}

    function GetDropWidth:Integer;
    function IsDropWidthStored:Boolean;
    procedure SetDropWidth(const Value:Integer);
  public
    Constructor Create(AOwner:TComponent); override;

    procedure Add(const Item:String);

    {$IFNDEF D6}
    procedure AddItem(Item: String; AObject: TObject);
    {$ENDIF}

    function CurrentItem:String;
    function SelectedObject:TObject;
  published
    property DropDownWidth:Integer read GetDropWidth write SetDropWidth
                                   stored IsDropWidthStored;
    {$IFNDEF FMX}property Height default 21;{$ENDIF}
    {$IFNDEF FMX}property ItemHeight default 13;{$ENDIF}
    property ItemIndex;
    {$IFNDEF FMX}property Style default csDropDownList;{$ENDIF}
  end;
{$ENDIF}
{$ENDIF}

Function ApplyDark(Color:TColor; HowMuch:Byte):TColor;
Function ApplyBright(Color:TColor; HowMuch:Byte):TColor;

Function Point3D(const x,y,z:Integer):TPoint3D; overload; {$IFDEF D9}inline;{$ENDIF}
Function Point3D(const P:TPoint; z:Integer):TPoint3D; overload; {$IFDEF D9}inline;{$ENDIF}

{$IFNDEF FMX}
{$IFNDEF D17}
Function Point3D(const X,Y,Z:Single):TPoint3DFloat; overload; {$IFDEF D9}inline;{$ENDIF}
{$ENDIF}
Function Point3D(const P:TPoint3D):TPoint3DFloat; overload; {$IFDEF D9}inline;{$ENDIF}
{$ENDIF}

Procedure SwapExtended(Var a,b:Extended); {$IFDEF D10}inline;{$ENDIF}   { exchanges a and b }
Procedure SwapDouble(Var a,b:Double); {$IFDEF D10}inline;{$ENDIF}   { exchanges a and b }
Procedure SwapSingle(Var a,b:Single); {$IFDEF D10}inline;{$ENDIF}
Procedure SwapInteger(Var a,b:Integer); {$IFDEF D10}inline;{$ENDIF} { exchanges a and b }

Procedure RectSize(Const R:TRect; out RectWidth,RectHeight:Integer); overload;{$IFDEF D9}inline;{$ENDIF}
Procedure RectSize(Const R:TRectF; out RectWidth,RectHeight:Single); overload;{$IFDEF D9}inline;{$ENDIF}

Procedure RectCenter(Const R:TRect; out X,Y:Integer); overload; {$IFDEF D9}inline;{$ENDIF}
function RectCenter(Const R:TRect):TPoint; overload; {$IFDEF D9}inline;{$ENDIF}
Procedure RectCenter(Const R:TRectF; out X,Y:Single); overload; {$IFDEF D9}inline;{$ENDIF}

Function RectFromPolygon(Const Points:Array of TPoint; NumPoints:Integer):TRect;
Function RectFromTriangle(Const Points:TTrianglePoints):TRect; {$IFDEF D9}inline;{$ENDIF}

{ Returns True if rectangle Small is inside the rectangle Big }
function RectangleInRectangle(const Small,Big:TRect):Boolean;

Procedure ClipCanvas(const ACanvas:TCanvas; Const Rect:TRect);
Procedure UnClipCanvas(const ACanvas:TCanvas);

// These Clipxxx routines are now deprecated...
// Use TTeeCanvas.Clipxxx equivalent methods.

Procedure ClipEllipse(const ACanvas:TTeeCanvas; Const Rect:TRect); {$IFDEF D10}inline;{$ENDIF}
                         {$IFDEF D11}deprecated 'Please use ACanvas.ClipEllipse method.';{$ENDIF} // Do not localize

Procedure ClipRoundRectangle(const ACanvas:TTeeCanvas; Const Rect:TRect; RoundSize:Integer); {$IFDEF D10}inline;{$ENDIF}
                         {$IFDEF D11}deprecated 'Please use ACanvas.ClipRectangle method.';{$ENDIF} // Do not localize

Procedure ClipPolygon(const ACanvas:TTeeCanvas; const Points:Array of TPoint; NumPoints:Integer);
                         {$IFDEF D11}deprecated 'Please use ACanvas.ClipPolygon method.';{$ENDIF} // Do not localize

Const
  // This is used to calculate average Text Height
  TeeCharForHeight     = 'W';  // Do not localize

  DarkerColorQuantity  : Byte=128; { <-- for dark 3D sides }
  DarkColorQuantity    : Byte=64;

{$IFNDEF TEEVCL}
procedure FreeAndNil(var Obj);
function StrToInt(const S: String): Integer;
function ColorToRGB(Color: TColor): Longint;
{$ENDIF}

var {$IFNDEF FMX}
    IsWindowsNT:Boolean=False;
    {$ENDIF}

    GetDefaultFontSize:Integer=0;
    GetDefaultFontName:String='';

{$IFNDEF FMX}
{$IFNDEF USEWINDOWS}
Function GetRValue(const Color:Integer):Byte; inline;
Function GetGValue(const Color:Integer):Byte; inline;
Function GetBValue(const Color:Integer):Byte; inline;
Function RGB(const r,g,b:Integer):TColor; inline;
{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}

{$IFDEF MSWINDOWS}
Function TeeCreatePenSmallDots(const AColor:TColor; const Space:Integer=0):HPen;
{$ENDIF}

Procedure TeeSetTeePen(const FPen:TPen; const APen:TTeePen; AColor:TColor);
{$ENDIF}

{$IFDEF FMX}
Function TeePoint(const aX,aY:Single):{$IFDEF FMX}TPointF{$ELSE}TPointFloat{$ENDIF}; {$IFDEF D9}inline;{$ENDIF}
{$ELSE}
Function TeePoint(const aX,aY:Integer):TPoint; overload; {$IFDEF D9}inline;{$ENDIF}
Function TeePoint(const P:TPointFloat):TPoint; overload; {$IFDEF D9}inline;{$ENDIF}
Function TeePointFloat(const P:TPoint):TPointFloat; overload; {$IFDEF D9}inline;{$ENDIF}
{$ENDIF}

procedure Offset(var P:{$IFDEF FMX}TPointF{$ELSE}TPoint{$ENDIF}; const Amount:TCoordinate); {$IFDEF D9}inline;{$ENDIF}

function PointInRect(Const Rect:TRect; const P:TPoint):Boolean; overload; {$IFNDEF FMX}{$IFDEF D9}inline;{$ENDIF}{$ENDIF}
function PointInRect(Const Rect:TRect; x,y:Integer):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}

{$IFNDEF FMX}
function PointInRect(Const Rect:TRectF; x,y:Integer):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}
{$ENDIF}

function TeeRect(const Left,Top,Right,Bottom:Integer):TRect; overload; {$IFDEF D9}inline;{$ENDIF}
function TeeRect(const Width,Height:Integer):TRect; overload; {$IFDEF D9}inline;{$ENDIF}
function TeeRect(const R:TRect):TRectF; overload; {$IFDEF D9}inline;{$ENDIF}

{$IFDEF FMX}
function PointInRect(Const Rect:TRect; const x,y:Single):Boolean; overload;
function TeeRect(const Width,Height:Single):TRectF; overload;
function TeeRect(const Left,Top,Right,Bottom:Single):TRectF; overload;
function IsRectEmpty(const R:TRect):Boolean;
function ColorToRGB(const Color:TAlphaColor):TAlphaColor; {$IFDEF D9}inline;{$ENDIF}
{$ENDIF}

{$IFDEF FMX}
procedure InflateRectF(var R: TRect; const dX, dY: Single); overload;
function EqualRect(const A,B:TRect):Boolean;
{$ELSE}
procedure InflateRectF(var R: TRectF; const dX, dY: Single); overload;
{$ENDIF}

Function RGBA(const RGBA:TRGBA):TColor; overload;
Function RGBA(const r,g,b,a:Integer):TColor; overload;
function ColorRGBA(const Color:TColor):TRGBA;

{$IFDEF FMX}
function RGB(const R,G,B:Byte):TColor;
{$NODEFINE RGB}

function GetRValue(const C:TColor):Byte;
{$NODEFINE GetRValue}

function GetGValue(const C:TColor):Byte;
{$NODEFINE GetGValue}

function GetBValue(const C:TColor):Byte;
{$NODEFINE GetBValue}

{$ENDIF}

Function OrientRectangle(Const R:TRect):TRect; overload;
Function OrientRectangle(Const Left,Top,Right,Bottom:TCoordinate):TRect; overload;

procedure TeeSetBitmapSize(const Bitmap:TBitmap; Width,Height:Integer);
procedure TeeSetBitmapDPI(const Bitmap: TBitmap; DPI: Integer); overload;
function TeeSetBitmapDPI(const Stream: TStream; DPI: Integer): Boolean; overload;

// Returns bounding rectangle containing all points in polygon P
Function PolygonBounds(Const P:Array of TPoint):TRect; overload;

{$IFNDEF FMX}
Function PolygonBounds(Const P:Array of TPointFloat):TRectF; overload;
{$ENDIF}

// Returns True when all points in polygon A are inside polygon B
function PolygonInPolygon(const A,B:Array of TPoint):Boolean;

// Returns size of P polygon
function PolygonArea(const P:TPointArray): Double;

// Returns "center" point of P polygon ("average" position of all P array XY points)
function PolygonCentroid(const P:TPointArray):{$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF}; overload;

// Default color depth
const
  TeePixelFormat={$IFDEF FMX}pf32Bit{$ELSE}pf24Bit{$ENDIF};

Function RGBValue(const Color:TColor):TRGB;

// Returns point "ATo" minus ADist pixels from AFrom point.
Function PointAtDistance(const AFrom,ATo:TPoint; const ADist:Single):TPoint; overload;
{$IFNDEF FMX}
Function PointAtDistance(const AFrom,ATo:TPointFloat; const ADist:Single):TPointFloat; overload;
{$ENDIF}

// Returns True when 3 first points in P are "face-viewing".
Function TeeCull(const P:TFourPoints):Boolean; overload;
Function TeeCull(const P:TFourPointFloats):Boolean; overload;

Function TeeCull(const P0,P1,P2:TPoint):Boolean; overload;

{$IFNDEF FMX}
Function TeeCull(const P0,P1,P2:TPointFloat):Boolean; overload;
{$ENDIF}

// Draws SRC bitmap with smooth stretch to Dst bitmap
type
  TSmoothStretchOption = (ssBestQuality, ssBestPerformance);

procedure SmoothStretch(Src, Dst: TBitmap); overload;
procedure SmoothStretch(Src, Dst: TBitmap; Option: TSmoothStretchOption); overload;

// Returns Sqrt( Sqr(x)+Sqr(y) )
Function TeeDistance(const x,y:Double):Double; {$IFDEF D9}inline;{$ENDIF}

{$IFNDEF FMX}
var
  TeeFontAntiAlias:Byte=ANTIALIASED_QUALITY;
      // CLEARTYPE_QUALITY = 5,
      // CLEARTYPE_NATURAL_QUALITY = 6

  TeeSetDCBrushColor:function(DC: HDC; Color: COLORREF): COLORREF; stdcall;
  TeeSetDCPenColor:function(DC: HDC; Color: COLORREF): COLORREF; stdcall;
{$ENDIF}

{$IFNDEF FMX}
{$IFNDEF LINUX}
// Load a DLL, compatible with Delphi 4 and up.
Function TeeLoadLibrary(Const FileName:String):HInst;

// Free Library, but do not free library in Windows 95 (lock bug)
Procedure TeeFreeLibrary(hLibModule: HMODULE);
{$ENDIF}
{$ENDIF}

var
  TeeNumCylinderSides:Integer=32;

procedure TeeBlendBitmaps(Const Percent: Double;
                          ABitmap,BBitmap:TBitmap; BOrigin:TPoint;
                          TransparentMask:TBitmap=nil);

{$IFNDEF FMX}
type
  TGradientRotate=procedure(const Gradient:TCustomTeeGradient; const Bitmap:TBitmap);

var
  TeeGradientRotate:TGradientRotate=nil;
{$ENDIF}

// Returns "Lines" array with pointers to all Bitmap row scanlines.

{$IFNDEF FMX}
{$IFNDEF LCL}
procedure TeeCalcLines(out Lines:TRGBArray; Bitmap:TBitmap); overload;
{$ENDIF}
{$ENDIF}

procedure TeeCalcLines(out Lines:TRGBAArray; Bitmap:TBitmap); overload;

// Returns canvas, graphics or printer driver capabilities
Function TeeGetDeviceCaps(Handle:TTeeCanvasHandle; Cap:Integer):Integer;

// Returns the number of bits per pixel on a given Bitmap or Canvas.
// ie: 32 = RGBA pixels.  24 = RGB pixels.
function TeeBitsperPixel(AHandle:TTeeCanvasHandle):Integer;

// Smoothes Bitmap contents, blends with (optional) Back bitmap.
procedure TeeShadowSmooth(const Bitmap,Back:TBitmap;
                          Left, Top, Width, Height:Integer;
                          BlurSize:Integer;
                          const Smoothness:Single;
                          ACanvas:TCanvas3D;
                          Clip:Boolean);

type
  ICanvasHyperlinks=interface
    ['{84DBB276-CBD2-4BBB-AC31-AACBF8B6F34C}']
    procedure AddLink(x,y:Integer; const Text,URL,Hint:String);
  end;

  ICanvasToolTips=interface
    ['{03EBCB55-D01F-4CA8-8A2F-48EB3BEBC5E3}']
    procedure AddToolTip(const Entity,ToolTip:String);
  end;

function TeeResourceStream(const AName:String):TResourceStream;

{$IFNDEF D6}
function Supports(const Instance: IInterface; const IID: TGUID): Boolean;
{$ENDIF}

type
  TeeNativeInt={$IFDEF D16}NativeInt{$ELSE}{$IFDEF FPC}PtrInt{$ELSE}Integer{$ENDIF}{$ENDIF};

function ObjectToTag(const AObject:TObject):TeeNativeInt; {$IFDEF D9}inline;{$ENDIF}

// GetClass function compatible with FMX and VCL
procedure TeeActivateGroup;
function TeeGetClass(const AClassName:String):TClass;

type
  {$IFNDEF FMX}
  TVisualEditor=class(TForm)
  private
    FOnDirty : TNotifyEvent;
  public
    procedure MarkDirty;
    procedure RefreshBlock(const AVisual:TVisualBlock); virtual;

    property OnDirty:TNotifyEvent read FOnDirty write FOnDirty;
  end;

  // BCB6 C++ problem, due to QC88686 bug, this type should be moved "up"
  // to the start of its "type" declaration.
  // Error was fixed in XE2:
  // http://qc.embarcadero.com/wc/qcmain.aspx?d=88686
  // "The symbol ... has zero size, or excessive offset in module ..."
  TVisualEditorClass=class of TVisualEditor;
  {$ENDIF}

  TChangeVisual=function(AOwner,AVisualOwner:TComponent; AVisual:TVisualBlock):TVisualBlock;
  TEditVisual=function(AOwner:TComponent; AVisual:TVisualBlock):Boolean;

  TTeeAddFontNames=Procedure(const Items:TStrings);

var
  TeeOnChangeVisual : TChangeVisual=nil;
  TeeOnEditVisual   : TEditVisual=nil;

  {$IFNDEF FMX}
  TeeVisualEditorClass:TVisualEditorClass=nil;
  {$ENDIF}

  TeeRenderClasses:TList{$IFDEF TEELISTMETA}<TCanvas3DClass>{$ENDIF}; // QC115377
  TeeAddFontNames:TTeeAddFontNames=nil;

{$IFDEF TEEVCL}
// Show the TColorDialog, return new color if changed
Function EditColor(const AOwner:TComponent; AColor:TColor):TColor; overload;

// Show the TColorDialog, return new color if changed
Function EditColor(const AOwner:TComponent; AColor:TColor; const AOptions:TColorDialogOptions):TColor; overload;

// Show the TColorDialog, return True if color changed
Function EditColorDialog(const AOwner:TComponent; var AColor:TColor; const AOptions:TColorDialogOptions=[]):Boolean;
{$ENDIF}

{ Used at EditColor function, for the Color Editor dialog }
var TeeCustomEditColors:TStrings=nil;

// Moved from TeeProcs
Procedure TeeDrawCheckBox( x,y:Integer;
                           const Canvas:{$IFDEF FMX}TTeeCanvas{$ELSE}TCanvas{$ENDIF};
                           Checked:Boolean;
                           ABackColor:TColor; CheckBox:Boolean=True);

// Speed-optimized Hypotenuse (RTL Math.Hypot is precision-optimized, but 80% slower)
function TeeHypot(const X,Y:Integer):Single; overload;
function TeeHypot(const X,Y:Single):Single; overload;

implementation

Uses {$IFDEF FMX}
     FMX.Filter, FMX.Effects, FMX.Forms, FMX.Colors,

     {$IFDEF HASCANVASD2D}
     FMX.Canvas.D2D, Winapi.D2D1,
     {$ENDIF}

     {$IFDEF HASCANVASGDIP}
     FMX.Canvas.GDIP, Winapi.GdipObj, Winapi.GdipAPI,
     {$ENDIF}

     {$IFDEF HASCANVASGPU}
     FMX.Canvas.GPU,
     {$ENDIF}

     {$IFDEF HASCANVASMAC}
     FMX.Canvas.Mac, Macapi.CoreGraphics, Macapi.CocoaTypes,
     {$ENDIF}

     {$IFDEF HASCANVASANDROID}
     Androidapi.Gles, Androidapi.Gles2, FMX.Context.GLES,
     {$ENDIF}

     {$ENDIF}

     Math,

     {$IFDEF D21}
     System.Rtti,
     {$ENDIF}

     {$IFDEF FMX}
     {$IFDEF FMX_DEBUG}
     TeeHtml, TeeConst
     {$ELSE}
     FMXTee.Html, FMXTee.Constants
     {$ENDIF}
     {$ELSE}
     {$IFDEF D16}
     VCLTee.TeeHtml, VCLTee.TeeConst
     {$ELSE}
     TeeHtml, TeeConst
     {$ENDIF}
     {$ENDIF}
     ;

{$IFNDEF FMX}
{$R TeeFonts.res}
{$ENDIF}

{$IFDEF FMX}
const
  clBlack  = TAlphaColors.Black;
  clWhite  = TAlphaColors.White;
  clSilver = TAlphaColors.Silver;
  clGray   = TAlphaColors.Gray;
  clLtGray = TAlphaColors.LtGray;
{$ENDIF}

type PPoints = ^TPoints;
     TPoints = Array[0..0] of TPoint;

{$IFNDEF FMX}
var
  ClipHistory : Array of HRgn;

procedure DeleteClipHistory;
{$IFNDEF LCL}
var t : Integer;
{$ENDIF}
begin
  {$IFNDEF LCL}
  for t:=0 to Length(ClipHistory)-1 do
      DeleteObject(ClipHistory[t]);
  {$ENDIF}

  ClipHistory:=nil;
end;
{$ENDIF}

{$IFDEF FMX}
function EqualRect(const A,B:TRect):Boolean;
begin
  result:=(A.Left=B.Left) and (A.Top=B.Top) and (A.Right=B.Right) and (A.Bottom=B.Bottom);
end;

procedure InflateRectF(var R: TRect; const dX, dY: Single); overload;
begin
  R.Left:=R.Left-dX;
  R.Top:=R.Top-dY;
  R.Right:=R.Right+dX;
  R.Bottom:=R.Bottom+dY;
end;
{$ELSE}
procedure InflateRectF(var R: TRectF; const dX, dY: Single); overload;
begin
  R.Left:=R.Left-dX;
  R.Top:=R.Top-dY;
  R.Right:=R.Right+dX;
  R.Bottom:=R.Bottom+dY;
end;
{$ENDIF}

Function TeeCull(const P0,P1,P2:TPoint):Boolean;
begin
  result:=( ((P0.x-P1.x) * (P2.y-P1.y)) -
            ((P2.x-P1.x) * (P0.y-P1.y))
          ) < 0;
end;

Function TeeCull(const P:TFourPoints):Boolean; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=TeeCull(P[0],P[1],P[2]);
end;

{$IFNDEF FMX}
Function TeeCull(const P0,P1,P2:TPointFloat):Boolean;
begin
  result:=( ((P0.x-P1.x) * (P2.y-P1.y)) -
            ((P2.x-P1.x) * (P0.y-P1.y))
          ) < 0;
end;
{$ENDIF}

Function TeeCull(const P:TFourPointFloats):Boolean;
begin
  result:=TeeCull(P[0],P[1],P[2]);
end;

{$IFDEF FMX}
function ColorToRGB(const Color:TAlphaColor):TAlphaColor; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=Color;
end;
{$ELSE}

{$IFNDEF D16}
procedure InflateRect(var R: TRect; const dX, dY: Integer); overload;
begin
  R.Left := R.Left - dX;
  R.Right := R.Right + dX;
  R.Top := R.Top - dY;
  R.Bottom := R.Bottom + dY;
end;
{$ENDIF}

{$ENDIF}

function ColorRGBA(const Color:TColor):TRGBA;
begin
  {$IFDEF FMX}
  result.Red   := TAlphaColorRec(Color).R;
  result.Green := TAlphaColorRec(Color).G;
  result.Blue  := TAlphaColorRec(Color).B;
  result.Alpha := TAlphaColorRec(Color).A;
  {$ELSE}
  result.Red   := (Color AND $000000FF);
  result.Green := (Color AND $0000FF00) SHR 8;
  result.Blue  := (Color AND $00FF0000) SHR 16;
  result.Alpha := 0;
  {$ENDIF}
end;

{$IFDEF FMX}
Function TeePoint(const aX,aY:Single):{$IFDEF FMX}TPointF{$ELSE}TPointFloat{$ENDIF}; {$IFDEF D9}inline;{$ENDIF}
begin
  with result do
  begin
    X:=aX;
    Y:=aY;
  end;
end;
{$ELSE}
Function TeePoint(const aX,aY:Integer):TPoint; {$IFDEF D9}inline;{$ENDIF}
begin
  with result do
  begin
    X:=aX;
    Y:=aY;
  end;
end;

Function TeePoint(const P:TPointFloat):TPoint; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=Round(P.X);
  result.Y:=Round(P.Y);
end;

Function TeePointFloat(const P:TPoint):TPointFloat; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=P.X;
  result.Y:=P.Y;
end;

{$ENDIF}

procedure Offset(var P:{$IFDEF FMX}TPointF{$ELSE}TPoint{$ENDIF}; const Amount:TCoordinate); {$IFDEF D9}inline;{$ENDIF}
begin
  P.X:=P.X+Amount;
  P.Y:=P.Y+Amount;
end;

procedure TeeGaugeAddFontNames(const Items:TStrings);
begin
  Items.Add(TeeFontElektra);
  Items.Add(TeeFontDigital);
end;

{$IFDEF FMX}
procedure AddCustomFonts;
begin
end;

procedure RemoveCustomFonts;
begin
end;
{$ELSE}
{$IFNDEF LCL}
var
  TeeFontLCD : THandle=0;
  TeeFontLED : THandle=0;
  TeeGDI32   : THandle=0;

function LoadGDI32:Boolean;
begin
  if TeeGDI32=0 then
     TeeGDI32:=TeeLoadLibrary(GDI32);

  result:=TeeGDI32<>0;
end;

procedure AddCustomFonts;
var
  TeeAddFont : function(p1: Pointer; p2: DWORD; p3: PDesignVector; p4: LPDWORD): THandle; stdcall;

  function AddFontFromResource(const ResName:String):THandle;
  var
    tmp     : TResourceStream;
    MemSize : Int64;
    Buffer  : Pointer;
    tmpC    : DWORD;
  begin
    result:=0;

    tmp:=TeeResourceStream(resName);

    if Assigned(tmp) then
    try
      MemSize:=tmp.Size;  // Windows 2003 "+1" ??

      GetMem(Buffer, MemSize);
      try
        tmp.Read(Buffer^,MemSize);
        tmpC:=1;

        result:=TeeAddFont(Buffer,MemSize,nil,@tmpC);

        //SendMessage(Application.Handle,WM_FONTCHANGE,0,0);
      finally
        FreeMem(Buffer);
      end;
    finally
      tmp.Free;
    end;
  end;

  procedure DoAddFonts;
  const
    resFile1='TEELCDFONT';
    resFile2='TEELEDFONT';

  begin
    TeeFontLCD:=AddFontFromResource(resFile1);
    TeeFontLED:=AddFontFromResource(resFile2);

    // Screen.ResetFonts;  <-- EnumFonts does not recognize newly added fonts,
    // so we use here a "hook" TeeAddFontNames pseudo-event called from TeeEdiFont
    TeeAddFontNames:=TeeGaugeAddFontNames;
  end;

begin
  TeeAddFontNames:=nil;

  if LoadGDI32 then
  begin
    @TeeAddFont:=GetProcAddress(TeeGDI32,'AddFontMemResourceEx'); // Do not localize

    if Assigned(@TeeAddFont) then
       DoAddFonts;
  end;
end;

procedure RemoveCustomFonts;
var
  TeeRemoveFont:function(p1: THandle): BOOL; stdcall;

  procedure DoRemoveFonts;
  begin
    if TeeFontLCD<>0 then
       TeeRemoveFont(TeeFontLCD);

    if TeeFontLED<>0 then
       TeeRemoveFont(TeeFontLED);
  end;

begin
  TeeAddFontNames:=nil;

  if LoadGDI32 then
  begin
    @TeeRemoveFont:=GetProcAddress(TeeGDI32,'RemoveFontMemResourceEx'); // Do not localize

    if Assigned(@TeeRemoveFont) then
       DoRemoveFonts;
  end;
end;
{$ENDIF}
{$ENDIF}

{$IFDEF FMX}
function PointInRect(Const Rect:TRect; const x,y:Single):Boolean; overload;
begin
  result:=(x>=Rect.Left) and (y>=Rect.Top) and
          (x<=Rect.Right) and (y<=Rect.Bottom);
end;
{$ENDIF}

function PointInRect(Const Rect:TRect; x,y:Integer):Boolean; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=(x>=Rect.Left) and (y>=Rect.Top) and
          (x<=Rect.Right) and (y<=Rect.Bottom);
end;

function PointInRect(Const Rect:TRect; const P:TPoint):Boolean; overload; {$IFNDEF FMX}{$IFDEF D9}inline;{$ENDIF}{$ENDIF}
begin
  result:=PointInRect(Rect,P.X,P.Y);
end;

{$IFNDEF FMX}
function PointInRect(Const Rect:TRectF; x,y:Integer):Boolean; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=(x>=Rect.Left) and (y>=Rect.Top) and
          (x<=Rect.Right) and (y<=Rect.Bottom);
end;
{$ENDIF}

{$IFDEF FMX}
function TeeRect(const Left,Top,Right,Bottom:Single):TRectF; overload;
begin
  result.Left  :=Left;
  result.Top   :=Top;
  result.Right :=Right;
  result.Bottom:=Bottom;
end;

function TeeRect(const Width,Height:Single):TRectF; overload;
begin
  result.Left  :=0;
  result.Top   :=0;
  result.Right :=Width;
  result.Bottom:=Height;
end;

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

{$ENDIF}

function TeeRect(const R:TRect):TRectF; {$IFDEF D9}inline;{$ENDIF}
begin
  result.Left:=R.Left;
  result.Top:=R.Top;
  result.Right:=R.Right;
  result.Bottom:=R.Bottom;
end;

function TeeRect(const Left,Top,Right,Bottom:Integer):TRect; {$IFDEF D9}inline;{$ENDIF}
begin
  result.Left  :=Left;
  result.Top   :=Top;
  result.Right :=Right;
  result.Bottom:=Bottom;
end;

function TeeRect(const Width,Height:Integer):TRect; {$IFDEF D9}inline;{$ENDIF}
begin
  result.Left  :=0;
  result.Top   :=0;
  result.Right :=Width;
  result.Bottom:=Height;
end;

// Makes sure the R rectangle Left is smaller than Right and
// Top is smaller than Bottom. Returns corrected rectangle.
Function OrientRectangle(const R:TRect):TRect;
begin
  with R do
       result:=OrientRectangle(Left,Top,Right,Bottom);
end;

Function OrientRectangle(const Left,Top,Right,Bottom:TCoordinate):TRect;
begin
  if Left>Right then
  begin
    result.Left:=Right;
    result.Right:=Left;
  end
  else
  begin
    result.Left:=Left;
    result.Right:=Right;
  end;

  if Top>Bottom then
  begin
    result.Top:=Bottom;
    result.Bottom:=Top;
  end
  else
  begin
    result.Top:=Top;
    result.Bottom:=Bottom;
  end;
end;

Function Point3D(const x,y,z:Integer):TPoint3D; {$IFDEF D9}inline;{$ENDIF}
begin
  result.x:=x;
  result.y:=y;
  result.z:=z;
end;

Function Point3D(const P:TPoint; z:Integer):TPoint3D; {$IFDEF D9}inline;{$ENDIF}
begin
  result.x:={$IFDEF FMX}Round(P.X){$ELSE}P.X{$ENDIF};
  result.y:={$IFDEF FMX}Round(P.Y){$ELSE}P.Y{$ENDIF};
  result.z:=z;
end;

{$IFNDEF FMX}
{$IFNDEF D17}
// Already at System.Types
Function Point3D(const X,Y,Z:Single):TPoint3DFloat; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=X;
  result.Y:=Y;
  result.Z:=Z;
end;
{$ENDIF}

Function Point3D(const P:TPoint3D):TPoint3DFloat; {$IFDEF D9}inline;{$ENDIF}
begin
  result.X:=P.X;
  result.Y:=P.Y;
  result.Z:=P.Z;
end;
{$ENDIF}

Procedure RectSize(Const R:TRect; out RectWidth,RectHeight:Integer); {$IFDEF D9}inline;{$ENDIF}
begin
  With R do
  begin
    RectWidth :={$IFDEF FMX}System.Round(Right-Left){$ELSE}Right-Left{$ENDIF};
    RectHeight:={$IFDEF FMX}System.Round(Bottom-Top){$ELSE}Bottom-Top{$ENDIF};
  end;
end;

Procedure RectSize(Const R:TRectF; out RectWidth,RectHeight:Single); overload;{$IFDEF D9}inline;{$ENDIF}
begin
  With R do
  begin
    RectWidth :={$IFDEF FMX}System.Round(Right-Left){$ELSE}Right-Left{$ENDIF};
    RectHeight:={$IFDEF FMX}System.Round(Bottom-Top){$ELSE}Bottom-Top{$ENDIF};
  end;
end;

Procedure RectCenter(Const R:TRect; out X,Y:Integer); overload; {$IFDEF D9}inline;{$ENDIF}
begin
  With R do
  begin
    X:={$IFDEF FMX}System.Round((Left+Right)*0.5){$ELSE}(Left+Right) div 2{$ENDIF};
    Y:={$IFDEF FMX}System.Round((Top+Bottom)*0.5){$ELSE}(Top+Bottom) div 2{$ENDIF};
  end;
end;

Procedure RectCenter(Const R:TRectF; out X,Y:Single); overload; {$IFDEF D9}inline;{$ENDIF}
begin
  With R do
  begin
    X:=(Left+Right)*0.5;
    Y:=(Top+Bottom)*0.5;
  end;
end;

function RectCenter(Const R:TRect):TPoint; overload; {$IFDEF D9}inline;{$ENDIF}
begin
  With R do
  begin
    result.X:=(Left+Right) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    result.Y:=(Top+Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
  end;
end;

procedure TeeSetBitmapSize(const Bitmap:TBitmap; Width,Height:Integer);
begin
  if (Bitmap.Width<>Width) or (Bitmap.Height<>Height) then
  begin
    {$IFDEF D10}
    // VCL and FMX
    Bitmap.SetSize(Width,Height); // Faster
    {$ELSE}
    // VCL only, pre-D10:
    Bitmap.Width:=Width;
    Bitmap.Height:=Height;
    {$ENDIF}
  end;
end;

//http://delphicikk.atw.hu/listaz.php?id=788&oldal=30
procedure TeeSetBitmapDPI(const Bitmap: TBitmap; DPI: Integer);
var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Bitmap.SaveToStream(Stream);
    if TeeSetBitmapDPI(Stream, DPI) then
    begin
      Stream.Position := 0;
      Bitmap.LoadFromStream(Stream);
    end;
  finally
    Stream.Free;
  end;
end;

function TeeSetBitmapDPI(const Stream: TStream; DPI: Integer): Boolean;
var Data: Word;
begin
  Data := Round(DPI * 39.370079);
  Stream.Position := 38;
  result:=Stream.Write(Data, 2) = 2;

  if result then
  begin
    Stream.Position := 42;
    result:=Stream.Write(Data, 2) = 2;
  end;
end;

// Returns the minimum left / top and the
// maximum right / bottom for all the points in "P" polygon
Function PolygonBounds(const P:Array of TPoint):TRect;
var l : Integer;
begin
  l:=Length(P);

  if l>0 then
     result:=RectFromPolygon(P,l)
  else
     result:=TeeZeroRect;
end;

{$IFNDEF FMX}
Function PolygonBounds(Const P:Array of TPointFloat):TRectF; overload;
var l,t : Integer;
begin
  l:=Length(P);

  if l>0 then
  with result do
  begin
    Top:=P[0].y;
    Left:=P[0].x;
    Bottom:=Top;
    Right:=Left;

    for t:=1 to l-1 do
    with P[t] do
    begin
      if X<Left then Left:=X
      else
      if X>Right then Right:=X;

      if Y<Top then Top:=Y
      else
      if Y>Bottom then Bottom:=Y;
    end;
  end
  else
    result:=TeeZeroRectF;
end;
{$ENDIF}

function PolygonInPolygon(const A,B:Array of TPoint):Boolean;

  Function InternalPointInPolygon(Const P:TPoint):Boolean;
  var i,j : Integer;
  begin
    result:=False;
    j:=High(B);
    for i:=0 to High(B) do
    begin
      if (((( B[i].Y <= P.Y ) and ( P.Y < B[j].Y ) ) or
           (( B[j].Y <= P.Y ) and ( P.Y < B[i].Y ) )) and
            ( P.X <= (1.0* (( B[j].X - B[i].X ) * ( P.Y - B[i].Y )))
            / (1.0*( B[j].Y - B[i].Y )) + B[i].X )) then
               result:=not result;
      j:=i;
    end;
  end;

var t : Integer;
begin
  for t:=0 to Length(A)-1 do
      if not InternalPointInPolygon(A[t]) then
      begin
        result:=False;
        exit;
      end;

  result:=True;
end;

function PolygonArea(const P:TPointArray): Double;
var N,t,tt : Integer;
begin
  result:=0;

  N:=Length(P);

  for t:=0 to N-1 do
  begin
    tt:= (t + 1) mod N;
    result:=result + (P[t].x * P[tt].y) - (P[t].y * P[tt].x);
  end;

  result:=Abs(result*0.5);
end;

function PolygonCentroid(const P:TPointArray):{$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF};
var Sign,
    Factor : Double;
    N,i,j : Integer;
begin
  N:=Length(P);

  if N>2 then
  begin
    result.x:=0;
    result.y:=0;

    sign:=0;

    for i:=0 to N-1 do
    begin
      j:= (i + 1) mod N;
      Factor:= ( P[i].x*P[j].y-P[j].x*P[i].y);
      sign:=sign+Factor;
      result.x:=result.x+ ( P[i].x+P[j].x)*Factor;
      result.y:=result.y+ ( P[i].y+P[j].y)*Factor;
    end;

    if Sign=0 then
    begin
      result.x:=0;
      result.y:=0;
    end
    else
    begin
      sign:=sign*0.5;

      Factor:=1.0/(6.0*sign);

      result.x:=result.x*Factor;
      result.y:=result.y*Factor;
    end;
  end
  else
  if N>1 then
  begin
    result.x:=(P[1].x+P[0].x)*0.5;
    result.y:=(P[1].y+P[0].y)*0.5;
  end
  else
  if N>0 then
  begin
    result.x:=P[0].x;
    result.y:=P[0].y;
  end
  else
  begin
    result.x:=0;
    result.y:=0;
  end;
end;

{ TTeeCorner }

Constructor TTeeCorner.Create(const Changed:TNotifyEvent);
begin
  inherited Create;
  IChanged:=Changed;
end;

procedure TTeeCorner.Assign(Source:TPersistent);
begin
  if Source is TTeeCorner then
     FStyle:=TTeeCorner(Source).FStyle
  else
     inherited;
end;

procedure TTeeCorner.SetStyle(const Value:TTeeCornerStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;

    if Assigned(IChanged) then
       IChanged(Self);
  end;
end;

{ TTeeCorners }

Constructor TTeeCorners.Create(const Changed:TNotifyEvent=nil);
begin
  inherited Create;

  FLeftBottom:=TTeeCorner.Create(Changed);
  FLeftTop:=TTeeCorner.Create(Changed);
  FRightBottom:=TTeeCorner.Create(Changed);
  FRightTop:=TTeeCorner.Create(Changed);
end;

Destructor TTeeCorners.Destroy;
begin
  FRightTop.Free;
  FRightBottom.Free;
  FLeftTop.Free;
  FLeftBottom.Free;

  inherited;
end;

procedure TTeeCorners.Assign(Source:TPersistent);
begin
  if Source is TTeeCorners then
  begin
    FLeftBottom.Assign(TTeeCorners(Source).FLeftBottom);
    FLeftTop.Assign(TTeeCorners(Source).FLeftTop);
    FRightBottom.Assign(TTeeCorners(Source).FRightBottom);
    FRightTop.Assign(TTeeCorners(Source).FRightTop);
  end
  else
    inherited;
end;

procedure TTeeCorners.SetLeftBottom(const Value:TTeeCorner);
begin
  FLeftBottom.Assign(Value);
end;

procedure TTeeCorners.SetLeftTop(const Value:TTeeCorner);
begin
  FLeftTop.Assign(Value);
end;

procedure TTeeCorners.SetRightBottom(const Value:TTeeCorner);
begin
  FRightBottom.Assign(Value);
end;

procedure TTeeCorners.SetRightTop(const Value:TTeeCorner);
begin
  FRightTop.Assign(Value);
end;

function TTeeCorners.GetPath(const ACanvas:TTeeCanvas; const R:TRect; X,Y:Integer):TTeeBasePath;
var dx,dy : Integer;
    offX,
    offY  : Integer;
begin
  result:=ACanvas.CreatePath;
  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;

  dx := {$IFDEF FMX}Round{$ENDIF}(R.Right-R.Left);
  dy := {$IFDEF FMX}Round{$ENDIF}(R.Bottom-R.Top);

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

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

  case FRightTop.Style of
    cosRound: begin
                if FLeftTop.Style=cosNone then
                begin
                  result.MoveTo(R.TopLeft);
                  result.LineTo(R.Right-X,R.Top);
                end;

                result.ArcTo(R.Right-X, R.Top, X, Y, 270, 90);
              end;
    cosBevel: begin
                result.MoveTo(R.Right-(X div 2),R.Top);
                result.LineTo(R.Right,R.Top+(Y div 2));
              end;
  else
    result.MoveTo(R.Right,R.Top);
  end;

  if FRightBottom.Style=cosRound then
  begin
    if FRightTop.Style<>cosRound then
       result.LineTo(R.Right,R.Bottom-Y);

    result.ArcTo(R.Right-X, R.Bottom-Y, x, y, 0, 90);
  end
  else
  begin
    if FRightTop.Style=cosRound then
       result.MoveTo(R.Right,R.Top+Y);

    if FRightBottom.Style=cosBevel then
    begin
      result.LineTo(R.Right,R.Bottom-(Y div 2));
      result.LineTo(R.Right-(X div 2),R.Bottom);
    end
    else
       result.LineTo(R.BottomRight);
  end;

  if FLeftBottom.Style=cosRound then
  begin
     if FRightBottom.Style<>cosRound then
        result.LineTo(R.Left+X,R.Bottom);

     result.ArcTo(R.Left, R.Bottom-Y, x, y, 90, 90);
  end
  else
  begin
    if FRightBottom.Style=cosRound then
       result.MoveTo(R.Right-X,R.Bottom);

    if FLeftBottom.Style=cosBevel then
    begin
      result.LineTo(R.Left+(X div 2),R.Bottom);
      result.LineTo(R.Left,R.Bottom-(Y div 2));
    end
    else
       result.LineTo(R.Left,R.Bottom);
  end;

  if FLeftTop.Style=cosRound then
  begin
     if FLeftBottom.Style<>cosRound then
        result.LineTo(R.Left,R.Top+Y);

     result.ArcTo(R.Left, R.Top, x, y, 180, 90);
  end
  else
  begin
    if FLeftBottom.Style=cosRound then
       result.MoveTo(R.Left,R.Bottom-Y);

    if FLeftTop.Style=cosBevel then
    begin
      result.LineTo(R.Left,R.Top+(Y div 2));
      result.LineTo(R.Left+(X div 2),R.Top);
    end
    else
    if FRightTop.Style<>cosRound then
       result.LineTo(R.TopLeft);
  end;

  result.Close;
end;

{ TTeePath }

type
  {$IFDEF FMX}
  FMXTPathData={$IFDEF D19}FMX.Graphics.TPathData{$ELSE}FMX.Types.TPathData{$ENDIF};
  {$ENDIF}

  TTeePath=class(TTeeBasePath)
  private
    ICanvas : TTeeCanvas;

    {$IFDEF FMX}
    IPath   : FMXTPathData;
    {$ENDIF}
  public
    Constructor Create(const Canvas:TTeeCanvas);

    {$IFDEF FMX}
    Destructor Destroy; override;
    {$ENDIF}

    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;

{ TTeePen }

Constructor TTeePen.Create(const OnChangeEvent:TNotifyEvent=nil);
begin
  inherited Create;

  FVisible:=True;
  DefaultVisible:=True;

  // FMX GPU (iOS,Android) speed: faster "flat" ending style than "round":
  DefaultEnd:={$IFDEF IOS}esFlat{$ELSE}esRound{$ENDIF};
  FEndStyle:=DefaultEnd;

  DefaultJoin:=jsRound;
  OnChange:=OnChangeEvent;

  DefaultWidth:=1;

  {$IFDEF FMX}
  FMode:=pmCopy;
  FWidth:=1;
  {$ENDIF}
end;

Destructor TTeePen.Destroy;
begin
  FFill.Free;
  inherited;
end;

function TTeePen.GetPenBrush:TTeeBrush;
begin
  if not Assigned(FFill) then
  begin
    FFill:=TTeeBrush.Create(OnChange);
    {$IFDEF FMX}
    FFill.InitColor(clBlack);
    {$ENDIF}
  end;

  result:=FFill;
end;

procedure TTeePen.InitColor(const AColor:TColor);
begin
  Color:=AColor;
  {$IFDEF FMX}
  Fill.InitColor(AColor);
  {$ENDIF}
end;

function TTeePen.IsGradientVisible:Boolean;
begin
  result:=Assigned(FFill) and FFill.HasGradient;
end;

procedure TTeePen.InitEndStyle(const Value:TPenEndStyle);
begin
  DefaultEnd:=Value;
  FEndStyle:=Value;
end;

procedure TTeePen.SetPenBrush(const Value:TTeeBrush);
begin
  if Assigned(Value) then
     Fill.Assign(Value)
  else
     FreeAndNil(FFill);
end;

Procedure TTeePen.Assign(Source:TPersistent);
begin
  if Source is TTeePen then
  begin
    FVisible  :=TTeePen(Source).Visible;
    FSmallDots:=TTeePen(Source).SmallDots;
    FSmallSpace:=TTeePen(Source).SmallSpace;
    FEndStyle :=TTeePen(Source).EndStyle;
    FJoinStyle:=TTeePen(Source).JoinStyle;
    Fill:=TTeePen(Source).FFill;

    {$IFDEF FMX}
    FWidth:=TTeePen(Source).FWidth;
    FStyle:=TTeePen(Source).FStyle;
    {$ENDIF}
  end;

  {$IFNDEF FMX}
  inherited;
  {$ENDIF}
end;

function TTeePen.HalfWidth:Integer;
begin
  if Visible and (Width>1) then
     result:={$IFDEF FMX}Round{$ENDIF}(Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF})
  else
     result:=0;
end;

procedure TTeePen.Hide;
begin
  Visible:=False;
end;

procedure TTeePen.Show;
begin
  Visible:=True;
end;

Function TTeePen.IsEndStored:Boolean;
begin
  result:=FEndStyle<>DefaultEnd;
end;

Function TTeePen.IsJoinStored:Boolean;
begin
  result:=FJoinStyle<>DefaultJoin;
end;

Function TTeePen.IsVisibleStored:Boolean;
begin
  result:=FVisible<>DefaultVisible;
end;

{$IFDEF FMX}
procedure TTeePen.Changed;
begin
  if Assigned(OnChange) then
     OnChange(Self);
end;
{$ENDIF}

procedure TTeePen.SetEndStyle(const Value: TPenEndStyle);
begin
  if FEndStyle<>Value then
  begin
    FEndStyle:=Value;
    Changed;
  end;
end;

procedure TTeePen.SetJoinStyle(const Value: TPenJoinStyle);
begin
  if FJoinStyle<>Value then
  begin
    FJoinStyle:=Value;
    Changed;
  end;
end;

Procedure TTeePen.SetSmallDots(const Value:Boolean);
begin
  if FSmallDots<>Value then
  begin
    FSmallDots:=Value;
    Changed;
  end;
end;

Procedure TTeePen.SetSmallSpace(const Value:Integer);
begin
  if FSmallSpace<>Value then
  begin
    FSmallSpace:=Value;
    Changed;
  end;
end;

Procedure TTeePen.SetVisible(const Value:Boolean);
Begin
  if FVisible<>Value then
  begin
    FVisible:=Value;
    Changed;
  end;
end;

{ TTeeHiddenPen }
Constructor TTeeHiddenPen.Create(const OnChangeEvent:TNotifyEvent);
Begin
  inherited;
  FVisible:=False;
  DefaultVisible:=False;
end;

{ TDottedGrayPen }
Constructor TDottedGrayPen.Create(const OnChangeEvent:TNotifyEvent);
Begin
  inherited;
  InitColor(clGray);
  Style:=psDot;
end;

{ TDarkGrayPen }
Constructor TDarkGrayPen.Create(const OnChangeEvent:TNotifyEvent);
Begin
  inherited;
  InitColor({$IFDEF FMX}claDarkGray{$ELSE}clDkGray{$ENDIF});
end;

{ TTeeBrush }

Constructor TTeeBrush.Create(const OnChangeEvent:TNotifyEvent=nil);
Begin
  inherited Create{$IFDEF FMX}(bsSolid, clDefault){$ENDIF};

  InitColor(clDefault);
  FBackColor:=clNone;
  OnChange:=OnChangeEvent;
end;

Destructor TTeeBrush.Destroy;
begin
  FGradient.Free;
  FImage.Free;

  // NOTE: XE4 and up, AV when trying to Bitmap.Free because TBrush already
  // destroys Bitmap in BrushResource.
  {$IFNDEF FMX}
  {$IFNDEF D16}
  Bitmap.Free;
  {$ENDIF}
  {$ENDIF}

  inherited;
end;

procedure TTeeBrush.InitColor(const Value:TColor);
var Old : TNotifyEvent;
begin
  Old:=OnChange;
  OnChange:=nil; // to avoid Invalidate;

  Color:=Value;
  DefaultColor:=Value;

  OnChange:=Old;
end;

function TTeeBrush.IsColorStored:Boolean;
begin
  result:=Color<>DefaultColor;
end;

Procedure TTeeBrush.Assign(Source:TPersistent);
begin
  if Source is TTeeBrush then
  begin
    Image:=TTeeBrush(Source).FImage;
    FBackColor:=TTeeBrush(Source).FBackColor;
    FHatchStyle:=TTeeBrush(Source).FHatchStyle;

    if Assigned(TTeeBrush(Source).FGradient) then
       Gradient:=TTeeBrush(Source).FGradient
    else
       FreeAndNil(FGradient);
  end;

  inherited;
end;

{$IFDEF FMX}
procedure TTeeBrush.Changed;
begin
  if Assigned(OnChanged) then
     OnChanged(Self);
end;
{$ENDIF}

Procedure TTeeBrush.Clear;
begin
  Style:=bsClear;
  Image:=nil;
  FreeAndNil(FGradient);
end;

procedure TTeeBrush.SetBackColor(const Value:TColor);
begin
  if FBackColor<>Value then
  begin
    FBackColor:=Value;
    Changed;
  end;
end;

procedure TTeeBrush.SetHatchStyle(const Value: THatchStyle);
begin
  if FHatchStyle<>Value then
  begin
    FHatchStyle:=Value;

    {$IFNDEF FMX}
    if Ord(FHatchStyle)<6 then
       Style:=TBrushStyle(Ord(FHatchStyle)+2);
    {$ENDIF}

    Changed;
  end;
end;

{$IFNDEF FMX}
function TTeeBrush.HasGraphic:Boolean;
begin
  result:=HasImage and Assigned(FImage.Graphic) and (not FImage.Graphic.Empty);
end;
{$ENDIF}

function TTeeBrush.HasImage:Boolean;
begin
  result:=Assigned(FImage);
end;

function TTeeBrush.HasGradient:Boolean;
begin
  result:=Assigned(FGradient) and FGradient.Visible;
end;

procedure TTeeBrush.SetImage(const Value: TTeePicture);
begin
  if Assigned(Value) then
  begin
    Image.Assign(Value);
    Changed;
  end
  else
  if Assigned(FImage) then
  begin
    FreeAndNil(FImage);
    Changed;
  end;
end;

procedure TTeeBrush.SetGradient(const Value:TTeeGradient);
begin
  if Assigned(Value) then Gradient.Assign(Value)
                     else FreeAndNil(FGradient);
  Changed;
end;

Function TTeeBrush.GetImage:TTeePicture;
begin
  if not Assigned(FImage) then
  begin
    FImage:=TTeePicture.Create;
    FImage.OnChange:=OnChange;
  end;

  result:=FImage;
end;

{$IFDEF FMX}
function TTeeBrush.GetOnChanged: TNotifyEvent;
begin
  result:=OnChanged;
end;

procedure TTeeBrush.SetOnChanged(const Value: TNotifyEvent);
begin
  OnChanged:=Value;
end;

function TTeeBrush.GetKind:TBrushKind;
begin
  result:=Kind;
end;

procedure TTeeBrush.SetKind(const Value:TBrushKind);
begin
  Kind:=Value;
end;
{$ENDIF}

function TTeeBrush.GetGradient:TTeeGradient;
begin
  if not Assigned(FGradient) then
  begin
    FGradient:=TTeeGradient.Create(OnChange);

    {$IFDEF FMX}
    FGradient.IBrush:=Self;
    {$ENDIF}
  end;

  result:=FGradient;
end;

{ TView3DOptions }
Constructor TView3DOptions.Create(const {$IFDEF TEEVCL}AParent:{$IFDEF FMX}TControl{$ELSE}TWinControl{$ENDIF}{$ENDIF});
begin
  inherited Create;

  {$IFDEF TEEVCL}
  FParent      :=AParent;
  {$ENDIF}

  FOrthogonal  :=True;
  FOrthoAngle  :=45;
  FFontZoom    :=100; { % }
  FZoom        :=100; { % }
  FZoomText    :=ztManual;
  FRotation    :=345;
  FElevation   :=345;
  FPerspective :=TeeDefaultPerspective; { % }
end;

function TView3DOptions.BackFaced:Boolean;
begin
  result:=(not Orthogonal) and (Rotation>90) and (Rotation<270);
end;

Procedure TView3DOptions.Repaint;
begin
  {$IFDEF TEEVCL}
  {$IFDEF FMX}
  if Assigned(FOnInvalidate) then
     FOnInvalidate(Self);
  {$ELSE}
  FParent.Invalidate;
  {$ENDIF}
  {$ENDIF}
end;

Procedure TView3DOptions.SetIntegerProperty(Var Variable:Integer; Value:Integer);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    Repaint;
  end;
end;

Procedure TView3DOptions.SetBooleanProperty(Var Variable:Boolean; Value:Boolean);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    Repaint;
  end;
end;

function TView3DOptions.GetElevation:Integer;
begin
  result:=Round(FElevation);
end;

Procedure TView3DOptions.SetElevationInteger(const Value:Integer);
begin
  SetElevation(Value);
end;

Procedure TView3DOptions.SetElevation(const Value:Double);
begin
  if FElevation<>Value then
  begin
    FElevation:=Value;
    Repaint;
  end;
end;

Procedure TView3DOptions.SetFontZoom(Value:Integer);
begin
  SetIntegerProperty(FFontZoom,Value);
end;

Procedure TView3DOptions.SetPerspective(Value:Integer);
begin
  SetIntegerProperty(FPerspective,Min(Value,100));
end;

function TView3DOptions.GetHorizOffset:Integer;
begin
  result:=Round(FHorizOffset);
end;

function TView3DOptions.GetVertOffset:Integer;
begin
  result:=Round(FVertOffset);
end;

function TView3DOptions.GetRotation:Integer;
begin
  result:=Round(FRotation);
end;

function TView3DOptions.GetZoom:Integer;
begin
  result:=Round(FZoom);
end;

Procedure TView3DOptions.SetRotationInteger(const Value:Integer);
begin
  SetRotation(Value);
end;

Procedure TView3DOptions.SetRotation(const Value:Double);
begin
  if FRotation<>Value then
  begin
    FRotation:=Value;
    Repaint;
  end;
end;

function TView3DOptions.GetTilt:Integer;
begin
  result:=Round(FTilt);
end;

Procedure TView3DOptions.SetTilt(const Value:Double);
begin
  if FTilt<>Value then
  begin
    FTilt:=Value;
    Repaint;
  end;
end;

Procedure TView3DOptions.SetTiltInteger(Value:Integer);
begin
  SetTilt(Value);
end;

Procedure TView3DOptions.SetHorizInteger(const Value:Integer);
begin
  SetHorizOffset(Value);
end;

Procedure TView3DOptions.SetHorizOffset(const Value:Double);
begin
  if FHorizOffset<>Value then
  begin
    FHorizOffset:=Value;

    Repaint;

    if Assigned(FOnScrolled) then
       FOnScrolled(True);
  end;
end;

Procedure TView3DOptions.SetVertInteger(const Value:Integer);
begin
  SetVertOffset(Value);
end;

Procedure TView3DOptions.SetVertOffset(const Value:Double);
begin
  if FVertOffset<>Value then
  begin
    FVertOffset:=Value;

    Repaint;

    if Assigned(FOnScrolled) then
       FOnScrolled(False);
  end;
end;

Procedure TView3DOptions.SetOrthoAngle(Value:Integer);
begin
  SetIntegerProperty(FOrthoAngle,Value);
end;

Procedure TView3DOptions.SetOrthogonal(Value:Boolean);
begin
  SetBooleanProperty(FOrthogonal,Value);
end;

Procedure TView3DOptions.SetZoomInteger(const Value:Integer);
begin
  SetZoom(Value);
end;

procedure TView3DOptions.SetZOffset(const Value: Double);
begin
  if FZOffset<>Value then
  begin
    FZOffset:=Value;

    Repaint;

    if Assigned(FOnScrolled) then
       FOnScrolled(False);
  end;
end;

Procedure TView3DOptions.SetZoom(const Value:Double);
begin
  if FZoom<>Value then
  begin
    if Assigned(FOnChangedZoom) then
       FOnChangedZoom(Round(Value));

    FZoom:=Value;
    Repaint;
  end;
end;

Procedure TView3DOptions.SetZoomText(const Value:TCanvasZoomText);
begin
  // Compatibility protection against bad old "Boolean" type values read
  // from DFM/*.tee streams:
  if Ord(Value)<>-1 then
     if FZoomText<>Value then
     begin
       FZoomText:=Value;
       Repaint;
     end;
end;

Procedure TView3DOptions.Assign(Source:TPersistent);
begin
  if Source is TView3DOptions then
  With TView3DOptions(Source) do
  begin
    Self.FElevation   :=FElevation;
    Self.FFontZoom    :=FFontZoom;
    Self.FHorizOffset :=FHorizOffset;
    Self.FOrthoAngle  :=FOrthoAngle;
    Self.FOrthogonal  :=FOrthogonal;
    Self.FPerspective :=FPerspective;
    Self.FRotation    :=FRotation;
    Self.FTilt        :=FTilt;
    Self.FVertOffset  :=FVertOffset;
    Self.FZoom        :=FZoom;
    Self.FZoomText    :=FZoomText;
  end;
end;

function TView3DOptions.CalcOrthoRatio: Double;
var tmpSin   : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos   : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpAngle : Double;
begin
  if Orthogonal then
  begin
    tmpAngle:=OrthoAngle;

    if tmpAngle>90 then
       tmpAngle:=180-tmpAngle;

    SinCos(tmpAngle*TeePiStep,tmpSin,tmpCos);

    result:=tmpSin/tmpCos;
  end
  else result:=1;
end;

{ TTeeCanvas }

{$IFDEF FMX}
function TBrushHelper.GetStyle:TBrushKind;
begin
  result:=Kind;
end;

procedure TBrushHelper.SetStyle(const Value:TBrushKind);
begin
  Kind:=Value;
end;
{$ENDIF}

Constructor TTeeCanvas.Create;
begin
  inherited;

  {$IFDEF FMX}
  FSupportsAlpha:=True;
  {$ENDIF}

  FSupportsConcave:=True;

  FPen:=TTeePen.Create(ChangedPen);
  FBrush:=TTeeBrush.Create(ChangedBrush);
  FFont:=TTeeFont.Create(ChangedFont);
end;

Destructor TTeeCanvas.Destroy;
begin
  {$IFDEF TEECACHEDLAYOUT}
  ILayout.Free;
  {$ENDIF}

  FPen.Free;
  FBrush.Free;
  FFont.Free;
  inherited;
end;

{$IFDEF TEECACHEDLAYOUT}
function TTeeCanvas.ICachedLayout(const AX,AY:Integer; const Text:String):TTextLayout;
begin
  if not Assigned(ILayout) then
  begin
    if TCanvasManager.DefaultCanvas = FCanvas.ClassType then
       ILayout := TTextLayoutManager.DefaultTextLayout.Create(FCanvas)
    else
       ILayout := TTextLayoutManager.TextLayoutByCanvas(FCanvas.ClassType).Create(FCanvas);

    ILayout.WordWrap := False;
    ILayout.HorizontalAlign := TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF};
    ILayout.VerticalAlign := TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF};
    ILayout.RightToLeft := False;
  end;

  ILayout.BeginUpdate;
  ILayout.TopLeft := PointF(AX,AY);
  ILayout.MaxSize := PointF(10000,10000);
  ILayout.Text := Text;
  ILayout.Font := FCanvas.Font;
  ILayout.Color := FCanvas.Fill.Color;
  ILayout.Opacity := Opacity;
  ILayout.EndUpdate;

  result:=ILayout;
end;
{$ENDIF}

procedure TTeeCanvas.ChangedPen(Sender:TObject);
begin
  DoChangedPen;
end;

procedure TTeeCanvas.ChangedBrush(Sender:TObject);
begin
  DoChangedBrush(Brush);
end;

procedure TTeeCanvas.ChangedFont(Sender:TObject);
begin
  if not IChangingFont then
     DoChangedFont;
end;

Procedure TTeeCanvas.InternalDark(Const AColor:TColor; Quantity:Byte);
var tmpColor : TColor;
begin
  tmpColor:=ApplyDark(AColor,Quantity);

  if FBrush.Style=bsSolid then
  begin
    FBrush.Color:=tmpColor;

    {$IFDEF FMX}
    FCanvas.Fill.Color:=tmpColor;
    {$ELSE}
    FCanvas.Brush.Color:=tmpColor;
    {$ENDIF}
  end
  else BackColor:=tmpColor;
end;

Function TTeeCanvas.GetFontHeight:Integer;
begin
  result:=TextHeight(TeeCharForHeight);
end;

function TTeeCanvas.GetGradient:TTeeGradient;
begin
  result:=FBrush.Gradient;
end;

function TTeeCanvas.IsGradientVisible:Boolean;
begin
  result:=(FBrush.Style<>bsClear) and FBrush.HasGradient;
end;

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

// For Windows GDI+ only, that cannot accept the "fake" VCL 4 byte colors
// defined in Graphics.pas (the System colors like clWindow, clHighLight, etc)
class function TTeeCanvas.SystemColor(const AColor:TColor):TColor;
begin
  {$IFDEF FMX}
  result:=AColor;
  {$ELSE}

  if AColor=clNone then
     result:=clBlack
  else
  begin
    result:=AColor;

    {$IFNDEF FPC}

    if (Cardinal(result)>={$IFDEF D7}clSystemColor{$ELSE}$80000000{$ENDIF}) and
       (Cardinal({$IFDEF D6}clMenuBar{$ELSE}clInfoBk{$ENDIF})>=Cardinal(result)) then
         result:=ColorToRGB(result);

    {$ENDIF}
  end;
  {$ENDIF}
end;

Procedure TTeeCanvas.SetCanvas(const ACanvas:TCanvas);
begin
  FCanvas:=ACanvas;

  {$IFNDEF TEECANVASLOCKS}
  if Assigned(FCanvas) then
  begin
    {$IFNDEF LCL}
    {$IFNDEF FMX}
    FCanvas.Pen.OwnerCriticalSection:=nil;
    FCanvas.Font.OwnerCriticalSection:=nil;
    FCanvas.Brush.OwnerCriticalSection:=nil;
    {$ENDIF}
    {$ENDIF}
  end;
  {$ENDIF}

  {$IFDEF FMX}

  {$IFDEF HASCANVASD2D}
  FCanvasIsD2D:=(FCanvas<>nil) and SameText(FCanvas.ClassName,'TCanvasD2D');
  {$ENDIF}
  {$IFDEF HASCANVASGDIP}
  FCanvasIsGDIP:=(FCanvas<>nil) and SameText(FCanvas.ClassName,'TCanvasGdiplus');
  {$ENDIF}
  {$IFDEF HASCANVASGPU}
  FCanvasIsGPU:=(FCanvas<>nil) and SameText(FCanvas.ClassName,'TCanvasGpu');
  {$ENDIF}

  {$ENDIF}
end;

Procedure TTeeCanvas.ResetState;
begin
  With FPen do
  begin
    Color:=clBlack;
    Width:=1;
    Style:=psSolid;
  end;

  with FBrush do
  begin
    Color:=clWhite;
    {$IFDEF FMX}Kind{$ELSE}Style{$ENDIF}:=bsSolid;

    if Assigned(FImage) then
       FImage.Graphic:=nil;
  end;

  With FFont do
  begin
    {$IFDEF FMX}
    FBrush.Color:=clBlack;
    {$ELSE}
    Color:=clBlack;
    {$ENDIF}

    Style:=[];

    {$IFNDEF FMX}
    CharSet:=DEFAULT_CHARSET;
    {$ENDIF}

    {$IFDEF FMX}
    Family:=GetDefaultFontName;
    {$ELSE}
    Name:=GetDefaultFontName;
    {$ENDIF}

    Size:=GetDefaultFontSize;

    {$IFNDEF FMX}
    {$IFDEF USESYSLOCALE}
    if (SysLocale.PriLangID=LANG_JAPANESE) then
        Size:=-MulDiv(DefFontData.Height,72,Screen.PixelsPerInch);
        // Delphi 7 and earlier versions : Size = 9
        // Delphi 2005 and later versions: Size = 8
    {$ENDIF}
    {$ENDIF}
  end;

  Gradient.InitDefaults;

  BackColor:=clWhite;
  BackMode:=cbmTransparent;
  TextAlign:=TA_LEFT;
end;

Procedure TTeeCanvas.AssignBrush(const ABrush:TTeeBrush);
begin
  AssignBrush(ABrush,ABrush.BackColor,ABrush.Color);
end;

Procedure TTeeCanvas.AssignBrush(const ABrush:TTeeBrush; ABackColor:TColor);
begin
  AssignBrush(ABrush,ABackColor,ABrush.Color);
end;

// deprecated, please use AssignBrush
Procedure TTeeCanvas.AssignBrushColor(const ABrush:TTeeBrush; AColor,ABackColor:TColor); // deprecated;
begin
  AssignBrush(ABrush,AColor,ABackColor);
end;

Procedure TTeeCanvas.AssignBrush(const ABrush:TTeeBrush; AColor,ABackColor:TColor);

  procedure SetBrushBitmap(ABitmap:{$IFDEF LCL}TCustomBitmap{$ELSE}TBitmap{$ENDIF});
  begin
    Brush.Bitmap{$IFDEF FMX}.Bitmap{$ENDIF}:=ABitmap;

    {$IFNDEF FMX}
    {$IFDEF TEEWINDOWS}
    SetTextColor(Handle,ColorToRGB(AColor));
    {$ENDIF}
    {$ENDIF}

    BackMode:=cbmOpaque;
    BackColor:=ABackColor;
  end;

var tmp: TBitmap;
begin
  FBrush.OnChange:=nil;

  if Assigned(ABrush.FImage) and Assigned(ABrush.FImage.Graphic)
     {$IFDEF FMX}and (not ABrush.FImage.Graphic.IsEmpty) {$ENDIF} then
  begin
    Brush.Style:=bsClear;

    if not (ABrush.FImage.Graphic is TBitmap) then
    begin
      {$IFDEF FMX}
      tmp:=TBitmap.Create(ABrush.FImage.Width, ABrush.FImage.Height);
      tmp.Canvas.DrawBitmap(ABrush.FImage, TRectF.Create(0,0,tmp.Width,tmp.Height),
        TRectF.Create(0,0,tmp.Width, tmp.Height),
        1);
      {$ELSE}

      tmp:=TBitmap.Create;
      TeeSetBitmapSize(tmp,ABrush.FImage.Width,ABrush.FImage.Height);
      tmp.Canvas.Draw(0,0,ABrush.FImage.Graphic);
      {$ENDIF}

      ABrush.FImage.Bitmap:=tmp;
      tmp.Free;
    end;

    SetBrushBitmap(ABrush.FImage.Bitmap);
  end
  else
  if Assigned(ABrush.Bitmap)
     {$IFDEF FMX}and Assigned(ABrush.Bitmap.Bitmap) and (not ABrush.Bitmap.Bitmap.IsEmpty){$ENDIF}
     {$IFDEF CLX}and (not ABrush.Bitmap.Empty){$ENDIF}
     then
  begin
    Brush.Style:=bsClear;
    SetBrushBitmap(ABrush.Bitmap{$IFDEF FMX}.Bitmap{$ENDIF});
  end
  else
  begin
    {$IFDEF FMX}
    Brush.Assign(ABrush);
    {$ELSE}
    Brush.Bitmap:=nil;
    {$ENDIF}

    if Monochrome then
       AColor:=clWhite;

    FBrush.Assign(ABrush);

    if ABrush.Style<>bsClear then
       FBrush.Color:=AColor;

    FBrush.BackColor:=ABackColor;

    {$IFDEF FMX}
    FCanvas.Fill.Assign(ABrush);
    {$ELSE}
    FCanvas.Brush.Assign(ABrush); // <-- redundant for GDI+ etc
    {$ENDIF}
  end;

  if Assigned(ABrush.FGradient) then
     Gradient.Assign(ABrush.Gradient)
  else
     Gradient.Visible:=False;

  FBrush.OnChange:=ChangedBrush;
  DoChangedBrush(FBrush);
end;

procedure TTeeCanvas.DoChangedBrush(const ABrush:TTeeBrush);
begin
  {$IFDEF FMX}
  FCanvas.Fill.Assign(ABrush);
  {$ELSE}
  FCanvas.Brush.Assign(ABrush);
  {$ENDIF}

  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 TTeeCanvas.AssignVisiblePen(const APen:TTeePen);
begin
  AssignVisiblePenColor(APen,APen.Color);
end;

Procedure TTeeCanvas.Rectangle(Const R:TRect);
begin
  with R do Rectangle(Left,Top,Right,Bottom);
end;

Procedure TTeeCanvas.DoRectangle(Const Rect:TRect); // obsolete
begin
  Rectangle(Rect);
end;

function TTeeCanvas.CreatePath:TTeeBasePath;
begin
  result:=CreatePath(nil,nil);
end;

function TTeeCanvas.CreatePath(const Points:TPointFloatArray; const Types:TPathPointTypes):TTeeBasePath;
begin
  result:=TTeePath.Create(Self);
end;

// "Curve" smoothing implemented in GDI+ canvas only:
procedure TTeeCanvas.Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean; const Z:Single);
begin
  if Closed then
     Polygon(Points)
  else
     Polyline(Points);
end;

Procedure TTeeCanvas.DoHorizLine(const X0,X1,Y:TCoordinate);
begin
  MoveTo(X0,Y);
  LineTo(X1,Y);
end;

Procedure TTeeCanvas.DoVertLine(const X,Y0,Y1:TCoordinate);
begin
  MoveTo(X,Y0);
  LineTo(X,Y1);
end;

procedure TTeeCanvas.InternalDrawCheck(x,y:Integer; Checked:Boolean; const ABackColor:TColor; CheckBox:Boolean=True);

  Procedure DoHorizLine(x1,x2,y:Integer);
  begin
    MoveTo(x1,y);
    LineTo(x2,y);
  end;

  Procedure DoVertLine(x,y1,y2:Integer);
  begin
    MoveTo(x,y1);
    LineTo(x,y2);
  end;

var t : Integer;
begin
  Pen.Style:=psSolid;
  Pen.Width:=1;
  Pen.Color:={$IFDEF FMX}claGray{$ELSE}clGray{$ENDIF};

  Brush.Style:=bsSolid;

  Gradient.Visible:=False;
  Brush.Color:=clWhite;

  if CheckBox then
  begin
    DoHorizLine(x+TeeCheckBoxSize,x,y);
    LineTo(x,y+TeeCheckBoxSize+1);

    if ABackColor=clWhite then
       Pen.Color:={$IFDEF FMX}claSilver{$ELSE}clSilver{$ENDIF}
    else
       Pen.Color:=clWhite;

    DoHorizLine(x,x+TeeCheckBoxSize+1,y+TeeCheckBoxSize+1);

    LineTo(x+TeeCheckBoxSize+1,y-1);
    Pen.Color:={$IFDEF FMX}claBlack{$ELSE}clBlack{$ENDIF};
    DoHorizLine(x+TeeCheckBoxSize-1,x+1,y+1);
    LineTo(x+1,y+TeeCheckBoxSize);

    Pen.Style:=psClear;
    Rectangle(x+2,y+2,x+TeeCheckBoxSize+1,y+TeeCheckBoxSize+1);
  end
  else
    Ellipse(x,y,x+TeeCheckBoxSize+1,y+TeeCheckBoxSize+1);

  if Checked then
  begin
    Pen.Style:=psSolid;
    Pen.Color:={$IFDEF FMX}claBlack{$ELSE}clBlack{$ENDIF};

    if CheckBox then
    begin
      for t:=1 to 3 do DoVertLine(x+2+t,y+4+t,y+7+t);
      for t:=1 to 4 do DoVertLine(x+5+t,y+7-t,y+10-t);
    end
    else
    begin
      Brush.Color:={$IFDEF FMX}claBlack{$ELSE}clBlack{$ENDIF};
      Ellipse(x+3,y+3,x+9,y+9);
    end;
  end;
end;

procedure TTeeCanvas.DrawCheckBox(x,y:Integer; Checked:Boolean; const ABackColor:TColor; CheckBox:Boolean=True);
begin
  {$IFDEF FMX}
  InternalDrawCheck(x,y,Checked,ABackColor,CheckBox);
  {$ELSE}
  TeeDrawCheckBox(x,y,ReferenceCanvas,Checked,ABackColor,CheckBox);
  {$ENDIF}
end;

Function RGBValue(const Color:TColor):TRGB; {$IFDEF D9}inline;{$ENDIF}
begin
  with result do
  begin
    Red:=Byte(Color);
    Green:=Byte(Color shr 8);
    Blue:=Byte(Color shr 16);
  end;
end;

{$IFNDEF FMX}
{$IFDEF TEEWINDOWS}
{$IFDEF MSWINDOWS}
function TeeCreatePenSmallDots(const AColor:TColor; const Space:Integer=0):HPen;
var LBrush   : {$IFDEF LCL}Windows.{$ENDIF}TLogBrush;
    tmpStyle : Array[0..1] of Longint;
begin
  LBrush.lbStyle:=bs_Solid;
  LBrush.lbColor:=AColor;
  LBrush.lbHatch:=0;

  if Space>0 then
  begin
    tmpStyle[0]:=0;
    tmpStyle[1]:=Space+1;
    result:=ExtCreatePen(PS_GEOMETRIC or PS_USERSTYLE,1,LBrush,2,@tmpStyle);
  end
  else
    result:=ExtCreatePen(PS_COSMETIC or PS_ALTERNATE,1,LBrush,0,nil);
end;
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}
Procedure TeeSetTeePen(const FPen:TPen; const APen:TTeePen; AColor:TColor);
const
  PenStyles: array[TPenStyle] of Word =

     {$IFDEF LCL}
     (PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT,
      PS_INSIDEFRAME, 0 { PS_PATTERN? }, PS_NULL

     {$ELSE}

     (PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL,
      PS_INSIDEFRAME
      {$IFDEF D10}
      ,PS_USERSTYLE, PS_ALTERNATE
      {$ENDIF}
     {$ENDIF}
    );

{$IFDEF MSWINDOWS}
var LBrush   : {$IFDEF LCL}Windows.{$ENDIF}TLogBrush;
{$ENDIF}
var tmpEnd : Integer;
begin
  if APen.SmallDots and (APen.Style<>psClear) then // http://bugs.teechart.net/show_bug.cgi?id=808
  begin
    // Delphi 2005 VCL bug.
    // TPen.GetPenData uses TLogPen instead of TExtLogPen
    // Workaround for Delphi 2005: Set "psDot" style
    // Another Delphi 2005 VCL Bug: Setting Pen.Handle leaks GDI Object.

    // These bugs appear fixed in BDS 2006.

    {$IFDEF D9WITHOUTUPDATE2}
    FPen.Color:=AColor;
    //FPen.Style:=psDot;
    FPen.Mode:=APen.Mode;

    if TeeCheckPenWidth then FPen.Width:=0
                        else FPen.Width:=1;

    {$ELSE}
    {$IFDEF MSWINDOWS}
    FPen.Handle:=TeeCreatePenSmallDots(AColor,APen.SmallSpace);
    FPen.Mode:=APen.Mode;
    {$ELSE}

    FPen.Color:=AColor;
    FPen.Mode:=APen.Mode;

    if TeeCheckPenWidth then FPen.Width:=0
                        else FPen.Width:=1;
    {$ENDIF}
    {$ENDIF}
  end
  else
  if APen.Width>1 then
  begin
    FPen.Assign(APen);
    FPen.Color:=AColor;

    {$IFDEF D9WITHOUTUPDATE2} // Workaround for VCL bug at TPen.SetData
    if APen.EndStyle<>esRound then
    begin
    {$ENDIF}

    {$IFDEF MSWINDOWS}
      LBrush.lbStyle:=BS_SOLID;
      LBrush.lbColor:=Cardinal(AColor);
      LBrush.lbHatch:=0;

      Case APen.EndStyle of
        esRound : tmpEnd:=PS_ENDCAP_ROUND;
        esSquare: tmpEnd:=PS_ENDCAP_SQUARE;
      else tmpEnd:=PS_ENDCAP_FLAT;
      end;

      case APen.JoinStyle of
        jsRound : tmpEnd:=tmpEnd or PS_JOIN_ROUND;
        jsBevel: tmpEnd:=tmpEnd or PS_JOIN_BEVEL;
      else tmpEnd:=tmpEnd or PS_JOIN_MITER;
      end;

      FPen.Handle:=ExtCreatePen( PS_GEOMETRIC or
                                 PenStyles[APen.Style] or
                                 tmpEnd,
                                 APen.Width,
                                 LBrush,
                                 0,
                                 nil );
      {$ENDIF}

      //  The following helps SVG (VML, etc) exporting when Pen.Width > 1,
      //  but breaks drawing dotted pens.
      //    SelectObject(Handle, ExtCreatePen( PS_GEOMETRIC or
      //         PenStyles[APen.Style] or tmpEnd,APen.Width,LBrush,0,nil));

      // FPen.Mode:=APen.Mode;

    {$IFDEF D9WITHOUTUPDATE2}
    end;
    {$ENDIF}
  end
  else
  begin
    FPen.Assign(APen);

    // speed optimizations ?
    //if APen.Style<>FPen.Style then FPen.Style:=APen.Style;
    //if APen.Width<>FPen.Width then FPen.Width:=APen.Width;
    //if APen.Mode<>FPen.Mode then FPen.Mode:=APen.Mode;

    if FPen.Color<>AColor then FPen.Color:=AColor;

    if TeeCheckPenWidth and (FPen.Style<>psSolid) and (FPen.Width=1) then
       FPen.Width:=0;
  end;
end;
{$ENDIF}

procedure TTeeCanvas.SetPenMode(const APen:TTeePen);
{$IFDEF MSWINDOWS}
{$IFDEF FMX}
begin
  // ?
end;
{$ELSE}
const
  PenModes: array[TPenMode] of Word =
    (R2_BLACK, R2_WHITE, R2_NOP, R2_NOT, R2_COPYPEN, R2_NOTCOPYPEN, R2_MERGEPENNOT,
     R2_MASKPENNOT, R2_MERGENOTPEN, R2_MASKNOTPEN, R2_MERGEPEN, R2_NOTMERGEPEN,
     R2_MASKPEN, R2_NOTMASKPEN, R2_XORPEN, R2_NOTXORPEN);
begin
  if (APen.Width>1) and (APen.EndStyle<>esRound) then
     SetROP2(Handle, PenModes[APen.Mode]);
end;
{$ENDIF}
{$ELSE}
begin
 // FPC Linux ?
end;
{$ENDIF}

procedure TTeeCanvas.AssignVisiblePenColor(const APen:TTeePen; AColor:TColor);
begin
  FPen.OnChange:=nil;

  if APen.Visible then
  begin
    if MonoChrome then AColor:=clBlack;

    FPen.Assign(APen);
    FPen.Color:=AColor;
  end
  else
  if FPen.Style<>psClear then
     FPen.Style:=psClear;

  FPen.OnChange:=ChangedPen;
  DoChangedPen;
end;

procedure TTeeCanvas.DoChangedPen;
{$IFDEF FMX}
var tmpStroke : {$IFDEF D17}TStrokeBrush{$ELSE}TBrush{$ENDIF};
{$ENDIF}
begin
  if FPen.Visible then
  begin
    {$IFNDEF FMX}
    if IsWindowsNT and (not SupportsFullRotation) then
    begin
      TeeSetTeePen(FCanvas.Pen,FPen,FPen.Color); { only valid in Windows-NT }
      SetPenMode(FPen);
    end
    else
    {$ENDIF}
    begin
      if TeeCheckPenWidth and (FPen.Style<>psSolid) and (FPen.Width=1) then
         FPen.Width:=0;

      {$IFDEF FMX}
      if Assigned(FCanvas) then
      begin
        tmpStroke:=FCanvas.Stroke;

        if FPen.Fill.HasGradient then
        begin
          PrepareGradient(FPen.Fill.Gradient,tmpStroke.Gradient);
          tmpStroke.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};
        end
        else
          tmpStroke.Assign(FPen.Fill);

        {$IFDEF D17}
        if FPen.Width=0 then
           tmpStroke.Thickness:=0.1
        else
           tmpStroke.Thickness:=FPen.Width;

        case FPen.FJoinStyle of
          jsRound : tmpStroke.Join:=TStrokeJoin.{$IFDEF D20}Round{$ELSE}sjRound{$ENDIF};
          jsBevel : tmpStroke.Join:=TStrokeJoin.{$IFDEF D20}Bevel{$ELSE}sjBevel{$ENDIF};
        else
          tmpStroke.Join:=TStrokeJoin.{$IFDEF D20}Miter{$ELSE}sjMiter{$ENDIF};
        end;

        if FPen.FEndStyle=esFlat then
           tmpStroke.Cap:=TStrokeCap.{$IFDEF D20}Flat{$ELSE}scFlat{$ENDIF}
        else
           tmpStroke.Cap:=TStrokeCap.{$IFDEF D20}Round{$ELSE}scRound{$ENDIF};

        {$ELSE}

        if FPen.Width=0 then
           FCanvas.StrokeThickness:=0.1
        else
           FCanvas.StrokeThickness:=FPen.Width;

        case FPen.FJoinStyle of
          jsRound : FCanvas.StrokeJoin:=TStrokeJoin.sjRound;
          jsBevel : FCanvas.StrokeJoin:=TStrokeJoin.sjBevel;
        else
          FCanvas.StrokeJoin:=TStrokeJoin.sjMiter;
        end;

        if FPen.FEndStyle=esFlat then
           FCanvas.StrokeCap:=TStrokeCap.scFlat
        else
           FCanvas.StrokeCap:=TStrokeCap.scRound;
        {$ENDIF}

        if FPen.SmallDots then
        begin
          {$IFDEF D17}
          tmpStroke.Dash:=TStrokeDash.{$IFDEF D20}Custom{$ELSE}sdCustom{$ENDIF};
          tmpStroke.SetCustomDash([1,1+FPen.SmallSpace],0);
          {$ELSE}
          FCanvas.StrokeDash:=TStrokeDash.sdCustom;
          FCanvas.SetCustomDash([0,1+FPen.SmallSpace],0);
          {$ENDIF}
        end
        else
        case FPen.Style of
            psSolid: {$IFDEF D17}tmpStroke.Dash{$ELSE}FCanvas.StrokeDash{$ENDIF}:=TStrokeDash.{$IFDEF D20}Solid{$ELSE}sdSolid{$ENDIF};
              psDot: {$IFDEF D17}tmpStroke.Dash{$ELSE}FCanvas.StrokeDash{$ENDIF}:=TStrokeDash.{$IFDEF D20}Dot{$ELSE}sdDot{$ENDIF};
             psDash: {$IFDEF D17}tmpStroke.Dash{$ELSE}FCanvas.StrokeDash{$ENDIF}:=TStrokeDash.{$IFDEF D20}Dash{$ELSE}sdDash{$ENDIF};
          psDashDot: {$IFDEF D17}tmpStroke.Dash{$ELSE}FCanvas.StrokeDash{$ENDIF}:=TStrokeDash.{$IFDEF D20}DashDot{$ELSE}sdDashDot{$ENDIF};
       psDashDotDot: {$IFDEF D17}tmpStroke.Dash{$ELSE}FCanvas.StrokeDash{$ENDIF}:=TStrokeDash.{$IFDEF D20}DashDotDot{$ELSE}sdDashDotDot{$ENDIF};
        end;
      end;
      {$ENDIF}

    end;
  end
  else
  {$IFDEF FMX}
  if FCanvas.Stroke.Kind<>bsClear then
     FCanvas.Stroke.Kind:=bsClear;
  {$ELSE}
  if FCanvas.Pen.Style<>psClear then
     FCanvas.Pen.Style:=psClear;
  {$ENDIF}
end;

procedure TTeeCanvas.DoChangedFont;
{$IFNDEF FMX}
{$IFNDEF LCL}
var Old : Integer;
{$ENDIF}
{$ENDIF}
begin
  if Assigned(FFont) then
  begin
    SetInterCharSize(FFont.InterCharSize);  // TV52015179

    if Assigned(FCanvas) then
    begin
      // For Metafile and printing, change PixelsPerInch before Assigning font:
      {$IFNDEF FMX}
      {$IFNDEF LCL}
      Old:=FCanvas.Font.PixelsPerInch;
      FCanvas.Font.PixelsPerInch:=FFont.PixelsPerInch;
      {$ENDIF}
      {$ENDIF}

      FCanvas.Font.Assign(FFont);

      // Reset back:
      {$IFNDEF FMX}
      {$IFNDEF LCL}
      FCanvas.Font.PixelsPerInch:=Old;
      {$ENDIF}
      {$ENDIF}
    end;
  end;
end;

Procedure TTeeCanvas.AssignFontSize(const AFont:TTeeFont; const ASize:Single);
var tmp : Single;
begin
  if IZoomText and (FontZoom<>100) then
     tmp:={$IFDEF FMX}Max(1,ASize*FontZoom*0.01){$ELSE}ASize*FontZoom*0.01{$ENDIF}
  else
     tmp:=ASize;

  IChangingFont:=True;

  with FFont do
  begin
    Assign(AFont);
    SizeFloat:=tmp;
  end;

  if MonoChrome then
     {$IFDEF FMX}FBrush{$ELSE}FFont{$ENDIF}.Color:=clBlack;

  IChangingFont:=False;

  DoChangedFont;
end;

Procedure TTeeCanvas.AssignFont(const AFont:TTeeFont);
Begin
  AssignFontSize(AFont,AFont.SizeFloat);
end;

procedure TTeeCanvas.SetInterCharSize(ASize:Integer);
{$IFDEF FMX}
begin
end;
{$ELSE}
{$IFDEF TEEWINDOWS}
var tmp : TTeeCanvasHandle;
{$ENDIF}
begin
  {$IFDEF TEEWINDOWS}
  {$IFDEF MSWINDOWS}
  tmp:=Handle;

  if GetTextCharacterExtra(tmp)<>ASize then
     SetTextCharacterExtra(tmp,ASize);
  {$ENDIF}
  {$ENDIF}
end;
{$ENDIF}

Function TTeeCanvas.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;

{$IFDEF AXUNICODE}
var tmpStr: WideString;
{$ENDIF}
{$IFDEF FMX}
var R : TRect;
{$ELSE}
var tmp : tagSize;
{$ENDIF}
begin
  {$IFDEF AXUNICODE}
  tmpStr:=AXUnicode(St);
  tmp:=TextExtent(tmpStr);  //don't go to base method
  result.X:=tmp.cx;
  result.Y:=tmp.cy;
  {$ELSE}

  {$IFDEF FMX}

  {$IFDEF TEECACHEDLAYOUT}
  R:=ICachedLayout(0,0,St).TextRect;
  {$ELSE}
  R:=RectF(0, 0, 10000, 10000);
  FCanvas.MeasureText(R, St, False, [], TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
                      TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF});
  {$ENDIF}

  result.X:=R.Right;
  result.Y:=R.Bottom;

  {$ELSE}

  tmp:=FCanvas.TextExtent(St);
  result.X:=tmp.cx;
  result.Y:=tmp.cy;
  {$ENDIF}
  {$ENDIF}

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;

Function TTeeCanvas.TextWidth(Const St:String):Integer;

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

{$IFDEF AXUNICODE}
var tmpStr: WideString;
{$ENDIF}
{$IFDEF FMX}
  {$IFNDEF TEECACHEDLAYOUT}
  var R : TRect;
  {$ENDIF}
{$ENDIF}
begin
  {$IFDEF AXUNICODE}
  tmpStr:=AXUnicode(St);
  result:=TextExtent(tmpStr).cx;  //don't go to base method
  {$ELSE}

  {$IFDEF FMX}

  {$IFDEF TEECACHEDLAYOUT}
  result:=Round(ICachedLayout(0,0,St).TextRect.Right);
  {$ELSE}
  R:=RectF(0, 0, 10000, 10000);
  FCanvas.MeasureText(R, St, False, [], TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
                      TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF});

  result:=Round(R.Right);
  {$ENDIF}
  {$ELSE}

  result:=FCanvas.TextExtent(St).cx;
  {$ENDIF}
  {$ENDIF}

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;

Function TTeeCanvas.TextHeight(Const St:String):Integer;

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

{$IFDEF FMX}
  {$IFNDEF TEECACHEDLAYOUT}
  var R : TRect;
  {$ENDIF}
{$ENDIF}
Begin
  {$IFDEF FMX}

  {$IFDEF TEECACHEDLAYOUT}
  result:=Round(ICachedLayout(0,0,St).TextRect.Bottom);
  {$ELSE}
  R := RectF(0, 0, 10000, 10000);
  FCanvas.MeasureText(R, St, False, [], TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
                      TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF});
  result := Round(R.Bottom);
  {$ENDIF}

  {$ELSE}
  result:=FCanvas.TextExtent(St).cy;
  {$ENDIF}

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;

{$IFDEF TEEUNICODE}
function TTeeCanvas.TextExtent(const Text:WideString):Types.TSize;
begin
  {$IFDEF MSWINDOWS}
  result.cx:=0;
  result.cy:=0;
  Windows.GetTextExtentPoint32W(Handle, PWideChar(Text), Length(Text), result);
  {$ELSE}
  result:=FCanvas.TextExtent(Text);
  {$ENDIF}
end;

Function TTeeCanvas.TextSize(const St:WideString):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 tmp : tagSize;
begin
  tmp:=TextExtent(St);

  result.X:=tmp.cx;
  result.Y:=tmp.cy;

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;

Function TTeeCanvas.TextWidth(Const St:WideString):Integer;

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

begin
  result:=TextExtent(St).cx;

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;

Function TTeeCanvas.TextHeight(Const St:WideString):Integer;

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

begin
  result:=TextExtent(St).cy;

  ApplyShadow(FFont.FShadow);
  ApplyShadow(FFont.FEmboss);
end;
{$ENDIF}

{$IFNDEF FMX}
function TTeeCanvas.Tessellate({$IFDEF D6}const{$ENDIF} X,Y: Array of TChartValue):TPointFloatArray;
begin
  // See TGLCanvas
  result:=nil;
end;
{$ENDIF}

procedure TTeeCanvas.Ellipse(const R:TRect);
begin
  with R{$IFDEF FMX}.Round{$ENDIF} do
       Ellipse(Left,Top,Right,Bottom);
end;

procedure TTeeCanvas.Frame3D( var Rect: TRect; TopColor,BottomColor: TColor;
                              Width: Integer);
var TopRight   : TPoint;
    BottomLeft : TPoint;
    P          : Array of TPoint;
begin
  FPen.OnChange:=nil;
  FPen.Width:=1;
  FPen.Style:=psSolid;
  FPen.Fill.Gradient.Visible:=False;
  FPen.OnChange:=ChangedPen;
  DoChangedPen;

  Rect.Bottom:=Rect.Bottom-1;
  Rect.Right:=Rect.Right-1;

  SetLength(P,3);

  while Width > 0 do
  begin
    Dec(Width);

    with Rect do
    begin
      TopRight.X := Right;
      TopRight.Y := Top;
      BottomLeft.X := Left;
      BottomLeft.Y := Bottom;
      FPen.Color := TopColor;

      P[0]:=BottomLeft;
      P[1]:=TopLeft;
      P[2]:=TopRight;
      Polyline(P);

      FPen.Color := BottomColor;
      BottomLeft.X:=BottomLeft.X-1;

      P[0]:=TopRight;
      P[1]:=BottomRight;
      P[2]:=BottomLeft;
      Polyline(P);
    end;

    {$IFDEF FMX}InflateRectF{$ELSE}InflateRect{$ENDIF}(Rect, -1, -1);
  end;

  P:=nil;

  Rect.Bottom:=Rect.Bottom+1;
  Rect.Right:=Rect.Right+1;
end;

procedure TTeeCanvas.LineTo(const P:TPoint);
begin
  LineTo(P.X,P.Y);
end;

procedure TTeeCanvas.MoveTo(const P:TPoint);
begin
  MoveTo(P.X,P.Y);
end;

{$IFNDEF FMX}
procedure TTeeCanvas.LineTo(const P:TPointFloat);
begin
  LineTo(Round(P.X),Round(P.Y));
end;

procedure TTeeCanvas.MoveTo(const P:TPointFloat);
begin
  MoveTo(Round(P.X),Round(P.Y));
end;
{$ENDIF}

{$IFDEF FMX}
function TTeeCanvas.Opacity: Single;
begin
  result:=1-(Transparency*0.01);
end;
{$ENDIF}

procedure TTeeCanvas.RoundRect(const R: TRect; X,Y:Integer; const ACorners:TTeeCorners=nil);
var tmp : TTeeBasePath;
begin
  if not Assigned(ACorners) then
  begin
    with R{$IFDEF FMX}.Round{$ENDIF} do
         RoundRect(Left,Top,Right,Bottom,X,Y);
  end
  else
  begin
    tmp:=ACorners.GetPath(Self,R,X,Y);
    try
      tmp.Draw(Self);
    finally
      tmp.Free;
    end;
  end;
end;

Procedure TTeeCanvas.TextOut(const X,Y:TCoordinate; const Text:String; AllowHtml:Boolean);
begin
  if AllowHtml then
     HtmlTextOut(Self,{$IFDEF FMX}Round{$ENDIF}(X),{$IFDEF FMX}Round{$ENDIF}(Y),Text)
  else
     TextOut(X,Y,Text);
end;

{$IFDEF AXUNICODE}

function TTeeCanvas.AXUnicode(const S:String):WideString;
var tmp : TStringList;
begin
  if Pos('@U#',S)= 1 then
  begin
    tmp:=Unicoding.ConvertUTF82CPList(S);
    result:=Unicoding.ConvertCPList2Char(tmp);
    tmp.Free;
  end
  else
    result:=Unicoding.StringToWideString(S,0);
end;

Procedure TTeeCanvas.TextOut(const X,Y:Integer; const Text:WideString);
var tmpWideString:WideString;
begin
  tmpWideString:=AXUnicode(Text);
  Windows.TextOutW(Handle,X, Y, PWideChar(tmpWideString),LStrLenW(PWideChar(tmpWideString)));
end;
{$ENDIF}

Procedure TTeeCanvas.Line(const X0,Y0,X1,Y1 : TCoordinate);
begin
  MoveTo(X0,Y0);
  LineTo(X1,Y1);
end;

procedure TTeeCanvas.Line(const FromPoint, ToPoint: TPoint);
begin
  Line(FromPoint.X,FromPoint.Y,ToPoint.X,ToPoint.Y)
end;

procedure TTeeCanvas.PolygonConcave({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
begin
  Polygon(Points);
end;

procedure TTeeCanvas.PushTransp;
var L : Integer;
begin
  L:=Length(ITranspStack);
  SetLength(ITranspStack,L+1);
  ITranspStack[L]:=Self.Transparency;
end;

procedure TTeeCanvas.PopTransp;
var L : Integer;
begin
  L:=Length(ITranspStack);

  Assert(L>0,'Canvas Transparency Stack might be corrupt');

  Transparency:=ITranspStack[L-1];
  SetLength(ITranspStack,L-1);
end;

Function TTeeCanvas.BeginBlending(const R: TRect;
  Transparency: TTeeTransparency):TTeeBlend;
begin
  PushTransp;
  Self.Transparency:=100-Round((100-Self.Transparency)*(100-Transparency)*0.01);

  {$IFDEF FMX}
  result:=nil;
  {$ELSE}
  result:=TTeeBlend.Create(Self,R);
  {$ENDIF}
end;

procedure TTeeCanvas.EndBlending(const Blend:TTeeBlend);
begin
  {$IFNDEF FMX}
  if Assigned(Blend) then
  begin
    Blend.DoBlend(Transparency);
    Blend.Free;
  end;
  {$ENDIF}

  PopTransp;
end;

const
  TwoPi=2.0*Pi;

procedure TTeeCanvas.CalcArcAngles(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer;
                                   out StartAngle,EndAngle:Single);
var
  XC,
  YC : Single;
  tmpX,
  tmpY : Single;
begin
  XC:= (X2+X1)*0.5;
  YC:= (Y2+Y1)*0.5;

  tmpY:=YC-Y3;
  tmpX:=X3-XC;
  StartAngle:=ArcTan2(tmpY,tmpX);

  if StartAngle<0 then
     StartAngle:=StartAngle+TwoPi;

  StartAngle:=StartAngle*HalfDivPi;

  tmpY:=YC-Y4;
  tmpX:=X4-XC;
  EndAngle:=ArcTan2(tmpY,tmpX);

  if EndAngle<0 then
     EndAngle:=EndAngle+TwoPi;

  EndAngle:=EndAngle*HalfDivPi;

  //  if EndAngle=0 then EndAngle:=361;
end;

class function TTeeCanvas.ColorAlpha(const AColor:TColor):Byte;
begin
  result:=TRGBA(AColor).Alpha;
end;

class function TTeeCanvas.ColorFrom(const AColor:TColor; const Alpha:Byte):TColor;
begin
  result:=AColor;
  TRGBA(result).Alpha:=Alpha;
end;

procedure TTeeCanvas.Arc(const Left, Top, Right, Bottom:Integer; const StartAngle,EndAngle:Single);
var
  XRadius, YRadius,
  XCenter, YCenter : Integer;

  Procedure CalcPoint(const Angle:Single; out x,y:Integer);
  var tmpSin : {$IFDEF D15}Single{$ELSE}Extended{$ENDIF};
      tmpCos : {$IFDEF D15}Single{$ELSE}Extended{$ENDIF};
  begin
    SinCos(Angle,tmpSin,tmpCos);
    x:=XCenter+Round(XRadius*tmpCos);
    y:=YCenter-Round(YRadius*tmpSin);
  end;

var X3,Y3 : Integer;
    X4,Y4 : Integer;
begin
  XRadius:=(Right-Left) div 2;
  YRadius:=(Bottom-Top) div 2;
  XCenter:=Left+XRadius;
  YCenter:=Top+YRadius;

  // GDI Clockwise
  if StartAngle<EndAngle then
  begin
    CalcPoint(StartAngle*TeePiStep,X3,Y3);
    CalcPoint(EndAngle*TeePiStep,X4,Y4);
  end
  else
  begin
    CalcPoint(StartAngle*TeePiStep,X4,Y4);
    CalcPoint(EndAngle*TeePiStep,X3,Y3);
  end;

  Arc(Left,Top,Right,Bottom,X3,Y3,X4,Y4);
end;

procedure TTeeCanvas.Arc(const R:TRectI; const StartAngle,EndAngle:Single);
begin
  with R do
       Arc(Left,Top,Right,Bottom,StartAngle,EndAngle);
end;

class function TTeeCanvas.IsConvexPolygon(const P:TPointArray):Boolean;
var n,i,j,k,
    flag : Integer;
    z : Single;
begin
  flag:=0;
  n:=Length(P);

  if n < 3 then
     result:=False
  else
  begin
    for i:=0 to n-1 do
    begin
      j := (i + 1) mod n;
      k := (i + 2) mod n;
      z := (p[j].x - p[i].x) * (p[k].y - p[j].y);
      z := z- (p[j].y - p[i].y) * (p[k].x - p[j].x);

      if z < 0 then
         flag:=flag or 1
      else
      if z > 0 then
         flag:=flag or 2;

      if flag=3 then
      begin
        result:=False;
        Exit;
      end;
    end;

    result:=flag <> 0;
  end;
end;

{$IFNDEF FMX}
class function TTeeCanvas.IsConvexPolygon(const P:TPointFloatArray):Boolean;
var n,i,j,k,
    flag : Integer;
    z : Single;
begin
  flag:=0;
  n:=Length(P);

  if n < 3 then
     result:=False
  else
  begin
    for i:=0 to n-1 do
    begin
      j := (i + 1) mod n;
      k := (i + 2) mod n;
      z := (p[j].x - p[i].x) * (p[k].y - p[j].y);
      z := z- (p[j].y - p[i].y) * (p[k].x - p[j].x);

      if z < 0 then
         flag:=flag or 1
      else
      if z > 0 then
         flag:=flag or 2;

      if flag=3 then
      begin
        result:=False;
        Exit;
      end;
    end;

    result:=flag <> 0;
  end;
end;
{$ENDIF}

// These are the Arrows points coordinates:
// pc,pd,pe,pf,pg,ph : TPoint;

    (*           pc
                   |\
    ph           pf| \
      |------------   \ ToPoint
 From |------------   /
    pg           pe| /
                   |/
                 pd
    *)
function TTeeCanvas.GetArrowPoints(const ArrowPercent:Single;
                           const FromPoint,ToPoint:TPoint;
                           ArrowWidth,ArrowHeight:Integer):TPointArray;
var x    : Double;
    y    : Double;
    SinA : Double;
    CosA : Double;

  function CalcArrowPoint:TPoint;
  begin
    result.X:=Round( x*CosA + y*SinA);
    result.Y:=Round(-x*SinA + y*CosA);
  end;

var tmpHoriz  : Integer;
    tmpVert   : Integer;
    dx        : TCoordinate;
    dy        : TCoordinate;
    tmpHoriz4 : Double;
    xb        : Double;
    yb        : Double;
    l         : Double;
begin
  dx:=ToPoint.x-FromPoint.x;
  dy:=FromPoint.y-ToPoint.y;
  l:=TeeDistance(dx,dy);

  tmpHoriz:=ArrowWidth;
  tmpVert :=Math.Min(Round(l),ArrowHeight);

  if l>0 then
  begin
    SetLength(result,7);

    SinA:= dy / l;
    CosA:= dx / l;

    xb:= ToPoint.x*CosA - ToPoint.y*SinA;
    yb:= ToPoint.x*SinA + ToPoint.y*CosA;

    x := xb - tmpVert;
    y := yb - tmpHoriz*0.5;
    result[3]:=CalcArrowPoint;

    y := yb + tmpHoriz*0.5;
    result[5]:=CalcArrowPoint;

    tmpHoriz4:=tmpHoriz*(ArrowPercent*0.005);
    y := yb - tmpHoriz4;
    result[2]:=CalcArrowPoint;

    y := yb + tmpHoriz4;
    result[6]:=CalcArrowPoint;

    x := FromPoint.x*cosa - FromPoint.y*sina;
    y := yb - tmpHoriz4;
    result[1]:=CalcArrowPoint;

    y := yb + tmpHoriz4;
    result[0]:=CalcArrowPoint;

    result[4]:=ToPoint;

    // [ph0,pg1,pe2,pc3,ToPoint4,pd5,pf6];
  end
  else
    result:=nil;
end;

Procedure TTeeCanvas.Arrow(Filled:Boolean; const ArrowPercent:Double;
                           const FromPoint,ToPoint:TPoint;
                           ArrowWidth,ArrowHeight:Integer);
var tmpP : TPointArray;
begin
  tmpP:=GetArrowPoints(ArrowPercent,FromPoint,ToPoint,ArrowWidth,ArrowHeight);

  if Assigned(tmpP) then  // if at least one pixel...
  Begin
    if Filled then
       Polygon(tmpP)
    else
    begin
      Line(FromPoint,ToPoint);
      LineTo(tmpP[5]);
      Line(ToPoint,tmpP[3]);
    end;

    tmpP:=nil;
  end;
end;

{ TPointBlock }

procedure TPointBlock.DoChanged;
begin
  if Assigned(FOnChanged) then
     FOnChanged(Self);

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

{ TPointXY }

Constructor TPointXYFloat.Create(const AOwner:TVisualBlock=nil; const AValue:Single=0;
                                 const ChangedEvent:TNotifyEvent=nil);
begin
  inherited Create;

  FOnChanged:=ChangedEvent;

  IOwner:=AOwner;
  IDefault:=AValue;

  Point.x:=AValue;
  Point.y:=AValue;
end;

procedure TPointXYFloat.Assign(Source: TPersistent);
begin
  if Source is TPointXYFloat then
  begin
    Point:=TPointXYFloat(Source).Point;
    DoChanged;
  end
  else
    inherited;
end;

function TPointXYFloat.GetX: Single;
begin
  result:=Point.x;
end;

function TPointXYFloat.GetY: Single;
begin
  result:=Point.y;
end;

function TPointXYFloat.IsXStored:Boolean;
begin
  result:=Point.X<>IDefault;
end;

function TPointXYFloat.IsYStored:Boolean;
begin
  result:=Point.Y<>IDefault;
end;

procedure TPointXYFloat.SetX(const Value: Single);
begin
  if Point.x<>Value then
  begin
    Point.x:=Value;
    DoChanged;
  end;
end;

procedure TPointXYFloat.SetY(const Value: Single);
begin
  if Point.y<>Value then
  begin
    Point.y:=Value;
    DoChanged;
  end;
end;

procedure TPointXYFloat.InitDefault(const Value:Double);
begin
  IDefault:=Value;
  Point.X:=Value;
  Point.Y:=Value;
end;

{ TPointXYZFloat }

Constructor TPointXYZFloat.Create(const AOwner:TVisualBlock=nil; const AValue:Double=0;
                                  const ChangedEvent:TNotifyEvent=nil);
begin
  inherited Create;

  FOnChanged:=ChangedEvent;

  IOwner:=AOwner;
  IDefault:=AValue;

  Point.X:=AValue;
  Point.Y:=AValue;
  Point.Z:=AValue;
end;

procedure TPointXYZFloat.Assign(Source: TPersistent);
begin
  if Source is TPointXYZFloat then
  begin
    Point:=TPointXYZFloat(Source).Point;
    DoChanged;
  end
  else
    inherited;
end;

function TPointXYZFloat.GetX:Single;
begin
  result:=Point.X;
end;

function TPointXYZFloat.GetY:Single;
begin
  result:=Point.Y;
end;

function TPointXYZFloat.GetZ:Single;
begin
  result:=Point.Z;
end;

procedure TPointXYFloat.ReadXZero(Reader: TReader);
begin
  if Reader.ReadBoolean then
     Point.X:=0;
end;

procedure TPointXYFloat.WriteXZero(Writer: TWriter);
begin
  Writer.WriteBoolean(Point.X=0);
end;

procedure TPointXYFloat.ReadYZero(Reader: TReader);
begin
  if Reader.ReadBoolean then
     Point.Y:=0;
end;

procedure TPointXYFloat.WriteYZero(Writer: TWriter);
begin
  Writer.WriteBoolean(Point.Y=0);
end;

procedure TPointXYFloat.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('XZero',ReadXZero,WriteXZero,(IDefault<>0) and (Point.X=0));
  Filer.DefineProperty('YZero',ReadYZero,WriteYZero,(IDefault<>0) and (Point.Y=0));
end;

function TPointXYZFloat.IsXStored:Boolean;
begin
  result:=Point.X<>IDefault;
end;

function TPointXYZFloat.IsYStored:Boolean;
begin
  result:=Point.Y<>IDefault;
end;

function TPointXYZFloat.IsZStored:Boolean;
begin
  result:=Point.Z<>IDefault;
end;

function TPointXYZFloat.AsRound:TPoint3D;
begin
  result.X:=Round(Point.X);
  result.Y:=Round(Point.Y);
  result.Z:=Round(Point.Z);
end;

procedure TPointXYZFloat.SetX(const Value: Single);
begin
  if Point.X<>Value then
  begin
    Point.X:=Value;
    DoChanged;
  end;
end;

procedure TPointXYZFloat.SetY(const Value: Single);
begin
  if Point.Y<>Value then
  begin
    Point.Y:=Value;
    DoChanged;
  end;
end;

procedure TPointXYZFloat.SetZ(const Value: Single);
begin
  if Point.Z<>Value then
  begin
    Point.Z:=Value;
    DoChanged;
  end;
end;

procedure TPointXYZFloat.SetPoint(const Value:TPoint3DFloat);
begin
  Point:=Value;
  DoChanged;
end;

procedure TPointXYZFloat.SetPoint(const X,Y,Z:Single);
begin
  Point.X:=X;
  Point.Y:=Y;
  Point.Z:=Z;

  DoChanged;
end;

function TPointXYZFloat.GetValue: Single;
begin
  result:=Point.X;
end;

procedure TPointXYZFloat.SetValue(const Value: Single);
begin
  with Point do
  begin
    X:=Value;
    Y:=Value;
    Z:=Value;
  end;

  DoChanged;
end;

procedure TPointXYZFloat.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('ZZero',ReadZZero,WriteZZero,(IDefault<>0) and (Point.Z=0));
end;

procedure TPointXYZFloat.InitDefault(const Value:Single);
begin
  IDefault:=Value;

  with Point do
  begin
    X:=Value;
    Y:=Value;
    Z:=Value;
  end;
end;

procedure TPointXYZFloat.ReadZZero(Reader: TReader);
begin
  if Reader.ReadBoolean then
     Point.Z:=0;
end;

procedure TPointXYZFloat.WriteZZero(Writer: TWriter);
begin
  Writer.WriteBoolean(Point.Z=0);
end;

{ TVisualBlock }

Constructor TVisualBlock.Create(AOwner: TComponent);
begin
  inherited;
  FLocation:=TPointXYZFloat.Create(Self,0);
  FSize:=TPointXYZFloat.Create(Self,100,SizeChanged);

  IsCloneable:=True;  // Experimental
end;

Destructor TVisualBlock.Destroy;
begin
  FSize.Free;
  FLocation.Free;
  inherited;
end;

procedure TVisualBlock.Assign(Source: TPersistent);
begin
  if Source is TVisualBlock then
  with TVisualBlock(Source) do
  begin
    Self.FLocation.Assign(Location);
    Self.FSize.Assign(FSize);

    Self.IsCloneable:=IsCloneable;
  end
  else
    inherited;
end;

procedure TVisualBlock.SetLocation(const Value:TPointXYZFloat);
begin
  FLocation.Assign(Value);
  Repaint;
end;

procedure TVisualBlock.SetSize(const Value: TPointXYZFloat);
begin
  FSize.Assign(Value);
  Repaint;
end;

procedure TVisualBlock.AddPathPoint(const X,Y:Single; const AColor:TColor=clDefault);
begin
  // Dummy
end;

procedure TVisualBlock.ClearPath;
begin
  // Dummy
end;

procedure TVisualBlock.SetBounds(const R:TRect; const Z0,Z1:Single;
            const Height:TCoordinate);
begin
  with Location.Point,R do
  begin
    X:=(Left+Right)*0.5;
    Z:=Height-((Top+Bottom)*0.5);
    Y:=(Z0+Z1)*0.5;
  end;

  with Size.Point,R do
  begin
    X:=Abs(Right-Left);
    Z:=Abs(Bottom-Top);
    Y:=Abs(Z1-Z0);
  end;
end;

procedure TVisualBlock.SizeChanged(Sender:TObject);
begin
end;

{$IFNDEF FMX}

{ TVisualEditor }

procedure TVisualEditor.MarkDirty;
begin
  if Assigned(OnDirty) then
     OnDirty(Self);
end;

procedure TVisualEditor.RefreshBlock(const AVisual:TVisualBlock);
begin
end;
{$ENDIF}

{ TCanvas3D }

Constructor TCanvas3D.Create;
begin
  inherited;
end;

class function TCanvas3D.IsAvailable:Boolean;
begin
  result:=True; // By default, all Canvas classes are valid. See TTeeGDIPlus
end;

procedure TCanvas3D.Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY, Z: Integer);
var tmp : {$IFDEF FMX}TRectI{$ELSE}TRect{$ENDIF};
    P0  : TPoint;
    P1  : TPoint;
begin
  tmp:=CalcRect3D(TeeRect(Left,Top,Right,Bottom),Z){$IFDEF FMX}.Round{$ENDIF};

  P0:=Calculate3DPosition(StartX,StartY,Z);
  P1:=Calculate3DPosition(EndX,EndY,Z);

  Arc(tmp.Left,tmp.Top,tmp.Right,tmp.Bottom,
          {$IFDEF FMX}Round{$ENDIF}(P0.X),
          {$IFDEF FMX}Round{$ENDIF}(P0.Y),
          {$IFDEF FMX}Round{$ENDIF}(P1.X),
          {$IFDEF FMX}Round{$ENDIF}(P1.Y)
          );
end;

Procedure TCanvas3D.Arrow( Filled:Boolean; Const FromPoint,ToPoint:TPoint;
                           ArrowWidth,ArrowHeight,Z:Integer);
begin
  Arrow(Filled,FromPoint,ToPoint,ArrowWidth,ArrowHeight,Z,Z,50);
end;

Procedure TCanvas3D.Assign(const Source:TCanvas3D);
begin
  Monochrome:=Source.Monochrome;
  F3DOptions:=Source.F3DOptions;
  FFontZoom:=Source.FFontZoom;
end;

procedure TCanvas3D.AssignFont3D(const AFont:TTeeFont);
begin
  if (not Supports3DText) and Assigned(View3DOptions) and
     (View3DOptions.ZoomText<>ztNo) and (View3DOptions.ZoomFloat<>100) then
       AssignFontSize(AFont,AFont.SizeFloat*0.01*View3DOptions.ZoomFloat)
     else
       AssignFont(AFont);
end;

function TCanvas3D.RenderToBitmap(var ABitmap:TBitmap; const R:TRect):Boolean;
begin
  result:=False;
end;

function TCanvas3D.BeginEntity(const Entity:String; const Visual:TVisualBlock=nil; const Transform:TTeeTransform=nil):TVisualBlock;
{$IFDEF FMXTRANSFORMS}
var l : Integer;
    m,m2 : TMatrix;
{$ENDIF}
begin
  {$IFDEF FMXTRANSFORMS}
  // Push transform
  l:=Length(ITransform);
  SetLength(ITransform,l+1);
  ITransform[l].Active:=Assigned(Transform) and Transform.Enabled;

  if ITransform[l].Active then
  begin
    ITransform[l].Matrix:=FCanvas.Matrix;

    FCanvas.BeginScene;

    m:=FCanvas.Matrix;

    if (Transform.Translate.X<>0) or (Transform.Translate.Y<>0) then
    begin
      {$IFDEF D19}
      m2:=TMatrix.Identity;
      {$ELSE}
      m2:=IdentityMatrix;
      {$ENDIF}

      m2.m31:=Transform.Translate.X;
      m2.m32:=Transform.Translate.Y;

      {$IFDEF D19}
      m:=m2*m;
      {$ELSE}
      m:=MatrixMultiply(m2,m);
      {$ENDIF}
    end;

    if Transform.Rotate<>0 then
    begin
      {$IFDEF D19}
      m2:=TMatrix.Identity;
      {$ELSE}
      m2:=IdentityMatrix;
      {$ENDIF}

      m2.m31:=Transform.Origin.X;
      m2.m32:=Transform.Origin.Y;

      {$IFDEF D19}
      m:=TMatrix.CreateRotation(DegToRad(Transform.Rotate))*m*m2;
      {$ELSE}
      m:=MatrixMultiply(MatrixMultiply(CreateRotationMatrix(DegToRad(Transform.Rotate)),m2),m);
      {$ENDIF}

      m2.m31:=-Transform.Origin.X;
      m2.m32:=-Transform.Origin.Y;

      {$IFDEF D19}
      m:=m2*m;
      {$ELSE}
      m:=MatrixMultiply(m2,m);
      {$ENDIF}
    end;

    if (Transform.Scale.X<>1) or (Transform.Scale.Y<>1) then
    begin
      {$IFDEF D19}
      m2:=TMatrix.Identity;
      {$ELSE}
      m2:=IdentityMatrix;
      {$ENDIF}

      m2.m31:=Transform.Origin.X;
      m2.m32:=Transform.Origin.Y;

      m2.m11:=Transform.Scale.X;
      m2.m22:=Transform.Scale.Y;

      {$IFDEF D19}
      m:=m2*m;
      {$ELSE}
      m:=MatrixMultiply(m2,m);
      {$ENDIF}

      m2.m11:=1;
      m2.m22:=1;
      m2.m31:=-Transform.Origin.X;
      m2.m32:=-Transform.Origin.Y;

      {$IFDEF D19}
      m:=m2*m;
      {$ELSE}
      m:=MatrixMultiply(m2,m);
      {$ENDIF}
    end;

    FCanvas.SetMatrix(m);
  end;
  {$ENDIF}

  result:=Visual;
end;

procedure TCanvas3D.EndEntity;
{$IFDEF FMXTRANSFORMS}
var l : Integer;
{$ENDIF}
begin
  {$IFDEF FMXTRANSFORMS}
  // Pop transform
  l:=Length(ITransform);

  if l>0 then
  begin
    if ITransform[l-1].Active then
    begin
      FCanvas.SetMatrix(ITransform[l-1].Matrix);
      FCanvas.EndScene;
    end;

    SetLength(ITransform,l-1);
  end;
  {$ENDIF}
end;

class Function TCanvas3D.Description:String;
begin
  result:='';
end;

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

function TCanvas3D.CubeHull(const Rect:TRect; MinZ,MaxZ:Integer):TPointArray;
begin
  SetLength(result,8);

  with Rect do
  begin
    result[0]:=Calculate3DPosition(Left,Bottom,MinZ);
    result[1]:=Calculate3DPosition(Left,Bottom,MaxZ);
    result[2]:=Calculate3DPosition(Left,Top,MaxZ);
    result[3]:=Calculate3DPosition(Right,Top,MaxZ);
    result[4]:=Calculate3DPosition(Right,Top,MinZ);
    result[5]:=Calculate3DPosition(Right,Bottom,MinZ);
    result[6]:=Calculate3DPosition(Left,Top,MinZ);
    result[7]:=Calculate3DPosition(Right,Bottom,MaxZ);
  end;

  ConvexHull(result);
end;

function TCanvas3D.Calc3DPoints(const Points:Array of TPoint; z:Integer):TPointArray;
var t : Integer;
begin
  SetLength(result,Length(Points));

  for t:=0 to Length(Points)-1 do
      result[t]:=Calculate3DPosition(Points[t].X,Points[t].Y,z);
end;

function TCanvas3D.CalcRect3D(const R: TRect; Z: Integer): TRect;
begin
  result.TopLeft:=Calculate3DPosition(R.TopLeft,Z);
  result.BottomRight:=Calculate3DPosition(R.BottomRight,Z);
end;

Function TCanvas3D.Calculate3DPosition(const P:TPoint3D):TPoint; {$IFDEF 10}inline;{$ENDIF}
begin
  result:=Calculate3DPosition(P.X,P.Y,P.Z)
end;

Function TCanvas3D.Calculate3DPosition(P:TPoint; z:Integer):TPoint; {$IFDEF 10}inline;{$ENDIF}
begin
  result:=Calculate3DPosition(P.X,P.Y,z)
end;

procedure TCanvas3D.Cube(const R: TRect; Z0, Z1: Integer;
                         DarkSides: Boolean=True; RoundSize:Integer=0);
begin
  with R{$IFDEF FMX}.Round{$ENDIF} do
       Cube(Left,Right,Top,Bottom,Z0,Z1,DarkSides,RoundSize);
end;

procedure TCanvas3D.Diamond(const R:TRect; Z0,Z1:Integer);
begin
  Diamond(R.Left,R.Top,R.Right,R.Bottom,Z0,Z1);
end;

procedure TCanvas3D.Diamond(const Left,Top,Right,Bottom:TCoordinate; Z0,Z1:Integer);
var tmpMidX : TCoordinate;
    tmpMidY : TCoordinate;
begin
  tmpMidX:=(Left+Right) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
  tmpMidY:=(Top+Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};

  PolygonWithZ([ TeePoint(tmpMidX,Top),
                 TeePoint(Right,tmpMidY),
                 TeePoint(tmpMidX,Bottom),
                 TeePoint(Left,tmpMidY)],(Z0+Z1) div 2);
end;

procedure TCanvas3D.DisableRotation;
begin
end;

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

procedure TCanvas3D.EnableRotation;
begin
end;

procedure TCanvas3D.EllipseWithZ(const Rect:TRect; Z: Integer);
begin
  with Rect do
       EllipseWithZ(Left,Top,Right,Bottom,Z);
end;

procedure TCanvas3D.RoundRect(const R:TRect; X,Y,Z0,Z1:Integer);
begin
  if SupportsFullRotation then
     RectangleWithZ(R,Z0)
  else
     RoundRect(CalcRect3D(R,Z0),X,Y);
end;

procedure TCanvas3D.RoundRectWithZ(const R:TRect; X,Y,Z:Integer);
begin
  RectangleWithZ(R,Z);
end;

Procedure TCanvas3D.TextOut3D(x,y,z:Integer; const Text:String);
begin
  TextOut3D(x,y,z,Text,False);
end;

procedure TCanvas3D.PolygonConcave({$IFDEF D6}const{$ENDIF} Points: array of TPoint; Z:Integer);
begin
  PolygonWithZ(Points,Z);
end;

{$IFDEF PIEFLOATS}
{$IFNDEF FMX}
// Floating-point version
Procedure TTeeCanvas3D.Calculate3DPosition(var P:TPointFloat; const zPos:Single);
var tmp,
    x,y,z, xx,yy,zz : Single;
begin
  if FIsOrthogonal then
  begin
    P.X:= IZoomFactor*(P.x-FXCenter+(IOrthoX*zPos)) +XCenterOffset;
    P.Y:= IZoomFactor*(P.y-FYCenter-(IOrthoY*zPos)) +YCenterOffset;
  end
  else
  if FIs3D then
  begin
    z:=zPos-FZCenter;
    x:=P.x-FXCenter;
    y:=P.y-FYCenter;

    zz:=z*c2 - x*s2;

    xx:=x*c2 + z*s2;
    yy:=y*c1 - zz*s1;

    if IHasPerspec then
       tmp:=IZoomFactor / ( 1+ IZoomPerspec * (zz*c1 + y*s1) )
    else
       tmp:=IZoomFactor;

    P.X:=((xx*c3 - yy*s3)*tmp)+XCenterOffset;
    P.Y:=((yy*c3 + xx*s3)*tmp)+YCenterOffset;
  end
  else
  begin
    P.X:=(IZoomFactor*(P.x-FXCenter)+XCenterOffset);
    P.Y:=(IZoomFactor*(P.y-FYCenter)+YCenterOffset);
  end;
end;
{$ENDIF}
{$ENDIF}

function TCanvas3D.PointFromCircle(const Rect: TRect; const Degrees: Double;
                           zPos: Integer=0; ClockWise: Boolean=False): {$IFDEF PIEFLOATS}TPointFloat{$ELSE}TPoint{$ENDIF};

var xcenter,
    yCenter : Integer;
    radiusH,
    radiusV : Double;
    tmpSin,
    tmpCos  : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  RectCenter(Rect,xCenter,yCenter);

  radiusH := (Rect.Right-Rect.Left) *0.5;
  radiusV := (Rect.Bottom-Rect.Top) *0.5;

  SinCos(TeePiStep*Degrees, tmpSin, tmpCos);

  result.X := xCenter + {$IFNDEF PIEFLOATS}Round{$ENDIF}(radiusH * tmpCos);

  if ClockWise then
     result.Y := yCenter + {$IFNDEF PIEFLOATS}Round{$ENDIF}(radiusV * tmpSin)
  else
     result.Y := yCenter - {$IFNDEF PIEFLOATS}Round{$ENDIF}(radiusV * tmpSin);

  if zPos <> 0 then
     {$IFDEF FMX}result:={$ENDIF}Calculate3DPosition(result, zPos);
end;

class function TCanvas3D.PointFromSpiral(const Rect: TRect; const Degrees,Twist: Double): TPoint;
var xCenter,
    yCenter : Integer;
    radians,
    radiusH,
    radiusV,
    a,r : Double;
    tmpSin,
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  radians := TeePiStep*Degrees;
  radiusH := (Rect.Right-Rect.Left) *0.5;
  radiusV := (Rect.Bottom-Rect.Top) *0.5;

  if radiusH > radiusV then
     a:= radiusV
  else
     a:= radiusH;

  r := a * Exp(radians * twist);

  SinCos(radians,tmpSin,tmpCos);

  RectCenter(Rect,xCenter,yCenter);

  result.X := xCenter + Round(r * tmpCos);
  result.Y := yCenter + Round(r * tmpSin);
end;

procedure TCanvas3D.PolygonWithZ(const Points:Array of TPoint; Z:Integer);
var P : TPointArray;
begin
  P:=Calc3DPoints(Points,Z);
  try
    Polygon(P);
  finally
    P:=nil;
  end;
end;

function TCanvas3D.Calc3DPointsFloat(const Points:Array of TPointFloat; Z:Integer):TPointFloatArray;
var t,l : Integer;
begin
  l:=Length(Points);
  SetLength(result,l);

  for t:=0 to l-1 do
  begin
    result[t]:=Points[t];
    Calculate3DPosition(result[t],Z);
  end;
end;

{$IFNDEF FMX}
procedure TCanvas3D.PolygonFloatWithZ(const Points:Array of TPointFloat; Z:Integer);
var P : TPointFloatArray;
begin
  P:=Calc3DPointsFloat(Points,Z);
  try
    PolygonFloat(P);
  finally
    P:=nil;
  end;
end;
{$ENDIF}

procedure TCanvas3D.Polyline(const Points:Array of TPoint; Z:Integer);
var P : TPointArray;
begin
  P:=Calc3DPoints(Points,Z);
  try
    Polyline(P);
  finally
    P:=nil;
  end;
end;

procedure TCanvas3D.HorizLine3D({$IFDEF FMX}const{$ENDIF} Left, Right, Y, Z: TCoordinate);
begin
  MoveTo3D(Left,Y,Z);
  LineTo3D(Right,Y,Z);
end;

procedure TCanvas3D.VertLine3D({$IFDEF FMX}const{$ENDIF} X, Top, Bottom, Z: TCoordinate);
begin
  MoveTo3D(X,Top,Z);
  LineTo3D(X,Bottom,Z);
end;

procedure TCanvas3D.ZLine3D({$IFDEF FMX}const{$ENDIF} X, Y, Z0, Z1: TCoordinate);
begin
  MoveTo3D(X,Y,Z0);
  LineTo3D(X,Y,Z1);
end;

procedure TCanvas3D.FourPointsFromRect(const R: TRect; Z: Integer; out P: TFourPoints);
begin
  with R do
  begin
    P[0]:=Calculate3DPosition(TopLeft,Z);
    P[1]:=Calculate3DPosition(Right,Top,Z);
    P[2]:=Calculate3DPosition(BottomRight,Z);
    P[3]:=Calculate3DPosition(Left,Bottom,Z);
  end;
end;

procedure TCanvas3D.FrontPlaneBegin;
begin
  if IDisabledRotation=0 then
     DisableRotation;
  Inc(IDisabledRotation);
end;

procedure TCanvas3D.FrontPlaneEnd;
begin
  Dec(IDisabledRotation);
  if IDisabledRotation=0 then
     EnableRotation;
end;

Procedure TCanvas3D.LineWithZ({$IFDEF FMX}const{$ENDIF} X0,Y0,X1,Y1,Z:TCoordinate);
begin
  MoveTo3D(X0,Y0,Z);
  LineTo3D(X1,Y1,Z);
end;

procedure TCanvas3D.LineWithZ(const FromPoint, ToPoint: TPoint; Z: Integer);
begin
  LineWithZ(FromPoint.X,FromPoint.Y,ToPoint.X,ToPoint.Y,Z)
end;

procedure TCanvas3D.MoveTo3D(const X,Y,Z:TCoordinate);
begin
  MoveTo(Calculate3DPosition(X,Y,Z));
end;

{$IFNDEF FMX}
procedure TCanvas3D.MoveTo3D(const X,Y,Z:Single);
begin
  MoveTo(Calculate3DPosition(Round(X),Round(Y),Round(Z)));
end;
{$ENDIF}

procedure TCanvas3D.MoveTo3D(const P:TPoint3D);
begin
  MoveTo3D(P.x,P.y,P.z);
end;

procedure TCanvas3D.LineTo3D(const P:TPoint3D);
begin
  LineTo3D(P.x,P.y,P.z);
end;

procedure TCanvas3D.LineTo3D(const X,Y,Z:TCoordinate);
begin
  LineTo(Calculate3DPosition(X,Y,Z));
end;

{$IFNDEF FMX}
procedure TCanvas3D.LineTo3D(const X,Y,Z:Single);
begin
  LineTo(Calculate3DPosition(Round(X),Round(Y),Round(Z)));
end;
{$ENDIF}

procedure TCanvas3D.PlaneWithZ(const P: TFourPoints; Z: Integer);
begin
  PlaneWithZ(P[0],P[1],P[2],P[3],Z);
end;

Procedure TCanvas3D.Rectangle(Const R:TRect; Z:Integer);
begin
  RectangleWithZ(R,Z);
end;

Procedure TCanvas3D.Rectangle(const X0,Y0,X1,Y1:TCoordinate; Z:Integer); {$IFDEF 10}inline;{$ENDIF}
begin
  RectangleWithZ(TeeRect(X0,Y0,X1,Y1),Z);
end;

function TCanvas3D.RectFromRectZ(const R: TRect; Z: Integer): TRect;
var P : TFourPoints;
begin
  FourPointsFromRect(R,Z,P);
  result:=RectFromPolygon(P,4);
end;

procedure TCanvas3D.RotatedEllipse(Left, Top, Right, Bottom, Z: Integer;
  const Angle: Double);
Var P       : TCirclePoints;
    PiStep  : Double;
    t       : Integer;
    tmpX    : Double;
    tmpY    : Double;
    XCenter : Double;
    YCenter : Double;
    XRadius : Double;
    YRadius : Double;
    tmpSin  : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos  : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpSinAngle : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCosAngle : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  XCenter:=(Right+Left)*0.5;
  YCenter:=(Bottom+Top)*0.5;
  XRadius:=XCenter-Left;
  YRadius:=YCenter-Top;

  piStep:=TwoPi/(NumCirclePoints-1);

  SinCos(Angle*TeePiStep,tmpSinAngle,tmpCosAngle);

  SetLength(P,NumCirclePoints);

  for t:=0 to NumCirclePoints-1 do
  begin
    SinCos(t*piStep,tmpSin,tmpCos);
    tmpX:=XRadius*tmpSin;
    tmpY:=YRadius*tmpCos;

    P[t].X:=XCenter+(tmpX*tmpCosAngle+tmpY*tmpSinAngle);
    P[t].Y:=YCenter+(-tmpX*tmpSinAngle+tmpY*tmpCosAngle);
  end;

  if Brush.Style<>bsClear then
     {$IFDEF FMX}
     PolygonWithZ(P,Z)
     {$ELSE}
     PolygonFloatWithZ(P,Z)
     {$ENDIF}
  else
  if Pen.Style<>psClear then
     PolylineWithZ(P,Z);

  P:=nil;
end;

Procedure TCanvas3D.PolylineWithZ(const Points:Array of TPointFloat; Z:Integer);
var P : TPointFloatArray;
begin
  P:=Calc3DPointsFloat(Points,Z);
  try
    {$IFDEF FMX}
    Polyline(P);
    {$ELSE}
    PolylineFloat(P);
    {$ENDIF}
  finally
    P:=nil;
  end;
end;

procedure TCanvas3D.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);

  procedure DoDrawEllipse;
  begin
    {$IFDEF D6}
    if SameValue(EndAngle-StartAngle,360) then
    {$ELSE}
    if (EndAngle-StartAngle)=360 then
    {$ENDIF}
       Self.Ellipse(Rect)
    else
       Pie(Rect,StartAngle,EndAngle);
  end;

var tmpG,
    tmpB : TTeeGradient;
begin
  Brush.Color:=Shadow.Color;
  Brush.Style:=bsSolid;

  if Pen.Style<>psClear then
  begin
    Pen.Style:=psSolid;
    Pen.Color:=Brush.Color;
  end;

  if Length(P)>0 then
     Polygon(P)
  else
  if Ellipse then
  begin
    if Shadow.Smooth then
    begin
      // Alternative: Draw gradient to simulate an elliptic shadow
      tmpG:=TTeeGradient.Create(nil);
      try
        tmpG.Visible:=True;

        if Shadow.Color=clBlack then
           tmpG.EndColor:=clSilver
        else
           tmpG.EndColor:=ApplyBright(Shadow.Color,32);

        tmpG.StartColor:=ApplyDark(Shadow.Color,128);
        tmpG.Direction:=gdRadial;

        tmpB:=TTeeGradient.Create(nil);
        try
          tmpB.Assign(Gradient);

          Gradient.Assign(tmpG);
          Pen.Style:=psClear;

          DoDrawEllipse;

          Gradient.Assign(tmpB);
        finally
          tmpB.Free;
        end;
      finally
        tmpG.Free;
      end;
    end
    else
      DoDrawEllipse;
  end
  else
  if RoundSize=0 then
     FillRect(Rect)
  else
     RoundRect(Rect,RoundSize,RoundSize);
end;

Function TTeeCanvas.IsAliased:Boolean;
begin
  Result:={$IFDEF FMX}True{$ELSE}False{$ENDIF};
end;

procedure TTeeCanvas.SetAntiAlias(const Value:Boolean);
begin
  // Nothing here. See TGDIPlusCanvas class.
  // Someday maybe FireMonkey will allow changing antialias on-the-fly.
end;

{$IFDEF FMX}
procedure AddPointsToPath(const APath:FMXTPathData; const Points:Array of TPoint; Closed:Boolean);
var t,l : Integer;
begin
  l:=Length(Points);

  if l>0 then
  begin
    APath.MoveTo(Points[0]);

    for t:=1 to l-1 do
        APath.LineTo(Points[t]);

    if Closed then
       APath.ClosePath;
  end;
end;

function PointsPath(const Points:Array of TPoint; Closed:Boolean):FMXTPathData;
begin
  result:=FMXTPathData.Create;
  AddPointsToPath(result,Points,Closed);
end;
{$ENDIF}

{$IFNDEF LCL}
{$DEFINE USE_SCANLINE}
{$ENDIF}

{$IFDEF FMX}
{$IFDEF D17}
{$UNDEF USE_SCANLINE} // TBitmap.Map and UnMap
{$ENDIF}
{$ENDIF}

{$IFDEF FMX}
function DifferentPoint(const A,B:TPoint):Boolean;
begin
  result:=(A.X<>B.X) or (A.Y<>B.Y);
end;
{$ENDIF}

{$IFDEF FMX}
{$IFDEF D17}
type
  FMXTBitmapData={$IFDEF D19}FMX.Graphics.TBitmapData{$ELSE}FMX.Types.TBitmapData{$ENDIF};
{$ENDIF}
{$ENDIF}

procedure TCanvas3D.StretchDraw(const Rect:TRect; const Graphic: TGraphic;
                                const Pos:Double; Plane:TCanvas3DPlane=cpZ);

var
  IPoints : TFourPoints;
  tmpPos  : Integer;
  R       : TRect;

  {$IFDEF FMX}
  OldPoints : TFourPoints;

  // Warning: This function is here to avoid an AV at runtime in Android ARM
  // (possible compiler bug, too complex expression)
  function DifferentPoints:Boolean;
  begin
    result:=DifferentPoint(IPoints[0],OldPoints[0]) or
            DifferentPoint(IPoints[1],OldPoints[1]) or
            DifferentPoint(IPoints[2],OldPoints[2]) or
            DifferentPoint(IPoints[3],OldPoints[3]);
  end;
  {$ENDIF}

  procedure CalcSide(const APos:TCoordinate; const Index1,Index2:Integer);
  begin
    case Plane of
      cpX: begin
             IPoints[Index1]:=Calculate3DPosition(tmpPos,R.Top,APos);
             IPoints[Index2]:=Calculate3DPosition(tmpPos,R.Bottom,APos);
           end;
      cpY: begin
             IPoints[Index1]:=Calculate3DPosition(APos,tmpPos,R.Top);
             IPoints[Index2]:=Calculate3DPosition(APos,tmpPos,R.Bottom);
           end;
    else
    begin
      IPoints[Index1]:=Calculate3DPosition(APos,R.Top,tmpPos);
      IPoints[Index2]:=Calculate3DPosition(APos,R.Bottom,tmpPos);
    end;
    end;
  end;

{$IFNDEF LCL}
const
  BytesPerPixel={$IFDEF FMX}4{$ELSE}3{$ENDIF};
{$ENDIF}

var x,y,
    tmpW,
    tmpH  : Integer;

    DestW,
    DestH : Double;

    Bitmap : TBitmap;

    {$IFDEF USE_SCANLINE}
    tmpScan : {$IFDEF FMX}PAlphaColorArray{$ELSE}PByteArray{$ENDIF};
    Line    : {$IFDEF FMX}PAlphaColorArray{$ELSE}PByteArray{$ENDIF};
    Dif     : TeeNativeInt;
    {$ENDIF}

    {$IFNDEF FMX}
    {$IFNDEF LCL}
    P       : PChar;
    {$ENDIF}
    {$ENDIF}

    {$IFDEF LCL}
    tmpCanvas : TCanvas;
    {$ENDIF}

    {$IFNDEF FMX}
    tmpFastBrush : Boolean;
    CanvasDC  : TTeeCanvasHandle;
    {$ENDIF}

    {$IFDEF FMX}
    IFMXP     : TPolygon;
    tmpOpacity: Single;
    {$IFDEF D17}
    tmpData   : FMXTBitmapData;
    {$ENDIF}
    {$ENDIF}
begin
  Pen.Style:=psClear;

  if Graphic is TBitmap then
  begin
    Bitmap:=TBitmap(Graphic);
    {$IFNDEF FMX}
    Bitmap.PixelFormat:=TeePixelFormat;
    {$ENDIF}
  end
  else
  begin
    Bitmap:=TBitmap.Create{$IFNDEF D19}{$IFDEF FMX}(0,0){$ENDIF}{$ENDIF};
    {$IFNDEF FMX}
    Bitmap.PixelFormat:=TeePixelFormat;

    {$IFDEF IGNOREPALETTE}
    Bitmap.IgnorePalette:=Graphic.Palette=0;
    {$ENDIF}
    {$ENDIF}

    Bitmap.Assign(Graphic);
    {$IFNDEF FMX}
    Bitmap.PixelFormat:=TeePixelFormat;
    {$ENDIF}
  end;

  tmpW:=Bitmap.Width;
  tmpH:=Bitmap.Height;

  if (tmpW=0) or (tmpH=0) then
     Exit;

  DestH:=(Rect.Bottom-Rect.Top)/tmpH;
  DestW:=(Rect.Right-Rect.Left)/tmpW;

  {$IFDEF LCL}
  tmpCanvas:=Bitmap.Canvas;
  {$ELSE}

  {$IFDEF FMX}
  {$IFDEF D17}
  if not Bitmap.Map(TMapAccess.{$IFDEF D20}Read{$ELSE}maRead{$ENDIF},tmpData) then
     Exit;
  {$ELSE}
  Line:=Bitmap.ScanLine[0];

  if tmpH>1 then
     Dif:=TeeNativeInt(Bitmap.ScanLine[1])-TeeNativeInt(Line)
  else
     Dif:=0;
  {$ENDIF}
  {$ELSE}
  Line:=Bitmap.ScanLine[0];

  if tmpH>1 then
     Dif:=TeeNativeInt(Bitmap.ScanLine[1])-TeeNativeInt(Line)
  else
     Dif:=0;
  {$ENDIF}

  {$ENDIF}

  {$IFNDEF FMX}
  tmpFastBrush:=Assigned(@TeeSetDCBrushColor) and CheckFastBrush;

  if tmpFastBrush then
  begin
    CanvasDC:=Handle;

    {$IFDEF TEEWINDOWS}
    {$IFDEF MSWINDOWS}
    SelectObject(CanvasDC,GetStockObject(DC_BRUSH));
    {$ENDIF}
    {$ENDIF}
  end
  else
    CanvasDC:=0;
  {$ENDIF}

  {$IFDEF FMX}
  SetLength(IFMXP,4);
  tmpOpacity:=Opacity;
  {$ENDIF}

  SetAntiAlias(False);

  tmpPos:=Round(Pos);

  R.Top:=Rect.Top;

  for y:=0 to tmpH-1 do
  begin
    {$IFDEF USE_SCANLINE}
    tmpScan:={$IFDEF FMX}PAlphaColorArray{$ELSE}PByteArray{$ENDIF}(TeeNativeInt(Line)+Dif*y);
    {$ENDIF}

    R.Bottom:=Rect.Top+Round(DestH*(y+1));
    R.Left:=Rect.Left;

    CalcSide(R.Left,0,3);

    for x:=0 to tmpW-1 do
    begin
      R.Right:=Rect.Left+Round(DestW*(x+1));

      CalcSide(R.Right,1,2);

      {$IFDEF LCL}
      Brush.Color:=tmpCanvas.Pixels[x,y];
      {$ELSE}

       {$IFDEF FMX}
       if DifferentPoints then
       begin
         {$IFDEF D17}
         FCanvas.Fill.Color:=tmpData.GetPixel(x,y);
         {$ELSE}
         FCanvas.Fill.Color:=$FF000000 or PAlphaColorArray(tmpScan)[X];
         {$ENDIF}

         IFMXP[0]:=IPoints[0];
         IFMXP[1]:=IPoints[1];
         IFMXP[2]:=IPoints[2];
         IFMXP[3]:=IPoints[3];

         FCanvas.FillPolygon(IFMXP, tmpOpacity);
       end;
       {$ELSE}

       p:=PChar(@tmpScan[X*BytesPerPixel]);

       {$IFNDEF TEESAFECLR}
       if tmpFastBrush then
          TeeSetDCBrushColor(CanvasDC,Byte((p+2)^) or (Byte((p+1)^) shl 8) or (Byte((p)^) shl 16))
       else
          Brush.Color:=Byte((p+2)^) or (Byte((p+1)^) shl 8) or (Byte((p)^) shl 16);
       {$ENDIF}
       {$ENDIF}

      {$ENDIF}

      {$IFNDEF FMX}
      Polygon(IPoints);
      {$ENDIF}

      R.Left:=R.Right;

      IPoints[0]:=IPoints[1];
      IPoints[3]:=IPoints[2];
    end;

    R.Top:=R.Bottom;
  end;

  SetAntiAlias(True);

  {$IFNDEF FMX}
  if tmpFastBrush then
     Brush.Handle:=0;
  {$ENDIF}

  {$IFDEF FMX}
  IFMXP:=nil;
  {$IFDEF D17}
  Bitmap.Unmap(tmpData);
  {$ENDIF}
  {$ENDIF}

  if not (Graphic is TBitmap) then
     Bitmap.Free;
end;

Procedure TCanvas3D.TrianglePointsTo2D(const P:TTrianglePoints3D; out Result:TTrianglePoints);
begin
  result[0]:=Calculate3DPosition(P[0]);
  result[1]:=Calculate3DPosition(P[1]);
  result[2]:=Calculate3DPosition(P[2]);
end;

procedure TCanvas3D.Triangles({$IFDEF D6}const{$ENDIF} P:Array of TPoint3D;
                              CreateCached:Boolean;
                              var Cached:TTeeCachedObject);
begin
  // OpenGL only
end;

{$IFDEF FPC}
function TCanvas3D.QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} IID: TGUID; out Obj): HResult; cdecl;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TCanvas3D._AddRef: Integer; cdecl;
begin
  result:=-1;
end;

function TCanvas3D._Release: Integer; cdecl;
begin
  result:=-1;
end;

{$ELSE}
function TCanvas3D._AddRef: Integer;
begin
  result:=-1;
end;

function TCanvas3D._Release: Integer;
begin
  result:=-1;
end;

function TCanvas3D.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;
{$ENDIF}

{ TTeeCanvas3D }

Constructor TTeeCanvas3D.Create;
begin
  inherited;

  FontZoom:=100;
  IZoomText:=True;

  {$IFNDEF FMX}
  FontQuality:=fqBest;
  {$ENDIF}

  // Firemonkey: Better speed with buffered True, but there are problems
  // when charts are inside scrollable layouts, etc.

  // BufferedDisplay (internal bitmap buffer)

  // Internal code no longer valid.
  // Speed is very similar with or without buffer. Buffer is better to offer quick
  // response to system paints when our canvas is not Dirty.

  // Internal test:
  (*
  // FMX: True only for GPU Canvas, or any canvas class that does not support Clip Rectangles.
  // Windows and OSX = False,  iOS/Android = True
  FBufferedDisplay:={$IFDEF FMX}
                    not (TCanvasStyle.SupportClipRects in TCanvasManager.DefaultCanvas.GetCanvasStyle)
                    {$ELSE}
                    True  // <-- VCL: Always True by default
                    {$ENDIF};
  *)

  // Default to True in <XE8, to give better speed and no flickering, for example at TeeGallery.
  FBufferedDisplay:={$IFDEF FMX}{$IFDEF D22}False{$ELSE}True{$ENDIF}{$ELSE}True{$ENDIF};

  FDirty:=True;
end;

Procedure TTeeCanvas3D.DeleteBitmap;
begin
  FreeAndNil(FBitmap);
end;

Destructor TTeeCanvas3D.Destroy;
begin
  DeleteBitmap;
  inherited;
end;

Function TTeeCanvas3D.GetBackMode:TCanvasBackMode;
begin
  {$IFDEF FMX}
  result:=cbmNone;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  result:=TCanvasBackMode(GetBkMode(FCanvas.Handle));
  {$ELSE}
  result:=TCanvasBackMode.cbmOpaque;
  {$ENDIF}
  {$ENDIF}
end;

Procedure TTeeCanvas3D.SetBackMode(const Value:TCanvasBackMode); { Opaque, Transparent }
begin
  {$IFDEF FMX}
  {$ELSE}
  {$IFDEF MSWINDOWS}
  SetBkMode(FCanvas.Handle,Ord(Value));
  {$ENDIF}
  {$ENDIF}
end;

Procedure TTeeCanvas3D.SetBackColor(const Value:TColor);
begin
  {$IFDEF FMX}
  {$ELSE}
  {$IFDEF MSWINDOWS}
  SetBkColor(FCanvas.Handle,TColorRef(Value));
  {$ENDIF}
  {$ENDIF}
end;

class function TTeeCanvas3D.Description:String;
begin
  result:='GDI'; //TeeMsg_GDI;
end;

function TTeeCanvas3D.GetBackColor:TColor;
begin
  {$IFDEF FMX}
  result:=clWhite;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  result:=TColor(GetBkColor(FCanvas.Handle));
  {$ELSE}
  result:=clWhite;
  {$ENDIF}
  {$ENDIF}
end;

Procedure TTeeCanvas3D.TextOut({$IFDEF FMX}const{$ENDIF} X,Y:TCoordinate; const Text:String);
{$IFNDEF FMX}
var tmpDC   : TTeeCanvasHandle;
    OldFont : HFont;
{$ENDIF}
begin
  {$IFDEF FMX}
  DirectTextOut(0,Round(X),Round(Y),Text);
  {$ELSE}

  {$IFDEF MSWINDOWS}
  tmpDC:=Handle;

  OldFont:=GetNewFont(tmpDC,FFont.Size);

  DirectTextOut(tmpDC,X,Y,Text);

  if OldFont<>0 then
     DeleteObject(SelectObject(tmpDC,OldFont));

  {$ELSE}
  DirectTextOut(0,Round(X),Round(Y),Text);
  {$ENDIF}

  {$ENDIF}
end;

Procedure TTeeCanvas3D.DirectTextOut(AHandle:TTeeCanvasHandle; X,Y:Integer; const Text:String);
{$IFDEF AXUNICODE}
var tmpWideString : WideString;
{$ENDIF}

  {$IFNDEF FMX}
  Function RectText(tmpX,tmpY:Integer):TRect;
  var tmpW,
      tmpH : Integer;
      tmp  : Integer;
  begin
    with TextSize(Text) do
    begin
      tmpW:=Round(X);
      tmpH:=Round(Y);
    end;

    //tmpW:=TextWidth(Text);
    //tmpH:=TextHeight(Text);

    tmp:=TextAlign;
    if tmp>=TA_BOTTOM then Dec(tmp,TA_BOTTOM);

    if tmp=TA_RIGHT then
       result:=TeeRect(tmpX-tmpW,tmpY,tmpX,tmpY+tmpH)
    else
    if tmp=TA_CENTER then
       result:=TeeRect(tmpX-(tmpW div 2),tmpY,tmpX+(tmpW div 2),tmpY+tmpH)
    else
       result:=TeeRect(tmpX,tmpY,tmpX+tmpW,tmpY+tmpH);
  end;
  {$ENDIF}

  {$IFDEF FMX}
  Procedure InternalTextOut(AX,AY:Integer; UseFont:Boolean=True);
  var tmp : Integer;
      tmpPath : FMXTPathData;
  begin
    tmp:=TextAlign;

    if tmp>=TA_BOTTOM then
    begin
      Dec(AY,TextHeight(Text));
      Dec(tmp,TA_BOTTOM);
    end;

    if tmp=TA_CENTER then
       Dec(AX,TextWidth(Text) div 2)
    else
    if tmp=TA_RIGHT then
       Dec(AX,TextWidth(Text));

    tmpPath:=nil;

    // Gradient or Outline ??
    if UseFont then
       if Font.Brush.HasGradient or
          (Assigned(Font.FOutline) and Font.FOutLine.Visible) then
       begin
         tmpPath:=FMXTPathData.Create;

         if not FCanvas.TextToPath(tmpPath,RectF(0,0,10000,10000),Text,False,
                                   TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
                                   TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF}) then
            FreeAndNil(tmpPath);
       end;

    {$IFDEF TEECACHEDLAYOUT}
    ICachedLayout(AX,AY,Text).RenderLayout(FCanvas);
    {$ELSE}

    if Font.Brush.HasGradient and Assigned(tmpPath) then
    begin
      PrepareGradient(Font.Gradient,FCanvas.Fill.Gradient);
      FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};

      tmpPath.Translate(AX,AY);
      FCanvas.FillPath(tmpPath,Opacity);
      tmpPath.Translate(-AX,-AY);
    end
    else
      FCanvas.FillText(RectF(AX,AY,10000,10000),Text,False,Opacity,[],
                       TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF},
                       TTextAlign.{$IFDEF D20}Leading{$ELSE}taLeading{$ENDIF});
    {$ENDIF}

    if UseFont then
    with FFont do
    if Assigned(FOutline) and FOutLine.Visible and Assigned(tmpPath) then
    begin
      AssignVisiblePen(FOutLine);

      tmpPath.Translate(AX,AY);
      FCanvas.DrawPath(tmpPath,Opacity);
    end;

    tmpPath.Free;
  end;
  {$ENDIF}

  {$IFNDEF FMX}
  procedure DoTextOut(X,Y:Integer);
  begin
    {$IFDEF AXUNICODE}
    Windows.TextOutW(AHandle,X,Y,PWideChar(tmpWideString),LStrLenW(PWideChar(tmpWideString)));
    {$ELSE}
    {$IFDEF MSWINDOWS}
    Windows.TextOut(AHandle,X,Y,PChar(Text),Length(Text));
    {$ELSE}
    FCanvas.TextOut(X,Y,Text);
    {$ENDIF}
    {$ENDIF}
  end;

  Procedure CreateFontPath;
  begin
    {$IFDEF MSWINDOWS}
    BeginPath(AHandle);
    DoTextOut(X,Y);
    EndPath(AHandle);
    {$ENDIF}
  end;
  {$ENDIF}

  procedure DoFontShadow(const AShadow:TTeeShadow; AFactor:Integer);
  var tmpX : Integer;
      tmpY : Integer;
      tmpBlend : TTeeBlend;

      {$IFDEF FMX}
      tmpColor : TColor;
      {$ELSE}
      tmpW : Integer;
      tmpAlign : Integer;
      {$ENDIF}
  begin
    with AShadow do
    if Visible and ((HorizSize<>0) or (VertSize<>0)) then
    begin
      if HorizSize<0 then
      begin
        tmpX:=X;
        Dec(X,AFactor*HorizSize);
      end
      else tmpX:=X+AFactor*HorizSize;

      if VertSize<0 then
      begin
        tmpY:=Y;
        Dec(Y,AFactor*VertSize);
      end
      else tmpY:=Y+AFactor*VertSize;

      if Transparency>0 then
         if SupportsAlpha then
            tmpBlend:=BeginBlending(TeeZeroRect,Transparency)
         else
            tmpBlend:=BeginBlending({$IFDEF FMX}TeeZeroRect{$ELSE}RectText(tmpX,tmpY){$ENDIF},Transparency)
      else
         tmpBlend:=nil;

      {$IFDEF FMX}
      tmpColor:=FCanvas.Fill.Color;
      FCanvas.Fill.Color:=AShadow.Color;
      InternalTextOut(tmpX,tmpY,False);
      FCanvas.Fill.Color:=tmpColor;
      {$ELSE}

      {$IFDEF MSWINDOWS}
      SetTextColor(AHandle,AShadow.Color);
      {$ELSE}
      FCanvas.Font.Color:=AShadow.Color;
      {$ENDIF}

      DoTextOut(tmpX,tmpY);

      if Smooth then
      if Assigned(Self.FBitmap) then
      begin
        with TBlurFilter.Create(nil) do
        try
          Steps:=Max(1,SmoothBlur);

          tmpW:=FCanvas.TextWidth(Text);

          tmpAlign:=Self.TextAlign;

          if tmpAlign>=TA_BOTTOM then
             Dec(tmpAlign,TA_BOTTOM);

          if tmpAlign=TA_RIGHT then
             Dec(tmpX,tmpW)
          else
          if tmpAlign=TA_CENTER then
             Dec(tmpX,tmpW div 2);

          // Do not use TextHeight/TextWidth as they already increase shadow size
          Apply(FBitmap,TeeRect(Max(0,tmpX-1),Max(0,tmpY),
                             Min(FBitmap.Width-1, tmpX+tmpW),
                             Min(FBitmap.Height-1, tmpY+FCanvas.TextHeight(Text)-1))
                             );
        finally;
          Free;
        end;

        AHandle:=FCanvas.Handle;
        TextAlign:=tmpAlign;
        BackMode:=cbmTransparent;
      end;
      {$ENDIF}

      if Transparency>0 then
         EndBlending(tmpBlend);
    end;
  end;

{$IFNDEF FMX}
var
  tmpFontGradient : Boolean;
  tmpFontOutLine  : Boolean;
  tmpFontPic      : Boolean;
  tmpHasOutlineGradient : Boolean;
  tmp : Integer;
{$ENDIF}
{$IFDEF TEECANVASDEBUG}
var tmpSize : TPointFloat;
{$ENDIF}
begin
  {$IFDEF FMX}
  FCanvas.Fill.Assign(FFont.FBrush);
  {$ENDIF}

  if Assigned(FFont.FShadow) then
     DoFontShadow(FFont.FShadow,1);

  if Assigned(FFont.FEmboss) then
     DoFontShadow(FFont.FEmboss,-1);

  {$IFDEF FMX}
  if Font.Gradient.Visible then
     PrepareBrush;

  InternalTextOut(x,y);
  {$ELSE}

  {$IFDEF MSWINDOWS}
  SetTextColor(AHandle, FFont.Color);
  {$ELSE}
  FCanvas.Font.Color:=FFont.Color;
  {$ENDIF}

  {$IFDEF AXUNICODE}
  tmpWideString:=AXUnicode(Text);
  {$ENDIF}

  with FFont do
  begin
    tmpFontOutLine:=Assigned(FOutline) and FOutLine.Visible;
    tmpFontGradient:=Assigned(FBrush.FGradient) and FBrush.FGradient.Visible;
    tmpFontPic:=FBrush.HasGraphic;
  end;

  if tmpFontOutLine or tmpFontGradient or tmpFontPic then
  begin
    if tmpFontOutLine then AssignVisiblePen(FFont.FOutLine)
                      else Pen.Style:=psClear;

    Brush.Color:=FFont.Color;
    Brush.Style:=bsSolid;

    BackMode:=cbmTransparent;

    CreateFontPath;

    if tmpFontGradient then
    begin
      tmpHasOutlineGradient:=Assigned(FFont.FOutLine) and
                             Assigned(FFont.FOutLine.FFill) and
                             FFont.FOutLine.FFill.HasGradient;

      {$IFDEF MSWINDOWS}
      if tmpHasOutlineGradient then
         WidenPath(AHandle);

      SelectClipPath(AHandle,RGN_AND);
      {$ENDIF}

      FFont.FBrush.FGradient.Draw(Self,RectText(x,y));
      UnClipRectangle;

      if tmpHasOutlineGradient then
         tmpFontOutline:=False
      else
      if tmpFontOutLine then
      begin
        // Create path again...
        CreateFontPath;
        Brush.Style:=bsClear;
      end;
    end
    else
    if tmpFontPic then
    begin
      {$IFDEF MSWINDOWS}
      SelectClipPath(AHandle,RGN_AND);
      {$ENDIF}

      StretchDraw(RectText(x,y),FFont.FBrush.FImage.Filtered);
      UnClipRectangle;

      // Create path again...
      if tmpFontOutLine then
      begin
        CreateFontPath;
        Brush.Style:=bsClear;
      end;
    end;

    {$IFDEF MSWINDOWS}
    if tmpFontOutLine then
       if FFont.Color=clNone then
          StrokePath(AHandle)
       else
       begin
         StrokeAndFillPath(AHandle);
         tmp:=(FFont.FOutline.Width div 2);
         DoTextOut(X+tmp,Y+tmp);
       end;
    {$ENDIF}

  end
  else DoTextOut(X,Y);
  {$ENDIF}

  {$IFDEF TEECANVASDEBUG}
  Pen.Color:=clRed;
  Pen.Style:=psSolid;
  Brush.Style:=bsClear;
  Rectangle(RectText(X,Y));
  {$ENDIF}
end;

procedure TTeeCanvas3D.Rectangle(const X0,Y0,X1,Y1:TCoordinate);
var
{$IFDEF FMX}
    R : TRect;
{$ELSE}
    tmpGradient : Boolean;
    oldStyle    : TBrushStyle;
{$ENDIF}

begin
  {$IFDEF FMX}
  R:=TeeRect(X0,Y0,X1,Y1);

  if FBrush.Kind<>TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF} then
  begin
    PrepareBrush;
    FCanvas.FillRect(R,0,0,AllCorners, Opacity);
    FBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  end;

  if FPen.Style<>psClear then
    FCanvas.DrawRect(R,0,0,AllCorners, Opacity);
  {$ELSE}

  oldStyle:=FBrush.Style;

  if FBrush.HasGraphic then
  begin
    FCanvas.StretchDraw(Rect(X0,Y0,X1,Y1),FBrush.Image.Filtered);
    FCanvas.Brush.Style:=bsClear;
  end;

  tmpGradient:=IsGradientVisible;

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

  {$IFDEF MSWINDOWS}
  Windows.Rectangle(FCanvas.Handle,X0,Y0,X1,Y1);
  {$ELSE}
  FCanvas.Rectangle(X0,Y0,X1,Y1);
  {$ENDIF}

  if tmpGradient then
     FBrush.Style:=oldStyle;
  {$ENDIF}
end;

procedure TTeeCanvas3D.RoundRect(X1,Y1,X2,Y2,X3,Y3:Integer);
var tmpR        : TRect;
{$IFNDEF FMX}
    tmpGradient : Boolean;
    oldStyle    : TBrushStyle;
{$ENDIF}
begin
  {$IFDEF FMX}
  tmpR:=TeeRect(X1,Y1,X2,Y2);

  if FBrush.Kind<>TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF} then
  begin
    PrepareBrush;
    FCanvas.FillRect(tmpR,X3 div 2,Y3 div 2,AllCorners,Opacity);
    FBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  end;

  if FPen.Style<>psClear then
     FCanvas.DrawRect(tmpR,X3 div 2,Y3 div 2,AllCorners,Opacity);
  {$ELSE}

  oldStyle:=FBrush.Style;

  if FBrush.HasGraphic then
  begin
    tmpR:=TeeRect(X1,Y1,X2,Y2);
    ClipRectangle(tmpR,X3,Y3);
    FCanvas.StretchDraw(Rect(X1,Y1,X2,Y2),FBrush.Image.Filtered);
    UnClipRectangle;
    FCanvas.Brush.Style:=bsClear;
  end;

  tmpGradient:=IsGradientVisible;

  if tmpGradient then
  begin
    tmpR:=TeeRect(X1,Y1,X2,Y2);
    ClipRectangle(tmpR,X3,Y3);
    GradientFill(FBrush.FGradient,tmpR);
    UnClipRectangle;
    FCanvas.Brush.Style:=bsClear;
  end;

  {$IFDEF MSWINDOWS}
  Windows.RoundRect(FCanvas.Handle,X1,Y1,X2,Y2,X3,Y3);
  {$ELSE}
  FCanvas.RoundRect(X1,Y1,X2,Y2,X3,Y3);
  {$ENDIF}

  if tmpGradient then
     FBrush.Style:=oldStyle;
  {$ENDIF}
end;

procedure TTeeCanvas3D.SetTextAlign(const Value:TCanvasTextAlign);
{$IFNDEF FMX}
{$IFNDEF USEWINDOWS}
var tmp : TTextStyle;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
  FTextAlign:=Value;
  {$ELSE}
  {$IFDEF USEWINDOWS}
  {$IFNDEF OSX} // Pending: CrossVCL
  Windows.SetTextAlign(FCanvas.Handle,Ord(Value));
  {$ENDIF}
  {$ELSE}
  tmp:=FCanvas.TextStyle;

  case Value of
    TA_CENTER : tmp.Alignment:=TAlignment.taCenter;
    TA_RIGHT  : tmp.Alignment:=TAlignment.taRightJustify;
  else
    tmp.Alignment:=TAlignment.taLeftJustify;
  end;

  FCanvas.TextStyle:=tmp;
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeeCanvas3D.MoveTo(const X,Y:TCoordinate);
begin
  {$IFDEF FMX}
  FX:=X;
  FY:=Y;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  Windows.MoveToEx(FCanvas.Handle, X, Y, nil);
  {$ELSE}
  FCanvas.MoveTo(X,Y);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeeCanvas3D.SetFastLine(const Active:Boolean);
begin
  if Active then
     IFastLine:={$IFDEF FMX}1{$ELSE}Handle{$ENDIF}
  else
     IFastLine:=0;
end;

procedure TTeeCanvas3D.FastLineTo(const X,Y:Integer);
begin
  {$IFDEF FMX}
  FCanvas.DrawLine(TeePoint(FX,FY),TeePoint(X,Y), Opacity);
  FX:=X;
  FY:=Y;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  Windows.LineTo(IFastLine, X, Y);
  {$ELSE}
  FCanvas.LineTo(X,Y);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeeCanvas3D.LineTo(const X,Y:TCoordinate);
begin
  {$IFDEF FMX}
  FCanvas.DrawLine(TeePoint(FX,FY),TeePoint(X,Y), Opacity);
  FX:=X;
  FY:=Y;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  Windows.LineTo(FCanvas.Handle, X, Y);
  {$ELSE}
  FCanvas.LineTo(X,Y);
  {$ENDIF}
  {$ENDIF}
end;

{ 3D Canvas }
Procedure TTeeCanvas3D.PolygonFour;
begin
  Polygon(IPoints);
end;

procedure TTeeCanvas3D.PlaneWithZ(P1,P2,P3,P4:TPoint; Z:Integer);
begin
  Calc3DTPoint(P1,Z);
  Calc3DTPoint(P2,Z);
  Calc3DTPoint(P3,Z);
  Calc3DTPoint(P4,Z);
  IPoints[0]:=P1;
  IPoints[1]:=P2;
  IPoints[2]:=P3;
  IPoints[3]:=P4;
  PolygonFour;
end;

Procedure TTeeCanvas3D.Calc3DTPoint(Var P:TPoint; z:Integer);
begin
  {$IFDEF FMX}
  Calc3DPoint(P,P.X,P.Y,z);
  {$ELSE}
  Calc3DPos(P.X,P.Y,Z);
  {$ENDIF}
end;

Function TTeeCanvas3D.Calc3DTPoint3D(Const P:TPoint3D):TPoint;
begin
  Calc3DPoint(result,P.X,P.Y,P.Z);
end;

Function TTeeCanvas3D.Calculate3DPosition(x,y,z:Integer):TPoint;
begin
  Calc3DPos(x,y,z);
  result.x:=x;
  result.y:=y;
end;

{$IFDEF FMX}
Function TTeeCanvas3D.Calculate3DPosition(const x,y,z:Single):TPoint;
begin
  Calc3DPoint(result, x,y,z);
end;
{$ENDIF}

Procedure TTeeCanvas3D.Calc3DPoint(out P:TPoint; x,y,z:Integer);
begin
  Calc3DPos(x,y,z);
  P.x:=x;
  P.y:=y;
end;

Procedure TTeeCanvas3D.Calculate2DPosition(var x,y:Integer; z:Integer);
var x1  : Integer;
    tmp : Double;
begin
  if IZoomFactor<>0 then
  begin
    tmp:=1.0/IZoomFactor;

    if FIsOrthogonal then
    begin
      x:=Round(((x-XCenterOffset)*tmp)-(IOrthoX*z))+FXCenter;
      y:=Round(((y-YCenterOffset)*tmp)+(IOrthoY*z))+FYCenter;
    end
    else
    if FIs3D and (tempXX<>0) and (c2c3<>0)  then
    begin
      x1:=x;
      z:=z-FZCenter;

      x:=Round((((x1-XCenterOffset)*tmp)-(z*tempXZ)-
                 (y -FYCenter)*c2s3)   / tempXX) + FXCenter;
      y:=Round((((y -YCenterOffset)*tmp)-(z*tempYZ)-
                 (x1-FXCenter)*tempYX) / c2c3)   + FYCenter;
    end;
  end;
end;

// Floating-point version
Procedure TTeeCanvas3D.Calc3DPoint(out P:TPoint; x,y,z:{$IFDEF FMX}Single{$ELSE}Double{$ENDIF});
var tmp,
    xx,yy,zz : {$IFDEF FMX}Single{$ELSE}Double{$ENDIF};
begin
  if FIsOrthogonal then
  begin
    P.X:={$IFNDEF FMX}Round{$ENDIF}( IZoomFactor*(x-FXCenter+(IOrthoX*z)) )+XCenterOffset;
    P.Y:={$IFNDEF FMX}Round{$ENDIF}( IZoomFactor*(y-FYCenter-(IOrthoY*z)) )+YCenterOffset;
  end
  else
  if FIs3D then
  begin
    z:=z-FZCenter;
    x:=x-FXCenter;
    y:=y-FYCenter;

    zz:=z*c2 - x*s2;

    xx:=x*c2 + z*s2;
    yy:=y*c1 - zz*s1;

    if IHasPerspec then
       tmp:=IZoomFactor / ( 1+ IZoomPerspec * (zz*c1 + y*s1) )
    else
       tmp:=IZoomFactor;

    P.X:={$IFNDEF FMX}Round{$ENDIF}((xx*c3 - yy*s3)*tmp)+XCenterOffset;
    P.Y:={$IFNDEF FMX}Round{$ENDIF}((yy*c3 + xx*s3)*tmp)+YCenterOffset;
  end
  else
  begin
    P.X:={$IFNDEF FMX}Round{$ENDIF}(IZoomFactor*(x-FXCenter)+XCenterOffset);
    P.Y:={$IFNDEF FMX}Round{$ENDIF}(IZoomFactor*(y-FYCenter)+YCenterOffset);
  end;
end;

// Integer version
Procedure TTeeCanvas3D.Calc3DPos(var x,y:Integer; z:Integer);
var tmp : Double;
    xx,yy,zz : Double;
begin
  if FIsOrthogonal then
  begin
    if z=0 then
    begin
      x:=Round( IZoomFactor*(x-FXCenter) )+XCenterOffset;
      y:=Round( IZoomFactor*(y-FYCenter) )+YCenterOffset;
    end
    else
    begin
      x:=Round( IZoomFactor*(x-FXCenter+(IOrthoX*z)) )+XCenterOffset;
      y:=Round( IZoomFactor*(y-FYCenter-(IOrthoY*z)) )+YCenterOffset;
    end;
  end
  else
  if FIs3D then
  begin
    Dec(z,FZCenter);
    Dec(x,FXCenter);
    Dec(y,FYCenter);

    zz:=z*c2 - x*s2;

    if IHasPerspec then
       tmp:=IZoomFactor / ( 1+ IZoomPerspec * (zz*c1 + y*s1) )
    else
       tmp:=IZoomFactor;

    if IHasTilt then
    begin
      xx:=x*c2 + z*s2;
      yy:=y*c1 - zz*s1;

      x:=Round((xx*c3 - yy*s3)*tmp)+XCenterOffset;
      y:=Round((yy*c3 + xx*s3)*tmp)+YCenterOffset;
    end
    else
    begin
      x:=Round((x*c2 +  z*s2)*tmp)+XCenterOffset;
      y:=Round((y*c1 - zz*s1)*tmp)+YCenterOffset;
    end;
  end;
end;

{$IFNDEF FMX}
Function TTeeCanvas3D.GetHandle:TTeeCanvasHandle;
begin
  result:=FCanvas.Handle;
end;

function TTeeCanvas3D.CanvasStyle:TCanvasStyle;
begin
  Result:=csGDI;
end;
{$ENDIF}

procedure TTeeCanvas3D.Cone(Vertical:Boolean; Left,Top,Right,Bottom,
                          Z0,Z1:Integer; Dark3D:Boolean; ConePercent:Integer);
begin
  InternalCylinder(Vertical,Left,Top,Right,Bottom,Z0,Z1,Dark3D,ConePercent);
end;

procedure TTeeCanvas3D.Sphere(x,y,z:Integer; Const Radius:Double);
var tmp : Integer;
begin
  tmp:=Round(Radius);
  EllipseWithZ(x-tmp,y-tmp,x+tmp,y+tmp,z);
end;

procedure TTeeCanvas3D.Surface3D( Style:TTeeCanvasSurfaceStyle;
                            SameBrush,CreateCached,AddNormals:Boolean;
                            NumXValues,NumZValues:Integer;
                            const CalcPoints:TTeeCanvasCalcPoints;
                            var Cached:TTeeCachedObject);
begin
  // Not implemented in GDI mode. (See TeeOpenGL.pas TGLCanvas class)
end;

procedure TCanvas3D.TriSurface(NumTriangles:Integer; CreateCached,AddNormals:Boolean;
                              const Blend:TTeeBlend; Transparency:TTeeTransparency;
                              const CalcPoints:TTeeCanvasTriPoints;
                              var Cached:TTeeCachedObject);
var t         : Integer;
    tmpColors : TTriangleColors3D;
    tmpPoints : TTrianglePoints3D;
    P         : TTrianglePoints;
    tmpIndexes: TTriangleIndexes;
begin
  // Here we do not use or create Cached param. See OpenGL.

  for t:=1 to NumTriangles do
  begin
    CalcPoints(t,tmpPoints,tmpColors,tmpIndexes);

    if Assigned(Blend) then
    begin
      TrianglePointsTo2D(tmpPoints,P);
      Blend.SetRectangle(RectFromTriangle(P));
    end;

    Triangle3D(tmpPoints,tmpColors);

    if Assigned(Blend) then
       Blend.DoBlend(Transparency);
  end;
end;

{$IFNDEF FMX}
procedure TTeeCanvas3D.SetFontQuality(var LogRec:TLogFont);

  function GetDefault:Byte;
  const
     CLEARTYPE_QUALITY = 5;
     //CLEARTYPE_NATURAL_QUALITY = 6;
  begin
    case FontQuality of
      fqDefault: result:=DEFAULT_QUALITY;
      fqBest   : result:=ANTIALIASED_QUALITY;
      fqNormal : result:=NONANTIALIASED_QUALITY;
    else
      result:=CLEARTYPE_QUALITY;
     end;
  end;

begin
  case FFont.FQuality of
     fqDefault   : LogRec.lfQuality:=GetDefault;
     fqBest,
     fqClearType : LogRec.lfQuality:=TeeFontAntiAlias;
     fqNormal    : LogRec.lfQuality:=NONANTIALIASED_QUALITY;
  end;
end;

function TTeeCanvas3D.GetNewFont(AHandle:TTeeCanvasHandle; ASize:Integer):HFont;
var LogRec : TLogFont;
    tmpH   : HFont;
begin
  {$IFDEF MSWINDOWS}
  tmpH:=FFont.Handle;

  GetObject(tmpH, SizeOf(LogRec), @LogRec);

  LogRec.lfHeight:= -MulDiv(ASize,FFont.PixelsPerInch,72);

  SetFontQuality(LogRec);

  // Extra weights: LogRec.lfWeight:=FW_THIN;

  result:=SelectObject(AHandle,CreateFontIndirect({$IFDEF LCL}@{$ENDIF}LogRec));
  {$ELSE}
  result:=FFont.Handle; // Linux
  {$ENDIF}
end;
{$ENDIF}

procedure TTeeCanvas3D.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);

  {$IFDEF FMX}
  function RoundRectPath(const R:TRect; x,y:Integer):FMXTPathData;
  begin
    result:=FMXTPathData.Create;
    result.AddRectangle(R,x,y,AllCorners);
  end;
  {$ENDIF}

{$IFDEF FMX}
var tmpP : FMXTPathData;
    tmpG : TGradient;
    t: Integer;
{$ENDIF}
begin
  {$IFDEF FMX}
  if Length(P)>0 then
  begin
    tmpP:=FMXTPathData.Create;

    tmpP.MoveTo(P[0]);
    for t := 1 to Length(P)-1 do
        tmpP.LineTo(P[t]);

    tmpP.ClosePath;
  end
  else
  if RoundSize<>0 then
     tmpP:=RoundRectPath(Rect,RoundSize,RoundSize)
  else
  if Ellipse then
  begin
    if (DonutPercent>0) or ((EndAngle-StartAngle)<>360) then
       tmpP:=DonutPath(Rect,StartAngle,EndAngle,DonutPercent,0)
    else
    begin
      tmpP:=FMXTPathData.Create;
      with Rect do
           tmpP.AddEllipse(RectF(Left,Top,Right,Bottom));
    end;
  end
  else
    // Plain Rectangle, set some corner rounding for smooth effect:
    tmpP:=RoundRectPath(Rect,4,4);

  try
    FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};

    tmpG:=FCanvas.Fill.Gradient;
    tmpG.Points.Clear;

    with TGradientPoint(tmpG.Points.Add) do
    begin
      Color:=TAlphaColors.Null;
      Offset:=0;
    end;

    with TGradientPoint(tmpG.Points.Add) do
    begin
      Color:=Shadow.Color;
      Offset:=0.1;
    end;

    with TGradientPoint(tmpG.Points.Add) do
    begin
      Color:=Shadow.Color;
      Offset:=1;
    end;

    FCanvas.FillPath(tmpP,Opacity);
    FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  finally
    tmpP.Free;
  end;

  {$ELSE}

  {$IFNDEF IOS}
  {$IFNDEF ANDROID}
  if Shadow.Smooth and (not Metafiling) {$IFNDEF FMX}and Assigned(FBitmap){$ENDIF} then
     Shadow.InternalDrawSmooth(Self,
                               Rect,
                               Ellipse,RoundSize,P,DonutPercent,StartAngle,EndAngle)
  else
  {$ENDIF}
  {$ENDIF}
     inherited;

  {$ENDIF}
end;

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

  if AllowHtml then
     HtmlTextOut(Self,x,y,Text)
  else
     {$IFDEF FMX}
     TextOut(X,Y,Text);
     {$ELSE}
     DirectTextOut(Handle,X,Y,Text);
     {$ENDIF}
end;

Procedure TTeeCanvas3D.RectangleWithZ(Const Rect:TRect; Z:Integer);
begin
  // Possible improvement:
  {
    if FIsOrthogonal then
       Rectangle(CalcRect3D(Rect,Z))
    else
       ...
  }

  With Rect do
  begin
    Calc3DPoint(IPoints[0],Left,Top,Z);
    Calc3DPoint(IPoints[1],Right,Top,Z);
    Calc3DPoint(IPoints[2],Right,Bottom,Z);
    Calc3DPoint(IPoints[3],Left,Bottom,Z);
  end;

  PolygonFour;
end;

{$IFNDEF FMX}
Procedure SetCanvasRegion(DC:TTeeCanvasHandle; Region:HRgn; Mode:Integer=RGN_AND);
var OldRegion : HRGN;
    tmp       : Integer;
begin
  if Region<>0 then
  begin
    {$IFDEF MSWINDOWS}
    OldRegion:=CreateRectRgn(0,0,0,0);

    if GetClipRgn(DC,OldRegion)=1 then
    begin
      tmp:=Length(ClipHistory);
      SetLength(ClipHistory,tmp+1);
      ClipHistory[tmp]:=OldRegion;
    end
    else
      DeleteObject(OldRegion);

    ExtSelectClipRgn(DC,Region,Mode);
    DeleteObject(Region);
    {$ENDIF}
  end;
end;
{$ENDIF}

{$IFDEF FMX}
// threadvar
var ICanvasState : TCanvasSaveState=nil;
{$ENDIF}

Procedure ClipCanvas(const ACanvas:TCanvas; Const Rect:TRect);
{$IFDEF FMX}
begin
  {$IFNDEF D17}
  if ACanvas.ClassName='TCanvasD2D' then
     Exit;
  {$ENDIF}

  ICanvasState:=ACanvas.SaveState;
  ACanvas.IntersectClipRect(Rect);
end;
{$ELSE}
var tmpDC  : TTeeCanvasHandle;
    P      : Array[0..1] of TPoint;
    Region : HRgn;
begin
  {$IFDEF MSWINDOWS}
  with Rect do
  begin
    p[0]:=TopLeft;
    p[1]:=BottomRight;
  end;

  tmpDC:=ACanvas.Handle;
  LPToDP(tmpDC,P,2);
  Region:=CreateRectRgn(P[0].X,P[0].Y,P[1].X,P[1].Y);
  SetCanvasRegion(tmpDC,Region);
  {$ENDIF}
end;
{$ENDIF}

procedure TTeeCanvas3D.ClipRectangle(Const Rect:TRect);
begin
  ClipCanvas(FCanvas,Rect);
end;

{$IFNDEF FMX}
// Printer Bug workaround:  "var" instead of "const" Points array parameter
function InternalCreateRegion(tmpDC:TTeeCanvasHandle; var Points:Array of TPoint;
                              NumPoints:Integer):HRgn;
begin
  {$IFDEF MSWINDOWS}
  LPToDP(tmpDC,Points,NumPoints);
  result:=CreatePolygonRgn(Points,NumPoints,ALTERNATE);
  {$ELSE}
  result:=0; // Linux
  {$ENDIF}
end;
{$ENDIF}

Procedure TTeeCanvas.ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                                 DiffRegion:Boolean=False);
{$IFDEF FMX}
begin
  ClipRectangle(PolygonBounds(Points)); // No TPath clipping supported in Firemonkey
end;
{$ELSE}

  function PointArrayOf(const Points:Array of TPoint):TPointArray;
  var t : Integer;
  begin
    SetLength(result,NumPoints);

    for t:=0 to NumPoints-1 do
        result[t]:=Points[t];
  end;

var Region : HRgn;
    tmpMap : Integer;
    tmpPoints : TPointArray;
    tmpDC     : TTeeCanvasHandle;
begin
  {$IFNDEF D8}
  tmpPoints:=nil;
  {$ENDIF}

  tmpDC:=Handle;

  {$IFDEF MSWINDOWS}
  tmpMap:=GetMapMode(tmpDC);

  if tmpMap<>MM_TEXT then
  begin
    tmpPoints:=PointArrayOf(Points);
    try
      Region:=InternalCreateRegion(tmpDC,tmpPoints,NumPoints);
    finally
      tmpPoints:=nil;
    end;
  end
  else
    Region:=CreatePolygonRgn(Points,NumPoints,ALTERNATE);
  {$ELSE}
  Region:=0; // Linux
  {$ENDIF}

  if DiffRegion then
     SetCanvasRegion(tmpDC,Region,RGN_DIFF)
  else
     SetCanvasRegion(tmpDC,Region);
end;
{$ENDIF}

Procedure ClipEllipse(const ACanvas:TTeeCanvas; Const Rect:TRect); {$IFDEF D10}inline;{$ENDIF}
begin
  ACanvas.ClipEllipse(Rect);
end;

Procedure TTeeCanvas.ClipEllipse(Const Rect:TRect; Inverted:Boolean=False);
{$IFDEF FMX}
begin
  ClipRectangle(Rect); // No Ellipse/Path clipping support in Firemonkey
end;
{$ELSE}

var DC : TTeeCanvasHandle;
    R  : TRect;
begin
  {$IFDEF USEWINDOWS}
  DC:=Handle;
  R:=Rect;

  LPToDP(DC,R,2);

  if Inverted then
     SetCanvasRegion(DC,CreateEllipticRgnIndirect(Rect),RGN_XOR)
  else
     SetCanvasRegion(DC,CreateEllipticRgnIndirect(R));
  {$ELSE}
  FCanvas.ClipRect:=R;
  {$ENDIF}
end;
{$ENDIF}

// Obsolete
Procedure ClipRoundRectangle(const ACanvas:TTeeCanvas; Const Rect:TRect; RoundSize:Integer); {$IFDEF D10}inline;{$ENDIF}
begin
  ACanvas.ClipRectangle(Rect,RoundSize,RoundSize);
end;

Procedure TTeeCanvas.ClipRectangle(Const Rect:TRect; RoundX,RoundY:Integer);
{$IFDEF FMX}
begin
  ClipRectangle(Rect);
end;
{$ELSE}

var R  : TRect;
    DC : HDC;
begin
  {$IFDEF USEWINDOWS}
  DC:=Handle;
  R:=Rect;

  LPToDP(DC,R,2);

  with R do
    SetCanvasRegion(DC,CreateRoundRectRgn(Left,Top,Right,Bottom,RoundX,RoundY));
  {$ELSE}
  FCanvas.ClipRect:=R;
  {$ENDIF}
end;
{$ENDIF}

// Obsolete
Procedure ClipPolygon(const ACanvas:TTeeCanvas; const Points:Array of TPoint; NumPoints:Integer);
begin
  ACanvas.ClipPolygon(Points,NumPoints);
end;

Function RectFromPolygon(Const Points:Array of TPoint; NumPoints:Integer):TRect;
var t : Integer;
begin
  result.TopLeft:=Points[0];
  result.BottomRight:=result.TopLeft;

  for t:=1 to NumPoints-1 do
  With Points[t] do
  begin
    if X<result.Left then result.Left:=X else
       if X>result.Right then result.Right:=X;

    if Y<result.Top then result.Top:=Y else
       if Y>result.Bottom then result.Bottom:=Y;
  end;

  result.Right:=result.Right+1;
  result.Bottom:=result.Bottom+1;
end;

Function RectFromTriangle(Const Points:TTrianglePoints):TRect;
begin
  result:=RectFromPolygon(Points,3);
end;

function RectangleInRectangle(const Small,Big:TRect):Boolean;
begin
  result:=PointInRect(Big,Small.TopLeft) and PointInRect(Big,Small.BottomRight);
end;

// Speed-optimized Hypotenuse (RTL Math.Hypot is precision-optimized, but 80% slower)
function TeeHypot(const X,Y:Integer):Single;
begin
  result:=Sqrt(Sqr(X)+Sqr(Y));
end;

function TeeHypot(const X,Y:Single):Single;
begin
  result:=Sqrt(Sqr(X)+Sqr(Y));
end;

// Returns the boundary points of the convex hull of a set of 2D xy points.
// Overwrites the input array.
// Written by Peter Bone : peterbone@hotmail.com
class function TTeeCanvas.ConvexHull(var Points : TPointArray) : Boolean;

  // sort an array of points by angle
  procedure QuickSortAngle(var A: TPointArray; var Angles : Array of Single; iLo, iHi: Integer);
  var
    Lo, Hi : Integer;
    Mid : Single;
    TempPoint : TPoint;
    TempAngle : Single;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := Angles[(Lo + Hi) div 2];
    repeat
      while Angles[Lo] < Mid do Inc(Lo);
      while Angles[Hi] > Mid do Dec(Hi);

      if Lo <= Hi then
      begin
        // swap points
        TempPoint := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := TempPoint;

        // swap angles
        TempAngle := Angles[Lo];
        Angles[Lo] := Angles[Hi];
        Angles[Hi] := TempAngle;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;

    // perform quicksorts on subsections
    if Hi > iLo then QuickSortAngle(A, Angles, iLo, Hi);
    if Lo < iHi then QuickSortAngle(A, Angles, Lo, iHi);
  end;

var
  LAngles : Array of Single;
  Lindex,
  tmpHigh,
  LPivotIndex : Integer;

  LMinY,
  LMaxX : TCoordinate;

  LPivot : TPoint;
  LBehind, LInfront : TPoint;
  LRightTurn : Boolean;
  LVecPoint : {$IFDEF FMX}TPoint{$ELSE}{$IFDEF LINUX}TPoint{$ELSE}TPointFloat{$ENDIF}{$ENDIF};
  tmp : Single;
begin
  Result:=True;

  if Length(Points) = 3 then Exit // already a convex hull
  else
  if Length(Points) < 3 then
  begin // not enough points
    Result := False;
    Exit;
  end;

  // find pivot point, which is known to be on the hull
  // point with lowest y - if there are multiple, point with highest x
  LMinY := 10000000;
  LMaxX := 10000000;
  LPivotIndex := 0;

  for Lindex := Low(Points) to High(Points) do
    if Points[Lindex].Y = LMinY then
    begin
      if Points[Lindex].X > LMaxX then
      begin
        LMaxX := Points[Lindex].X;
        LPivotIndex := Lindex;
      end;
    end
    else
    if Points[Lindex].Y < LMinY then
    begin
      LMinY := Points[Lindex].Y;
      LMaxX := Points[Lindex].X;
      LPivotIndex := Lindex;
    end;

  // put pivot into seperate variable and remove from array
  LPivot := Points[LPivotIndex];
  Points[LPivotIndex] := Points[High(Points)];
  SetLength(Points, High(Points));

  // calculate angle to pivot for each point in the array
  // quicker to calculate dot product of point with a horizontal comparison vector
  SetLength(LAngles, Length(Points));
  for Lindex := Low(Points) to High(Points) do
  begin
    LVecPoint.X := LPivot.X - Points[Lindex].X; // point vector
    LVecPoint.Y := LPivot.Y - Points[Lindex].Y;

    // reduce to a unit-vector - length 1
    tmp:=TeeHypot(LVecPoint.X, LVecPoint.Y);

    if tmp=0 then LAngles[Lindex]:=0
             else LAngles[Lindex] := LVecPoint.X / tmp;
  end;

  // sort the points by angle
  QuickSortAngle(Points, LAngles, Low(Points), High(Points));

  // step through array to remove points that are not part of the convex hull
  Lindex := 1;
  Repeat

    // assign points behind and infront of current point
    if Lindex = 0 then LRightTurn := True
    else
    begin
      LBehind := Points[Lindex-1];
      if Lindex = High(Points) then LInfront := LPivot
                               else LInfront := Points[Lindex + 1];

      // work out if we are making a right or left turn using vector product
      LRightTurn:= ((LBehind.X-Points[Lindex].X)*(LInfront.Y-Points[Lindex].Y))-
                   ((LInfront.X-Points[Lindex].X)*(LBehind.Y-Points[Lindex].Y)) < 0;
    end;

    if LRightTurn then Inc(Lindex) // go to next point
    else
    begin // point is not part of the hull

      tmpHigh:=High(Points);

      // remove point from convex hull
      if Lindex = tmpHigh then
         SetLength(Points, tmpHigh)
      else
      begin
        System.Move(Points[Lindex + 1], Points[Lindex], (tmpHigh - Lindex) * SizeOf(TPoint) );
        SetLength(Points, tmpHigh);
      end;

      Dec(Lindex); // backtrack to previous point
    end;
  until Lindex = High(Points);

  // add pivot back into points array
  SetLength(Points, Length(Points) + 1);
  Points[High(Points)] := LPivot;
end;

procedure TTeeCanvas3D.ClipCube(Const Rect:TRect; MinZ,MaxZ:Integer);
var tmpR : TRect;
    P    : TPointArray;
begin
  {$IFNDEF D7}
  P:=nil; // <-- Fix: Hint
  {$ENDIF}

  if FIs3D then
  begin
    P:=CubeHull(Rect,MinZ,MaxZ);
    try
      ClipPolygon(P,Length(P));
    finally
      P:=nil;
    end;
  end
  else
  begin
    tmpR:=Rect;
    tmpR.Left:=tmpR.Left+1;
    tmpR.Top:=tmpR.Top+1;
    tmpR.Bottom:=tmpR.Bottom-1;
    ClipRectangle(tmpR);
  end;
end;

Procedure UnClipCanvas(const ACanvas:TCanvas);
{$IFDEF FMX}
begin
  {$IFNDEF D17}
  if ACanvas.ClassName='TCanvasD2D' then
     Exit;
  {$ENDIF}

  // Protection against restoring the same state more than once.
  // Mainly for OSX to avoid an AV, but valid for any Canvas class.
  if Assigned(ICanvasState) then
  begin
    ACanvas.RestoreState(ICanvasState);
    ICanvasState:=nil;
  end;
end;
{$ELSE}
var tmp : Integer;
begin
  tmp:=High(ClipHistory);

  if tmp>-1 then
  begin
    {$IFDEF MSWINDOWS}
    SelectClipRgn(ACanvas.Handle,ClipHistory[tmp]);
    DeleteObject(ClipHistory[tmp]);
    {$ENDIF}

    SetLength(ClipHistory,tmp);
  end
  else
    {$IFDEF MSWINDOWS}
    SelectClipRgn(ACanvas.Handle,0);
    {$ENDIF}
end;
{$ENDIF}

procedure TTeeCanvas3D.UnClipRectangle;
begin
  UnClipCanvas(FCanvas);
end;

Const PerspecFactor=1.0/120.0;

Procedure TTeeCanvas3D.Projection(MaxDepth:Integer; const ABounds,Rect:TRect);
begin
  RectCenter(Rect,FXCenter,FYCenter);

  Inc(FXCenter,Round(RotationCenter.X));
  Inc(FYCenter,Round(RotationCenter.Y));

  FZCenter:=Round( (MaxDepth*0.5) + RotationCenter.Z );

  XCenterOffset:=FXCenter;
  YCenterOffset:=FYCenter;

  if Assigned(F3DOptions) then
  With F3DOptions do
  begin
    Inc(XCenterOffset,HorizOffset);
    Inc(YCenterOffset,VertOffset);

    if FIs3D then
       IZoomFactor:=0.01*F3DOptions.ZoomFloat;

    CalcPerspective(Rect);
  end;
end;

Procedure TTeeCanvas3D.CalcPerspective(const Rect:TRect);
var tmp : TCoordinate;
begin
  IHasPerspec:=F3DOptions.Perspective>0;

  if IHasPerspec then
  begin
    tmp:=Rect.Right-Rect.Left;
    if tmp<1 then tmp:=1;

    IZoomPerspec:=IZoomFactor*F3DOptions.Perspective*PerspecFactor/tmp;
  end;
end;

Procedure TTeeCanvas3D.CalcTrigValues;
const
  MaxLimit : Double = 1E-5;

Var rx : Double;
    ry : Double;
    rz : Double;
begin
  if not FIsOrthogonal then
  begin
    if Assigned(F3DOptions) then
    With F3DOptions do
    begin
      rx:=-FElevation;
      ry:=-FRotation;
      rz:=Tilt;
    end
    else
    begin
      rx:=0;
      ry:=0;
      rz:=0;
    end;

    IHasPerspec:=False;
    IZoomPerspec:=0;

    SinCos(rx*TeePiStep,s1,c1);
    SinCos(ry*TeePiStep,s2,c2);

    IHasTilt:=rz<>0;

    SinCos(rz*TeePiStep,s3,c3);

    c2s3:=c2*s3;
    c2c3:=Math.Max(MaxLimit,c2*c3);

    tempXX:=Math.Max(MaxLimit, s1*s2*s3 + c1*c3 );
    tempYX:=( c3*s1*s2 - c1*s3 );

    tempXZ:=( c1*s2*s3 - c3*s1 );
    tempYZ:=( c1*c3*s2 + s1*s3 );
  end;
end;

{$IFDEF FMX}
{$IFDEF D18}
{$DEFINE FMXSCALE}
{$ENDIF}
{$ENDIF}

Function TTeeCanvas3D.InitWindow( const DestCanvas:TCanvas;
                                  const A3DOptions:TView3DOptions;
                                  ABackColor:TColor;
                                  Is3D:Boolean;
                                  Const UserRect:TRect):TRect;
var tmpH      : Integer;
    tmpW      : Integer;

    tmpHScaled: Integer;
    tmpWScaled: Integer;

    tmpCanvas : TCanvas;

    tmpSin    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpAngle  : Double;
begin
  FBounds:=UserRect;
  F3DOptions:=A3DOptions;

  if Assigned(F3DOptions) then
  begin
    FontZoom:=F3DOptions.FontZoom;
    IZoomText:=F3DOptions.ZoomText<>ztNo;
  end;

  FIs3D:=Is3D;
  FIsOrthogonal:=False;
  IZoomFactor:=1;

  if FIs3D then
  begin
    if Assigned(F3DOptions) then
    begin
      FIsOrthogonal:=F3DOptions.Orthogonal;

      if FIsOrthogonal then
      begin
        tmpAngle:=F3DOptions.OrthoAngle;

        if tmpAngle>90 then
        begin
          IOrthoX:=-1;
          tmpAngle:=180-tmpAngle;
        end
        else IOrthoX:=1;

        SinCos(tmpAngle*TeePiStep,tmpSin,tmpCos);

        if tmpCos<0.01 then IOrthoY:=1
                       else IOrthoY:=tmpSin/tmpCos;
      end;
    end;

    CalcTrigValues;
  end;

  // VCL: Extended test for non-capable Windows devices:
  (*
  {$IFNDEF FMX}
  if UseBuffer then
  begin
    if GetDeviceCaps(FCanvas.Handle,RASTERCAPS) and RC_BITBLT <> 1 then
       UseBuffer:=False;
  end;
  {$ENDIF}
  *)

  if UseBuffer then
  begin
    RectSize(UserRect,tmpW,tmpH);

    {$IFDEF FMXSCALE}
    tmpWScaled:=Round(tmpW*DestCanvas.Scale);
    tmpHScaled:=Round(tmpH*DestCanvas.Scale);
    {$ELSE}
    tmpWScaled:=tmpW;
    tmpHScaled:=tmpH;
    {$ENDIF}

    if not Assigned(FBitmap) then
    begin
      FBitmap:=TBitmap.Create{$IFDEF FMX}(tmpWScaled,tmpHScaled){$ENDIF};

      {$IFDEF FMX}

      {$IFDEF D20}
      FBitmap.BitmapScale:=DestCanvas.Scale;
      {$ENDIF}

      {$ELSE}
      {$IFDEF IGNOREPALETTE}
      FBitmap.IgnorePalette:=True;
      {$ENDIF}

      {$ENDIF}
    end;

    // Just in case panel size has been changed (ie: resized or changed by code)
    // check if FBitmap buffer needs to be resized as well:
    TeeSetBitmapSize(FBitmap,tmpWScaled,tmpHScaled);

    // FMX Alternative:
    // tmpCanvas:=TCanvasManager.CreateFromBitmap(FBitmap,TCanvasQuality.HighQuality)
    
    tmpCanvas:=FBitmap.Canvas;

    {$IFNDEF FMX}
    tmpCanvas.OnChange:=nil;
    tmpCanvas.OnChanging:=nil;
    {$ENDIF}

    SetCanvas(tmpCanvas);

    {$IFDEF FMX}
    tmpCanvas.BeginScene;

    if ColorAlpha(ABackColor)=0 then
       FBitmap.Clear(ABackColor);
    {$ENDIF}

    result:=TeeRect(tmpW,tmpH);
  end
  else
  begin
    SetCanvas(DestCanvas);
    result:=UserRect;
  end;

  // After calling SetCanvas, initialize BackMode:
  BackMode:=cbmOpaque;
end;

Procedure TTeeCanvas3D.TransferBitmap(ALeft,ATop:Integer; const ACanvas:TCanvas);
{$IFNDEF FMX}
{$IFOPT C+}
var tmpRes : LongBool;
{$ENDIF}
{$ENDIF}
begin
//  Assert(FBitmap.PixelFormat=pfDevice,'Buffer Bitmap PixelFormat <> pfDevice');

  {$IFDEF LCL}
  ACanvas.Draw(ALeft, ATop, FBitmap);
  {$ELSE}

  {$IFDEF FMX}
  ACanvas.DrawBitmap(FBitmap,RectF(0,0,FBitmap.Width,FBitmap.Height),

     RectF(ALeft,ATop,
           ALeft+{$IFDEF FMXSCALE}Round(FBitmap.Width/ACanvas.Scale){$ELSE}FBitmap.Width{$ENDIF},
           ATop +{$IFDEF FMXSCALE}Round(FBitmap.Height/ACanvas.Scale){$ELSE}FBitmap.Height{$ENDIF}),

     1 {AbsoluteOpacity},
     True);

  {$ELSE}

  {$IFNDEF TEEOCX}

  // Dont paint anything if View3DOptions is nil.
  // This can happen, for example, if setting Chart1.AutoRepaint:=False before
  // docking the chart parent form inside another form:
  // https://quality.embarcadero.com/browse/RSP-11239
  // REGRESSION, Remove check, otherwise non-parented charts cannot be used,
  // like in:  Chart1.Draw(Image1.Canvas,R)
  //  if Assigned(View3DOptions) and (View3DOptions.Parent.Parent<>nil) then

  {$ENDIF}
  {$IFOPT C+}
  begin
    tmpRes:=
  {$ENDIF}
    BitBlt( ACanvas.Handle,ALeft,ATop,
            FBitmap.Width,
            FBitmap.Height,
            FBitmap.Canvas.Handle,0,0,SRCCOPY);

  {$IFOPT C+}
    {$IFNDEF TEEOCX}
    Assert(tmpRes,'Error '+IntToStr(GetLastError)+' transferring internal Bitmap using BitBlt');
    {$ENDIF}
  end;
  {$ENDIF}

  {$ENDIF}
  {$ENDIF}
end;

Function TTeeCanvas3D.ReDrawBitmap:Boolean;
begin
  result:=not FDirty;

  if result then
     TransferBitmap(0,0,FCanvas);
end;

function TTeeCanvas3D.RenderToBitmap(var ABitmap:TBitmap; const R:TRect):Boolean;
begin
  // Reuse internal double-buffer Bitmap instead of creating a new one:
  result:=(not FDirty) and  // <-- just in case the chart needs to be repainted
          UseBuffer and Assigned(Bitmap) and
          EqualRect(R,TeeRect(Bitmap.Width,Bitmap.Height));

  if result then
     ABitmap.Assign(Bitmap);
end;

Procedure TTeeCanvas3D.ShowImage(const DestCanvas,DefaultCanvas:TCanvas; Const UserRect:TRect);
begin
  if UseBuffer and Assigned(FBitmap) then
  begin
    {$IFDEF FMX}
    FCanvas.EndScene;

    if Assigned(DestCanvas) then
    begin
      // BeginScene needed for "Effects" paint stage:
      if DestCanvas.BeginScene then
      try
        with UserRect.TopLeft.Round do
             TransferBitmap(X,Y,DestCanvas);
      finally
        DestCanvas.EndScene;
      end;
    end;

    {$ELSE}
    TransferBitmap(UserRect.Left,UserRect.Top,DestCanvas);
    {$ENDIF}

    FDirty:=False;
  end;

  SetCanvas(DefaultCanvas);
end;

{$IFNDEF FMX}
function TTeeCanvas3D.TranspBitmap(const AGraphic:TGraphic; const R:TRect):TBitmap;
var tmpW, tmpH : Integer;
begin
  result:=TBitmap.Create;

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

  TeeSetBitmapSize(result,tmpW,tmpH);
  result.PixelFormat:=pf32Bit;

  // For Transparent images (ie: PNG with alpha pixels),
  // we should copy the source rectangle from the internal Bitmap
  // (only when BufferedDisplay=True)
  if Assigned(Bitmap) then
     {$IFDEF MSWINDOWS}
     BitBlt(result.Canvas.Handle,0,0,tmpW,tmpH,Bitmap.Canvas.Handle,R.Left,R.Top,SRCCOPY);
     {$ELSE}
     //Bitmap.Canvas.StretchDraw();
     {$ENDIF}

  result.Canvas.StretchDraw(TeeRect(tmpW,tmpH),AGraphic);
end;

function TTeeCanvas3D.TranspBitmap(const AGraphic:TGraphic; const X,Y:Integer):TBitmap;
begin
  result:=TranspBitmap(AGraphic,TeeRect(X,Y,X+AGraphic.Width,Y+AGraphic.Height));
end;
{$ENDIF}

procedure TTeeCanvas3D.StretchDraw(const ARect: TRect; const Graphic: TGraphic);
{$IFNDEF FMX}
var tmpDest : TBitmap;
    tmp     : TBitmap;
    tmpB    : TBitmap;
    tmpIsBitmap : Boolean;
{$ENDIF}
begin
  {$IFDEF FMX}
  FCanvas.DrawBitmap(Graphic,TeeRect(Graphic.Width,Graphic.Height),
                     ARect,Opacity,FStretchQuality=sqLow);
  {$ELSE}
  if Assigned(Graphic) then
  begin
    if FStretchQuality=sqHigh then
    begin
      tmpIsBitmap:=Graphic is TBitmap;

      if tmpIsBitmap then
         tmp:=TBitmap(Graphic)
      else
      begin
        if Graphic.Transparent then
        begin
          tmpB:=TranspBitmap(Graphic,ARect);
          FCanvas.Draw(ARect.Left,ARect.Top,tmpB);
          tmpB.Free;
          Exit;
        end;

        tmp:=TBitmap.Create;

        {$IFNDEF LCL}
        if Graphic is TMetafile then
        begin
          tmp.Width:=Graphic.Width;
          tmp.Height:=Graphic.Height;
          tmp.Canvas.StretchDraw(TeeRect(tmp.Width,tmp.Height),Graphic);
        end
        else
        {$ENDIF}
           tmp.Assign(Graphic);
      end;

      try
        tmpDest:=TBitmap.Create;
        try
          TeeSetBitmapSize(tmpDest,Abs(ARect.Right-ARect.Left),Abs(ARect.Bottom-ARect.Top));
          SmoothStretch(tmp,tmpDest);
          FCanvas.Draw(ARect.Left,ARect.Top,tmpDest);
        finally
          tmpDest.Free;
        end;
      finally
        if not tmpIsBitmap then
           tmp.Free;
      end;
    end
    else
    if Graphic.Transparent then
    begin
      tmpB:=TranspBitmap(Graphic,ARect);
      FCanvas.StretchDraw(ARect,tmpB);
      tmpB.Free;
    end
    else
       FCanvas.StretchDraw(ARect,Graphic);
  end;
  {$ENDIF}
end;

procedure TTeeCanvas3D.Draw(X, Y: Integer; const Graphic: TGraphic);
{$IFNDEF FMX}
var tmpB : TBitmap;
{$ENDIF}
begin
  {$IFDEF FMX}
  FCanvas.DrawBitmap(Graphic,
     TeeRect(Graphic.Width,Graphic.Height),
     TeeRect(X,Y,X+Graphic.Width,Y+Graphic.Height),
     Opacity,
     FStretchQuality=sqLow);

  {$ELSE}
  if Assigned(Graphic) and (not Graphic.Empty) then
    if Graphic.Transparent then
    begin
      tmpB:=TranspBitmap(Graphic,X,Y);
      FCanvas.Draw(X,Y,tmpB);
      tmpB.Free;
    end
    else
      FCanvas.Draw(X,Y,Graphic);
  {$ENDIF}
end;

Function RGBA(const RGBA:TRGBA):TColor;
begin
  {$IFDEF FMX}
  TAlphaColorRec(result).A:=RGBA.Alpha;
  TAlphaColorRec(result).R:=RGBA.Red;
  TAlphaColorRec(result).G:=RGBA.Green;
  TAlphaColorRec(result).B:=RGBA.Blue;
  {$ELSE}
  result := (RGBA.Red or (RGBA.Green shl 8) or (RGBA.Blue shl 16) or (RGBA.Alpha shl 24));
  {$ENDIF}
end;

Function RGBA(const r,g,b,a:Integer):TColor;
begin
  {$IFDEF FMX}
  TAlphaColorRec(result).A:=a;
  TAlphaColorRec(result).R:=r;
  TAlphaColorRec(result).G:=g;
  TAlphaColorRec(result).B:=b;
  {$ELSE}
  result := (r or (g shl 8) or (b shl 16) or (a shl 24));
  {$ENDIF}
end;

{$IFDEF FMX}
function RGB(const R,G,B:Byte):TColor;
begin
  TAlphaColorRec(result).R:=R;
  TAlphaColorRec(result).G:=G;
  TAlphaColorRec(result).B:=B;
  TAlphaColorRec(result).A:=$FF;
end;

function GetRValue(const C:TColor):Byte;
begin
  result:=TAlphaColorRec(C).R;
end;

function GetGValue(const C:TColor):Byte;
begin
  result:=TAlphaColorRec(C).G;
end;

function GetBValue(const C:TColor):Byte;
begin
  result:=TAlphaColorRec(C).B;
end;
{$ENDIF}

{$IFNDEF FMX}
{$IFNDEF USEWINDOWS}
function GetRValue(const Color:Integer):Byte;
begin
  Result := Byte(Color);
end;

function GetGValue(const Color:Integer):Byte;
begin
  Result := Byte(Color shr 8);
end;

function GetBValue(const Color:Integer):Byte;
begin
  Result := Byte(Color shr 16);
end;

function RGB(const r,g,b:Integer):TColor; inline;
begin
  result:=RGBToColor(r,g,b);
end;
{$ENDIF}
{$ENDIF}

// For gradient balance
Function TeeSigmoid(const Index,Balance,Total:Double):Double;
const Divisor:Double=1/(200/2);
begin
  result:=Exp((0.5+(Balance*Divisor))*Ln((1+Index)/(1+Total)));
end;

Procedure TTeeCanvas3D.GradientFill( Const Rect : TRect;
                                     StartColor : TColor;
                                     EndColor   : TColor;
                                     Direction  : TGradientDirection;
                                     Balance    : Integer=50;
                                     RadialX    : Integer=0;
                                     RadialY    : Integer=0);
{$IFDEF FMX}
var G : TGradient;
begin
  G:=FCanvas.Fill.Gradient;

  G.Color:=StartColor;
  G.Color1:=EndColor;

  if Direction=gdRadial then
  begin
    G.RadialTransform.Position.X:=RadialX;
    G.RadialTransform.Position.Y:=RadialY;
    G.Style:=TGradientStyle.{$IFDEF D20}Radial{$ELSE}gsRadial{$ENDIF};
  end
  else
    G.Style:=TGradientStyle.{$IFDEF D20}Linear{$ELSE}gsLinear{$ENDIF};

  FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};
  FCanvas.FillRect(Rect,0,0,AllCorners,1);
  FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
end;
{$ELSE}

Var T0,T1,T2 : Integer;
    D0,D1,D2 : Integer;
    Range    : Integer;
    FDC      : TTeeCanvasHandle;
    tmpBrush : HBRUSH;
    OldColor : TColor;

  Function CalcColor(Index:Integer):TColor;
  var tmpD : Double;
  begin
    if Balance=50 then
       result:=(( T0 + ((Index*D0) div Range) ) or
               (( T1 + ((Index*D1) div Range) ) shl 8) or
               (( T2 + ((Index*D2) div Range) ) shl 16))
    else
    begin
      tmpD:=TeeSigmoid(Index,Balance,Range);

      result:=(( T0 + Round(tmpD*D0) ) or
              (( T1 + Round(tmpD*D1) ) shl 8) or
              (( T2 + Round(tmpD*D2) ) shl 16));
    end;
  end;

var tmpFastBrush : Boolean;

  {$IFDEF MSWINDOWS}
  procedure CheckFastBrush;
  begin
    FDC:=FCanvas.Handle;

    tmpFastBrush:=Assigned(@TeeSetDCBrushColor);

    if tmpFastBrush then
       tmpBrush:=SelectObject(FDC,GetStockObject(DC_BRUSH));
  end;
  {$ENDIF}

  Procedure CalcBrushColor(Index:Integer);
  var tmp : TColor;
  begin
    tmp:=CalcColor(Index);

    if tmp<>OldColor then
    begin
      {$IFDEF MSWINDOWS}
      if tmpFastBrush then
         TeeSetDCBrushColor(FDC,tmp)
      else
      begin
        if tmpBrush<>0 then
           DeleteObject(SelectObject(FDC,tmpBrush)); // <-- Win API is very slow here !
        tmpBrush:=SelectObject(FDC,CreateSolidBrush(tmp));
      end;
      {$ELSE}
      FCanvas.Brush.Color:=tmp;
      {$ENDIF}

      OldColor:=tmp;
    end;
  end;

Var tmpRect : TRect;

  Procedure RectGradient(Horizontal:Boolean);
  var t,
      P1,
      P2,
      P3    : Integer;
      Size  : Integer;
      Steps : Integer;
  begin
    if Horizontal then
    begin
      P3:=tmpRect.Bottom-tmpRect.Top;
      Size:=tmpRect.Right-tmpRect.Left;
    end
    else
    begin
      P3:=tmpRect.Right-tmpRect.Left;
      Size:=tmpRect.Bottom-tmpRect.Top;
    end;

    Steps:=Size;
    if Steps>256 then Steps:=256;
    P1:=0;
    OldColor:=-1;

    Range:=Pred(Steps);

    if Range>0 then
    begin
      CheckFastBrush;

      for t:=0 to Steps-1 do
      Begin
        CalcBrushColor(t);

        P2:=(t+1)*Size div Steps;

        {$IFDEF MSWINDOWS}
        if Horizontal then PatBlt(FDC,tmpRect.Right-P1,tmpRect.Top,P1-P2,P3,PATCOPY)
                      else PatBlt(FDC,tmpRect.Left,tmpRect.Bottom-P1,P3,P1-P2,PATCOPY);
        {$ENDIF}

        P1:=P2;
      end;
    end;
  end;

  Procedure FromCorner;
  var FromTop : Boolean;
      tmpXEnd : Boolean;

      SizeX,
      SizeY,
      tmp1,
      tmp2,
      P0,
      P1      : Integer;
  begin
    FromTop:=Direction=gdFromTopLeft;

    with tmpRect do
    if FromTop then P1:=Top
               else P1:=Bottom;

    P0:=P1;
    RectSize(tmpRect,SizeX,SizeY);
    Range:=Max(1,SizeX+SizeY);
    tmp1:=0;
    tmp2:=0;

    CheckFastBrush;

    tmpXEnd:=False;

    Repeat
      CalcBrushColor(tmp1+tmp2);

      {$IFDEF MSWINDOWS}
      if not tmpXEnd then
         PatBlt(FDC,tmpRect.Left+tmp2-1,P0,1,P1-P0,PATCOPY);
      {$ENDIF}

      if tmp1<SizeY then
      begin
        Inc(tmp1);

        if FromTop then
        begin
          {$IFDEF MSWINDOWS}
          PatBlt(FDC,tmpRect.Left,P0,tmp2,1,PATCOPY);
          {$ENDIF}

          if P0<tmpRect.Bottom then Inc(P0)
        end
        else
        begin
          {$IFDEF MSWINDOWS}
          PatBlt(FDC,tmpRect.Left,P0-1,tmp2,1,PATCOPY);
          {$ENDIF}

          if P0>tmpRect.Top then Dec(P0);
        end;
      end;

      if tmp2<SizeX then
         Inc(tmp2)
      else
         tmpXEnd:=True;

    Until (tmp1>=SizeY) and (tmp2>=SizeX);
  end;

  Procedure FromCenter;
  Const TeeGradientPrecision : Integer = 1;   { how many pixels precision, ( 1=best) }
  var tmpXCenter,
      tmpYCenter,
      SizeX,
      SizeY,
      P0,
      P1,
      tmpLeft,
      tmpTop,
      tmp1,
      tmp2        : Integer;
  begin
    RectSize(tmpRect,SizeX,SizeY);
    tmpXCenter:=SizeX shr 1;
    tmpYCenter:=SizeY shr 1;
    tmp1:=0;
    tmp2:=0;
    Range:=Max(1,tmpXCenter+tmpYCenter);

    CheckFastBrush;

    Repeat
      CalcBrushColor(tmp1+tmp2);

      P0:=SizeY-(2*tmp1);
      P1:=SizeX-(2*tmp2);

      tmpLeft:=tmpRect.Left+tmp2;
      tmpTop:=tmpRect.Top+tmp1;

      {$IFDEF MSWINDOWS}
      PatBlt(FDC,tmpLeft,tmpTop,TeeGradientPrecision,P0,PATCOPY);
      PatBlt(FDC,tmpRect.Right-tmp2-1,tmpTop,TeeGradientPrecision,P0,PATCOPY);
      PatBlt(FDC,tmpLeft,tmpTop,P1,TeeGradientPrecision,PATCOPY);
      PatBlt(FDC,tmpLeft,tmpRect.Bottom-tmp1-TeeGradientPrecision,
                P1,TeeGradientPrecision,PATCOPY);
      {$ENDIF}

      if tmp1<tmpYCenter then Inc(tmp1,TeeGradientPrecision);
      if tmp2<tmpXCenter then Inc(tmp2,TeeGradientPrecision);

    Until (tmp1>=tmpYCenter) and (tmp2>=tmpXCenter);
  end;

  Procedure DoDrawRadial;
  var tmp : TCustomTeeGradient;
  begin
    tmp:=TCustomTeeGradient.Create(nil);
    try
      tmp.FDirection:=gdRadial;

      tmp.StartColor:=StartColor;
      tmp.EndColor:=EndColor;

      tmp.FBalance:=Balance;
      tmp.FRadialX:=RadialX;
      tmp.FRadialY:=RadialY;

      tmp.DrawRadial(Self,tmpRect);
    finally
      tmp.Free;
    end;
  end;

  procedure DiagonalGradient(Up:Boolean);
  var tmpFrom,
      tmpTo    : TPoint;
      tmpSize  : Integer;
      Steps    : Integer;
      SizeX,
      SizeY    : Integer;
      t        : Integer;
      tmpPen   : TPen;
  begin
    if Up then
       tmpFrom:=TeePoint(tmpRect.Left,tmpRect.Bottom)
    else
    begin
      tmpRect.Top:=tmpRect.Top-1;
      tmpRect.Bottom:=tmpRect.Bottom-1;
      tmpFrom:=tmpRect.TopLeft;
    end;

    tmpTo:=tmpFrom;
    RectSize(tmpRect,SizeX,SizeY);

    // Speed protection against huge Gradient sizes (it'll take many seconds)
    if SizeX*SizeY > 8192*8192 then
       Exit;
       
    tmpSize:=Max(SizeX,SizeY)+Min(SizeX,SizeY);

    Steps:=Pred(tmpSize);
    Range:=Max(1,Min(255,Steps));

    tmpPen:=TPen.Create;
    try
      tmpPen.Assign(Pen);

      Pen.Width:=1;
      Pen.Style:=psSolid;
      Pen.Mode:=pmCopy;

      for t:=0 to tmpSize-1 do
      begin
        // VCL.Net bug.
        // Setting Pen.Color many times leaves Canvas out of resources
        Pen.Color:=CalcColor(t*Range div tmpSize);

        Line(tmpFrom,tmpTo);

        if not Up then
        begin
          if tmpTo.X<tmpRect.Right then Inc(tmpTo.X)
          else
          if tmpTo.Y<tmpRect.Bottom then Inc(tmpTo.Y);

          if tmpFrom.Y<tmpRect.Bottom then Inc(tmpFrom.Y)
          else
          if tmpFrom.X<tmpRect.Right then Inc(tmpFrom.X)
        end
        else
        begin
          if tmpTo.X<tmpRect.Right then Inc(tmpTo.X)
          else
          if tmpTo.Y>tmpRect.Top then Dec(tmpTo.Y);

          if tmpFrom.Y>tmpRect.Top then Dec(tmpFrom.Y)
          else
          if tmpFrom.X<tmpRect.Right then Inc(tmpFrom.X)
        end;
      end;

      Pen.Assign(tmpPen);
    finally
      tmpPen.Free;
    end;
  end;

begin
  tmpRect:=OrientRectangle(Rect);

  // deprecated. Use TTeeGradient.Draw method.
  if Direction=gdRadial then
  begin
    DoDrawRadial;
    exit;
  end;

  if (Direction<>gdTopBottom) and (Direction<>gdLeftRight) and
     (Direction<>gdDiagonalUp) and (Direction<>gdDiagonalDown) then
       SwapInteger(Integer(StartColor),Integer(EndColor));

  T0:=Byte(StartColor);
  T1:=Byte(StartColor shr 8);
  T2:=Byte(StartColor shr 16);

  D0:=Byte(EndColor)-T0;
  D1:=Byte(EndColor shr 8)-T1;
  D2:=Byte(EndColor shr 16)-T2;

  tmpBrush:=0;
  OldColor:=-1;

  Case Direction of
    gdLeftRight,
    gdRightLeft      : RectGradient(True);
    gdTopBottom,
    gdBottomTop      : RectGradient(False);
    gdFromTopLeft,
    gdFromBottomLeft : FromCorner;
    gdFromCenter     : FromCenter;
    gdDiagonalUp     : DiagonalGradient(True);
    gdDiagonalDown   : DiagonalGradient(False);
  end;

  {$IFDEF MSWINDOWS}
  if tmpBrush<>0 then
     DeleteObject(SelectObject(FDC,tmpBrush));
  {$ENDIF}
end;
{$ENDIF}

procedure TTeeCanvas3D.GradientFill(const Gradient:TCustomTeeGradient; const Rect:TRect);
var
  R : TRect;

  Procedure DoVert(C0,C1,C2,C3:TColor);
  begin
    R.Bottom:=(Rect.Bottom+Rect.Top) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    GradientFill(R,C0,C1,Gradient.Direction);
    R.Top:=R.Bottom;
    R.Bottom:=Rect.Bottom;
    GradientFill(R,C2,C3,Gradient.Direction);
  end;

  Procedure DoHoriz(C0,C1,C2,C3:TColor);
  begin
    R.Right:=(Rect.Left+Rect.Right) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
    GradientFill(R,C0,C1,Gradient.Direction);
    R.Left:=R.Right;
    R.Right:=Rect.Right;
    GradientFill(R,C2,C3,Gradient.Direction);
  end;

  Function TryGrayColor(Const AColor:TColor):TColor;
  var tmp : Byte;
  begin
    if Monochrome then
    begin
      with RGBValue(AColor) do
           tmp:=(Blue+Green+Red) div 3;

      {$IFDEF FMX}
      result:={$IFDEF D17}System.UIConsts.{$ELSE}FMX.Types.{$ENDIF}MakeColor(tmp,tmp,tmp,1);
      {$ELSE}
      result:=RGB(tmp,tmp,tmp);
      {$ENDIF}
    end
    else result:=AColor;
  end;

var tmpMid   : TColor;
    tmpStart : TColor;
    tmpEnd   : TColor;
begin
  with Gradient do
  begin
    if FAngle<>0 then
    begin
      {$IFDEF FMX}
      Self.FCanvas.Fill.Gradient.RadialTransform.RotationAngle:=FAngle;
      {$ELSE}
      DrawRotated(Self,Rect);
      Exit;
      {$ENDIF}
    end;

    tmpMid:=TryGrayColor(MidColor);
    tmpStart:=TryGrayColor(StartColor);
    tmpEnd:=TryGrayColor(EndColor);

    if Direction=gdRadial then
       GradientFill(Rect,tmpStart,tmpEnd,Direction,Balance,RadialX,RadialY)
    else
    if (tmpMid<>clNone) {IHasMiddle} and (Direction<>gdDiagonalUp) and (Direction<>gdDiagonalDown) then
    begin
      R:=Rect;

      Case Direction of
      gdTopBottom: DoVert(tmpMid,tmpEnd,tmpStart,tmpMid);
      gdBottomTop: DoVert(tmpStart,tmpMid,tmpMid,tmpEnd);
      gdLeftRight: DoHoriz(tmpMid,tmpEnd,tmpStart,tmpMid);
      gdRightLeft: DoHoriz(tmpStart,tmpMid,tmpMid,tmpEnd);
      else
         GradientFill(Rect,tmpStart,tmpEnd,Gradient.Direction)
      end;
    end
    else
      GradientFill(Rect,tmpStart,tmpEnd,Gradient.Direction,Gradient.Balance);
  end;
end;

procedure TTeeCanvas3D.EraseBackground(const Rect: TRect);
begin
  FillRect(Rect);
end;

{$IFDEF FMX}
procedure TTeeCanvas.PrepareGradient(const Source:TTeeGradient;
                               const Dest:TGradient);

  function InvertGradient:Boolean;
  begin
    case Source.Direction of
      gdRightLeft,
      gdBottomTop,
      gdDiagonalDown,
      gdDiagonalUp  : result:=True;
    else
      result:=False;
    end;
  end;

var L, t : Integer;
    M : TTransform;
begin
  Dest.Points.Clear;

  L:=Source.Colors.Count;

  if InvertGradient then
     for t:=0 to L-1 do
      with TGradientPoint(Dest.Points.Add) do
      begin
        Offset:=1-Source.Colors[L-t-1].Offset;
        Color:=Source.Colors[L-t-1].Color;
      end
  else
     for t:=0 to L-1 do
      with TGradientPoint(Dest.Points.Add) do
      begin
        Offset:=Source.Colors[t].Offset;
        Color:=Source.Colors[t].Color;
      end;

  case Source.Direction of
    gdTopBottom,
    gdBottomTop: begin
                   Dest.StartPosition.X:=0.5;
                   Dest.StopPosition.X:=0.5;
                   Dest.StartPosition.Y:=0;
                   Dest.StopPosition.Y:=1;
                 end;
    gdLeftRight,
    gdRightLeft: begin
                   Dest.StartPosition.X:=0;
                   Dest.StopPosition.X:=1;
                   Dest.StartPosition.Y:=0.5;
                   Dest.StopPosition.Y:=0.5;
                 end;
  gdFromTopLeft,
 gdDiagonalDown: begin
                   Dest.StartPosition.X:=0;
                   Dest.StopPosition.X:=1;
                   Dest.StartPosition.Y:=0;
                   Dest.StopPosition.Y:=1;
                 end;
gdFromBottomLeft,
   gdDiagonalUp: begin
                   Dest.StartPosition.X:=0;
                   Dest.StopPosition.X:=1;
                   Dest.StartPosition.Y:=1;
                   Dest.StopPosition.Y:=0;
                 end;
  else
    Dest.StartPosition.X:=0;
    Dest.StartPosition.Y:=0;
    Dest.StopPosition.X:=0;
    Dest.StopPosition.Y:=1;
  end;

  if (Source.Direction=gdRadial) or (Source.Direction=gdFromCenter) then
  begin
    M:=Dest.RadialTransform;

    M.RotationCenter.X:=0.5+Source.RadialX*0.01;
    M.RotationCenter.Y:=0.5+Source.RadialY*0.01;

    M.RotationAngle:=Source.Angle;

    M.Position.X:=0;
    M.Position.Y:=0;

    Dest.Style:=TGradientStyle.{$IFDEF D20}Radial{$ELSE}gsRadial{$ENDIF};
  end
  else
    Dest.Style:=TGradientStyle.{$IFDEF D20}Linear{$ELSE}gsLinear{$ENDIF};
end;

procedure TTeeCanvas3D.PrepareBrush;
begin
  if IsGradientVisible then
  begin
    PrepareGradient(FBrush.Gradient,FCanvas.Fill.Gradient);
    FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};
  end
  else
    FCanvas.Fill.Assign(FBrush);
end;
{$ENDIF}

procedure TTeeCanvas3D.FillRect(const Rect: TRect);
begin
  {$IFDEF FMX}

  PrepareBrush;
  FCanvas.FillRect(Rect,0,0, AllCorners, Opacity);
  FBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};

  {$ELSE}
  if IsGradientVisible then
     GradientFill(FBrush.FGradient,Rect)
  else
     {$IFDEF MSWINDOWS}
     Windows.FillRect(FCanvas.Handle, Rect, FBrush.Handle);
     {$ELSE}
     FCanvas.FillRect(Rect);
     {$ENDIF}
  {$ENDIF}
end;

Function ApplyDark(Color:TColor; HowMuch:Byte):TColor;
Var r : Byte;
    g : Byte;
    b : Byte;
Begin
  r:=Byte(Color);
  g:=Byte(Color shr 8);
  b:=Byte(Color shr 16);

  if r>HowMuch then Dec(r,HowMuch) else r:=0;
  if g>HowMuch then Dec(g,HowMuch) else g:=0;
  if b>HowMuch then Dec(b,HowMuch) else b:=0;

  result := (r or (g shl 8) or (b shl 16)
            {$IFDEF FMX}or (Byte(Color shr 24) shl 24){$ENDIF});
end;

Function ApplyBright(Color:TColor; HowMuch:Byte):TColor;
Var r : Byte;
    g : Byte;
    b : Byte;
Begin
  r:=Byte(Color);
  g:=Byte(Color shr 8);
  b:=Byte(Color shr 16);

  if (r+HowMuch)<256 then Inc(r,HowMuch) else r:=255;
  if (g+HowMuch)<256 then Inc(g,HowMuch) else g:=255;
  if (b+HowMuch)<256 then Inc(b,HowMuch) else b:=255;

  result := (r or (g shl 8) or (b shl 16)
            {$IFDEF FMX}or (Byte(Color shr 24) shl 24){$ENDIF});
end;

Procedure TTeeCanvas3D.Cube(Left,Right,Top,Bottom,Z0,Z1,BevelSize:Integer;
                            DarkSides:Boolean=True; RoundSize:Integer=0);
Begin
  Cube(Left,Right,Top,Bottom,Z0,Z1,DarkSides,RoundSize);
end;

Procedure TTeeCanvas3D.Cube(Left,Right,Top,Bottom,Z0,Z1:Integer;
                            DarkSides:Boolean=True; RoundSize:Integer=0);

var
  OldColor : TColor;

  procedure DoPolygon(const ADirection:TGradientDirection; ADark:Byte);
  begin
    if DarkSides then
    begin
      Gradient.Visible:=True;
      Gradient.Direction:=ADirection;
      Gradient.StartColor:=OldColor;
      Gradient.EndColor:=ApplyDark(OldColor,ADark);
    end;

    PolygonFour;

    if DarkSides then
       Gradient.Visible:=False;
  end;

  Function Culling:Double;
  begin
    result:=((IPoints[3].x-IPoints[2].x) * (IPoints[1].y-IPoints[2].y)) -
            ((IPoints[1].x-IPoints[2].x) * (IPoints[3].y-IPoints[2].y));
  end;

var P0,P1,
    P2,P3    : TPoint;
    tmp      : Double;
    Transparent : Boolean;
begin
  Calc3DPoint(P0,Left,Top,z0);
  Calc3DPoint(P1,Right,Top,z0);
  Calc3DPoint(P2,Right,Bottom,z0);
  Calc3DPoint(P3,Right,Top,z1);

  if (FBrush.Style=bsSolid) {$IFDEF FMX}or
     (FBrush.Style=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF}){$ENDIF} then
     OldColor:=FBrush.Color
  else
     OldColor:=BackColor;

  Transparent:=FBrush.Style=bsClear;

  IPoints[0]:=P0;
  IPoints[1]:=P1;
  IPoints[2]:=P2;
  Calc3DPoint(IPoints[3],Left,Bottom,z0);

  if Transparent or (Culling>0) then
     PolygonFour // front-side
  else
  begin
    Calc3DPoint(IPoints[0],Left,Top,z1);
    Calc3DPoint(IPoints[1],Right,Top,z1);
    Calc3DPoint(IPoints[2],Right,Bottom,z1);
    Calc3DPoint(IPoints[3],Left,Bottom,z1);
    PolygonFour // back-side
  end;

  Calc3DPoint(IPoints[2],Right,Bottom,z1);

  IPoints[0]:=P1;
  IPoints[1]:=P3;
  IPoints[3]:=P2;

  if Transparent or (Culling>0) then
     DoPolygon(gdLeftRight,DarkerColorQuantity); // right-side

  IPoints[0]:=P0;
  Calc3DPoint(IPoints[1],Left,Top,z1);
  Calc3DPoint(IPoints[2],Left,Bottom,z1);
  Calc3DPoint(IPoints[3],Left,Bottom,z0);

  if Transparent then
     tmp:=1
  else
     tmp:=(IPoints[3].x-IPoints[0].x) * (IPoints[1].y-IPoints[0].y) -
          (IPoints[1].x-IPoints[0].x) * (IPoints[3].y-IPoints[0].y);

  if tmp>0 then
     DoPolygon(gdRightLeft,DarkerColorQuantity); // left-side

  Calc3DPoint(IPoints[3],Left,Top,z1);

  // culling
  if Transparent then tmp:=1
                 else tmp:=(P0.x-P1.x) * (P3.y-P1.y) - (P3.x-P1.x) * (P0.y-P1.y);

  if tmp>0 then
  begin
    IPoints[0]:=P0;
    IPoints[1]:=P1;
    IPoints[2]:=P3;

    DoPolygon(gdBottomTop,DarkColorQuantity); // top-side
  end;

  Calc3DPoint(IPoints[0],Left,Bottom,z0);
  Calc3DPoint(IPoints[2],Right,Bottom,z1);
  Calc3DPoint(IPoints[1],Left,Bottom,z1);
  IPoints[3]:=P2;

  if Transparent or (Culling<0) then
     DoPolygon(gdTopBottom,DarkColorQuantity); // Bottom-side
end;

Procedure TTeeCanvas3D.RectangleZ(const Left,Top,Bottom:TCoordinate;
                                  const Z0,Z1:Integer);
begin
  Calc3DPoint(IPoints[0],Left,Top,Z0);
  Calc3DPoint(IPoints[1],Left,Top,Z1);
  Calc3DPoint(IPoints[2],Left,Bottom,Z1);
  Calc3DPoint(IPoints[3],Left,Bottom,Z0);
  PolygonFour;
end;

Procedure TTeeCanvas3D.RectangleY(const Left,Top,Right:TCoordinate;
                                  const Z0,Z1:Integer);
begin
  Calc3DPoint(IPoints[0],Left,Top,Z0);
  Calc3DPoint(IPoints[1],Right,Top,Z0);
  Calc3DPoint(IPoints[2],Right,Top,Z1);
  Calc3DPoint(IPoints[3],Left,Top,Z1);
  PolygonFour;
end;

procedure TTeeCanvas3D.DisableRotation;
begin
  FWas3D:=FIs3D;
  FIs3D:=False;
end;

procedure TTeeCanvas3D.EnableRotation;
begin
  FIs3D:=FWas3D;
end;

Procedure TTeeCanvas3D.Invalidate;
begin
  FDirty:=True;
end;

procedure TTeeCanvas3D.RotateLabel3D(x,y,z:Integer; const St:String; RotDegree:Double; AllowHTML:Boolean=False);
begin
  Calc3DPos(x,y,z);
  RotateLabel(x,y,St,RotDegree,AllowHTML);
end;

{$IFDEF LCL}
const
  GM_LAST=2;
{$ENDIF}

procedure TTeeCanvas3D.RotateLabel(x,y:Integer; const St:String; RotDegree:Double;
                                   AllowHTML:Boolean=False);
{$IFDEF FMX}
var t,Old : TMatrix;
begin
  if RotDegree<>0 then
  begin
    Old:=FCanvas.Matrix;

    {$IFDEF D19}
    t:=TMatrix.Identity;
    {$ELSE}
    t:=IdentityMatrix;
    {$ENDIF}

    t.m31:=ITextCenter.X;
    t.m32:=ITextCenter.Y;

    {$IFDEF D19}
    FCanvas.SetMatrix(TMatrix.CreateRotation(DegToRad(360-RotDegree))*t*Old);
    {$ELSE}
    FCanvas.SetMatrix(MatrixMultiply(MatrixMultiply(
              CreateRotationMatrix(DegToRad(360-RotDegree)),t),Old));
    {$ENDIF}

    DirectTextOut(0,0,Round(Y-ITextCenter.Y),St);

    FCanvas.SetMatrix(Old);
  end
  else
    DirectTextOut(0,X,Y,St);
end;
{$ELSE}

var OldFont: HFONT;
    NewFont: HFONT;
    LogRec : TLOGFONT;
    tmpSin,
    tmpCos : Extended;
    tmpX,
    tmpY   : Single;
    FDC   : TTeeCanvasHandle;
begin
  while RotDegree>360 do
        RotDegree:=RotDegree-360;

  if (RotDegree=0) and (CharacterAngle=0) then
     TextOut(x,y,St)
  else
  begin
    {$IFDEF MSWINDOWS}
    FDC:=Handle;

    GetObject(FFont.Handle, SizeOf(LogRec), @LogRec);

    LogRec.lfEscapement:=Round(RotDegree*10.0);

    if CharacterAngle<>0 then
       LogRec.lfOrientation:=Round(CharacterAngle*10.0);

    LogRec.lfOutPrecision:=OUT_TT_ONLY_PRECIS;

    SetFontQuality(LogRec);

    if CharacterAngle<>0 then
       SetGraphicsMode(FDC,GM_ADVANCED);

    NewFont:=CreateFontIndirect({$IFDEF LCL}@{$ENDIF}LogRec);

    OldFont:=SelectObject(FDC,NewFont);
    {$ENDIF}

    if IUseTextCenter then
    begin
      SinCos(RotDegree*TeePiStep,tmpSin,tmpCos);

      if TextAlign=TA_CENTER then
         tmpX:=0
      else
         tmpX:=(X-ITextCenter.X);

      tmpY:=(Y-ITextCenter.Y);

      X:=Round(ITextCenter.X+{Round}(  tmpX*tmpCos + tmpY*tmpSin ));
      Y:=Round(ITextCenter.Y+{Round}( -tmpX*tmpSin + tmpY*tmpCos ));

      // Rotated html text not yet available with GDI. Use GDI+
      {
      if AllowHTML then
         HtmlTextOut(Self,x,y,St)
      else
      }
         DirectTextOut(FDC,x,y,St);
    end
    else
    // Rotated html text not yet available with GDI. Use GDI+
    {
    if AllowHTML then
       HtmlTextOut(Self,x,y,St)
    else
    }
       DirectTextOut(FDC,x,y,St);

    {$IFDEF MSWINDOWS}
    if OldFont<>0 then
       DeleteObject(SelectObject(FDC,OldFont));

    if CharacterAngle<>0 then
       SetGraphicsMode(FDC,GM_LAST);
    {$ENDIF}
  end;
end;
{$ENDIF}

{$IFDEF FMX}
{$IFDEF HASCANVASD2D}
type
  TCustomCanvasD2DHelper=class(TCustomCanvasD2D)
  public
    procedure DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
  end;

function D2Point(const X, Y: Single): TD2D1Point2F; inline;
begin
  Result.x := X;
  Result.y := Y;
end;

function D2Matrix(const M: TMatrix): TD2D1Matrix3X2F;
begin
  Result._11 := M.m11;
  Result._12 := M.m12;
  Result._21 := M.m21;
  Result._22 := M.m22;
  Result._31 := M.m31;
  Result._32 := M.m32;
end;

function D2Color(const AColor: TAlphaColor; const Opacity: Single): TD2D1ColorF;
var
  D2R: Single;
  D2G: Single;
  D2B: Single;
  D2A: Single;
begin
  D2R := TAlphaColorRec(AColor).R / $FF;
  D2B := TAlphaColorRec(AColor).B / $FF;
  D2G := TAlphaColorRec(AColor).G / $FF;
  D2A := TAlphaColorRec(AColor).A / $FF;
  Result := D2D1ColorF(D2R, D2G, D2B, D2A * Opacity);
end;

procedure TCustomCanvasD2DHelper.DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
var
  FTarget: ID2D1RenderTarget;
  FStrokeBrush: ID2D1Brush;
  FStrokeStyle: ID2D1StrokeStyle;

  procedure ObtainTarget;
  var r : TRttiContext;
      t : TRttiType;
      f : TRttiField;
      i : IInterface;
  begin
    r:=TRttiContext.Create;
    t:=r.GetType(ClassInfo);

    f:=t.GetField('FTarget');
    i:=f.GetValue(Self).AsInterface;
    FTarget:=i as ID2D1RenderTarget;
  end;

  procedure ObtainStroke;
  var r : TRttiContext;
      t : TRttiType;
      f : TRttiField;
      i : IInterface;
  begin
    r:=TRttiContext.Create;
    t:=r.GetType(ClassInfo);

    f:=t.GetField('FStrokeBrush');
    i:=f.GetValue(Self).AsInterface;
    FStrokeBrush:=i as ID2D1Brush;

    f:=t.GetField('FStrokeStyle');
    i:=f.GetValue(Self).AsInterface;
    FStrokeStyle:=i as ID2D1StrokeStyle;
  end;

  (* Cannot be called. It is private and rtti cannot obtain or call it
  procedure CallApplyStroke;
  var r : TRttiContext;
      t : TRttiInstanceType;
      m : TRttiMethod;
      args : Array of TValue;
  begin
    r:=TRttiContext.Create;
    t:=r.GetType(ClassInfo) as TRttiInstanceType;

    m:=t.GetMethod('ApplyStroke');

    // ApplyStroke(Stroke, TRectF.Empty, AOpacity);

    if m<>nil then
    begin
      SetLength(args,3);
      args[0]:=Stroke;
      args[1]:=TValue.From<TRectF>(TRectF.Empty);
      args[2]:=AOpacity;

      m.Invoke(Self,args);
    end;
  end;
  *)

  function CreateGeometry:ID2D1PathGeometry;
  var Path: ID2D1GeometrySink;
  begin
    SharedFactory.CreatePathGeometry(result);
    result.Open(Path);
    Path.BeginFigure(D2Point(P[0].X, P[0].Y), D2D1_FIGURE_BEGIN_HOLLOW);
    Path.AddLines(PD2D1Point2F(@P[1]),Length(P)-1);
    Path.EndFigure(D2D1_FIGURE_END_OPEN);
    Path.Close;
  end;

var
  Old,tmp : TD2D1AntiAliasMode;
begin
  if Length(P)>0 then
  begin
    ObtainTarget;

    if FTarget <> nil then
    begin
      ObtainStroke;

      if FStrokeBrush<>nil then
      begin
        FTarget.CreateSolidColorBrush(D2Color(Stroke.Color, AOpacity), nil, ID2D1SolidColorBrush(FStrokeBrush));

        //CallApplyStroke;

        Old:=FTarget.GetAntialiasMode;

        {if ForceAliased then
           Aliased:=True
        else
           Aliased:=False;
        }

        if Aliased then
           tmp:=D2D1_ANTIALIAS_MODE_ALIASED
        else
           tmp:=D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; // D2D1_TEXT_ANTIALIAS_MODE_DEFAULT

        if Old<>tmp then
           FTarget.SetAntialiasMode(tmp);

        FTarget.DrawGeometry(CreateGeometry, FStrokeBrush, Stroke.Thickness, FStrokeStyle);

        if Old<>tmp then
           FTarget.SetAntialiasMode(Old);
      end;
    end;
  end;
end;
{$ENDIF}

{$IFDEF HASCANVASGDIP}
procedure GdipHelperDrawLines(const ACanvas:TCanvas; const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);

  function GDIPColor(const AColor:TColor; ATransparency:Byte):Cardinal;
  var tmp : Integer;
  begin
    result:=AColor;

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

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

  function ObtainGraphics:TGPGraphics;
  var r : TRttiContext;
      t : TRttiType;
      f : TRttiField;
  begin
    r:=TRttiContext.Create;
    t:=r.GetType(ACanvas.ClassInfo);

    f:=t.GetField('FGPGraphics');
    result:=f.GetValue(ACanvas).AsObject as TGPGraphics;
  end;

var G : TGPGraphics;
    tmpPen : TGPPen;
    L : Integer;
begin
  L:=Length(P);

  if L>0 then
  begin
    G:=ObtainGraphics;

    if G<>nil then
    begin
      tmpPen:=TGPPen.Create(GDIPColor(ACanvas.Stroke.Color,Round(255*(1-AOpacity))));

      G.SetSmoothingMode(SmoothingModeHighSpeed);
      G.DrawLines(tmpPen,PGPPointF(@P[0]),L);
      G.SetSmoothingMode(SmoothingModeHighQuality);

      tmpPen.Free;
    end;
  end;
end;
{$ENDIF}

{$IFDEF HASCANVASGPU}
type
  TCustomCanvasGpuHelper=class(TCustomCanvasGpu)
  public
    procedure DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
  end;

procedure TCustomCanvasGpuHelper.DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
var Path : FMXTPathData;
begin
  // No better option. OpenGLES is not working in old "fixed function pipeline" mode,
  // so the trick to call glDrawArrays is not possible.

  Path:=PointsPath(P,False);
  try
    DrawPath(Path,AOpacity);
  finally
    Path.Free;
  end;
end;
{$ENDIF}

{$IFDEF HASCANVASMAC}
type
  TCustomCanvasMacHelper=class(TCanvasQuartz)
  public
    procedure DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
  end;

procedure TCustomCanvasMacHelper.DrawLines(const P: array of TPointF; const AOpacity:Single; Aliased:Boolean=True);
var t,L : Integer;
begin
  L:=Length(P);

  if (L>0) and (Context <> nil) then
  begin
    ApplyStroke(Stroke,TeeZeroRectF,AOpacity);

    if Aliased then
       CGContextSetShouldAntialias(Context,{$IFNDEF D27}Ord{$ENDIF}(False));

    // Not useful (lines should be defined by two points each segment)
    // CGContextStrokeLineSegments(Context,PCGPoint(@P[0]),Length(P));

    CGContextBeginPath(Context);
    CGContextMoveToPoint(Context, P[0].X, P[0].Y);

    for t:=1 to L-1 do
        CGContextAddLineToPoint(Context, P[t].X, P[t].Y);

    CGContextStrokePath(Context);

    if Aliased then
       CGContextSetShouldAntialias(Context,{$IFNDEF D27}Ord{$ENDIF}(True));
  end;
end;
{$ENDIF}

{$ENDIF}

Procedure TTeeCanvas3D.Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
{$IFDEF FMX}

  // Create a TPath, add all points, and then draw the Path
  procedure DoPath;
  var Path : FMXTPathData;
  begin
    Path:=PointsPath(Points,False);
    try
      FCanvas.DrawPath(Path,Opacity);
    finally
      Path.Free;
    end;
  end;

{$IFNDEF D19}
{$IFDEF D18}
{$IFDEF IOS}
{$DEFINE NOFIXED_IOS}
{$ENDIF}
{$ENDIF}
{$ENDIF}

  {$IFDEF NOFIXED_IOS}
  procedure DrawAsSegments;
  var t : Integer;
  begin
    // Workaround for QC113261 (no TPath can be used)
    MoveTo(Points[0]);

    for t:= 1 to Length(Points)-1 do
        LineTo(Points[t]);
  end;
  {$ENDIF}

begin
  if Length(Points)>0 then
  begin
    {$IFDEF NOFIXED_IOS}
    DrawAsSegments;
    {$ELSE}

    if IFastLine=0 then
       DoPath
    else
    {$IFDEF HASCANVASMAC}
    begin
      TCustomCanvasMacHelper(FCanvas).DrawLines(Points,Opacity);
      Exit;
    end;
    {$ENDIF}
    {$IFDEF HASCANVASD2D}
    if FCanvasIsD2D then
       TCustomCanvasD2DHelper(FCanvas).DrawLines(Points,Opacity)
    else
    {$ENDIF}
    {$IFDEF HASCANVASGDIP}
    if FCanvasIsGdip then
       GdipHelperDrawLines(FCanvas,Points,Opacity)
    else
    {$ENDIF}
    {$IFDEF HASCANVASGPU} // No good solution yet for TCanvasGpu
    if FCanvasIsGPU then
       TCustomCanvasGpuHelper(FCanvas).DrawLines(Points,Opacity)
    else
    {$ENDIF}
       DoPath;
    {$ENDIF}
  end;
end;

{$ELSE}
begin
  {$IFDEF MSWINDOWS}
  Windows.Polyline(FCanvas.Handle, PPoints(@Points)^, High(Points)+1);
  {$ELSE}
  FCanvas.Polyline(Points);
  {$ENDIF}
end;
{$ENDIF}

{$IFDEF PIEFLOATS}
{$IFNDEF FMX}
function RoundPoints(const Points:Array of TPointFloat):TPointArray;
var l,
    t : Integer;
begin
  l:=Length(Points);
  SetLength(result,l);

  for t:=0 to l-1 do
  begin
    result[t].X:=Round(Points[t].X);
    result[t].Y:=Round(Points[t].Y);
  end;
end;

procedure TTeeCanvas3D.PolygonFloat(const Points:Array of TPointFloat);
var P : TPointArray;
begin
  P:=RoundPoints(Points);
  try
    Polygon(P);
  finally
    P:=nil;
  end;
end;

procedure TTeeCanvas3D.PolylineFloat(const Points:Array of TPointFloat);
var P : TPointArray;
begin
  P:=RoundPoints(Points);
  try
    Polyline(P);
  finally
    P:=nil;
  end;
end;

{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}
procedure TTeeCanvas3D.PolylineFloat(const Points:Array of TPoint3DFloat);
var P : TPointFloatArray;
    t : Integer;
begin
  SetLength(P,Length(Points));

  for t:=0 to Length(Points)-1 do
  begin
    P[t].X:=Points[t].X;
    P[t].Y:=Points[t].Y;

    Calculate3DPosition(P[t],Points[t].Z);
  end;

  PolylineFloat(P);

  P:=nil;
end;
{$ENDIF}

procedure TTeeCanvas3D.Polygon({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
var {$IFDEF FMX}
    P: FMXTPathData;
    {$ELSE}
    tmpGradient : Boolean;
    {$ENDIF}
begin
  {$IFDEF FMX}
  P:=PointsPath(Points,True);
  try
    PrepareBrush;

    if FBrush.Kind<>TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF} then
    begin
      FCanvas.FillPath(P, Opacity);
      FBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
    end;

    if FPen.Style<>psClear then
       FCanvas.DrawPath(P, Opacity);
  finally
    P.Free;
  end;
  {$ELSE}

  if FBrush.HasGraphic then
  begin
    ClipPolygon(Points,Length(Points));
    FCanvas.StretchDraw(PolygonBounds(Points),FBrush.Image.Filtered);
    UnClipRectangle;

    FCanvas.Brush.Style:=bsClear;
  end;

  tmpGradient:=IsGradientVisible;

  if tmpGradient then
  begin
    ClipPolygon(Points,Length(Points));
    GradientFill(FBrush.FGradient,PolygonBounds(Points));
    UnClipRectangle;

    FCanvas.Brush.Style:=bsClear;
  end;

  {$IFDEF MSWINDOWS}
  Windows.Polygon(FCanvas.Handle, PPoints(@Points)^, High(Points) + 1);
  {$ELSE}
  FCanvas.Polygon(Points);
  {$ENDIF}
  {$ENDIF}
end;

Procedure TTeeCanvas3D.Polygon3D(const Points: Array of TPoint3D);
var P : TPointArray;
    L : Integer;
    t : Integer;
begin
  L:=Length(Points);
  SetLength(P,L);
  try
    for t:=0 to L-1 do
    with Points[t] do
         Calc3DPoint(P[t],x,y,z);

    Polygon(P);
  finally
    P:=nil;
  end;
end;

{$IFNDEF FMX}
procedure TCanvas3D.Polygon3DConcave({$IFDEF D6}const{$ENDIF} Points: array of TPoint3DFloat; const Invert:Boolean=False);
var tmp : Array of TPoint3D;
    t   : Integer;
begin
  // TODO: Opposite. Do this at overload Polygon3D (TPoint3D) instead of here.
  SetLength(tmp,Length(Points));

  for t:=0 to Length(Points)-1 do
  begin
    tmp[t].x:=Round(Points[t].X);
    tmp[t].y:=Round(Points[t].Y);
    tmp[t].z:=Round(Points[t].Z);
  end;

  Polygon3DConcave(tmp,Invert);

  tmp:=nil;
end;
{$ENDIF}

procedure TCanvas3D.Polygon3DConcave({$IFDEF D6}const{$ENDIF} Points: array of TPoint3D; const Invert:Boolean=False);
begin
  Polygon3D(Points);
end;


procedure TTeeCanvas3D.Ellipse(const X1, Y1, X2, Y2: TCoordinate);
{$IFDEF FMX}
var R : TRect;
begin
  R:=TeeRect(X1,Y1,X2,Y2);

  if FBrush.Kind<>TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF} then
  begin
    PrepareBrush;
    FCanvas.FillEllipse(R,Opacity);
    FBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  end;

  if FPen.Style<>psClear then
     FCanvas.DrawEllipse(R,Opacity);
end;
{$ELSE}

var oldStyle    : TBrushStyle;
    tmpGradient : Boolean;
    tmpR        : TRect;
begin
  if FBrush.Style<>FCanvas.Brush.Style then
     DoChangedBrush(FBrush);

  oldStyle:=FCanvas.Brush.Style;

  tmpGradient:=IsGradientVisible;

  if tmpGradient then
  begin
    tmpR:=TeeRect(X1,Y1,X2+1,Y2+1);

    ClipEllipse(tmpR);
    GradientFill(FBrush.FGradient,tmpR);
    UnClipRectangle;

    FCanvas.Brush.Style:=bsClear;
  end;

  {$IFDEF MSWINDOWS}
  Windows.Ellipse(FCanvas.Handle, X1, Y1, X2, Y2);
  {$ELSE}
  FCanvas.Ellipse(X1,Y1,X2,Y2);
  {$ENDIF}

  if tmpGradient then
     FCanvas.Brush.Style:=oldStyle;
end;
{$ENDIF}

procedure TTeeCanvas3D.GetCirclePoints(var P:TCirclePoints; const X1, Y1, X2, Y2: TCoordinate);
var PCenter : {$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF};
    XRadius : {$IFDEF FMX}Single{$ELSE}Double{$ENDIF};
    YRadius : {$IFDEF FMX}Single{$ELSE}Double{$ENDIF};
    t       : Integer;
    tmpSin  : {$IFDEF D15}{$IFDEF FMX}Single{$ELSE}Double{$ENDIF}{$ELSE}Extended{$ENDIF};
    tmpCos  : {$IFDEF D15}{$IFDEF FMX}Single{$ELSE}Double{$ENDIF}{$ELSE}Extended{$ENDIF};
    PiStep  : Single;
begin
  PiStep:=TwoPi*(360.0/(NumCirclePoints-1))/360.0;

  PCenter.X:=(X2+X1)*0.5;
  PCenter.Y:=(Y2+Y1)*0.5;
  XRadius:=(X2-X1)*0.5;
  YRadius:=(Y2-Y1)*0.5;

  for t:=0 to NumCirclePoints-1 do
  begin
    SinCos(t*PiStep,tmpSin,tmpCos);
    P[t].X:=PCenter.X+(XRadius*tmpSin);
    P[t].Y:=PCenter.Y+(YRadius*tmpCos);
  end;
end;

Procedure TTeeCanvas3D.GetCirclePoints(var P:TCirclePoints; X1, Y1, X2, Y2, Z: Integer);
var PCenter : TPointFloat;
    XRadius : Double;
    YRadius : Double;
    t       : Integer;
    tmpSin  : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos  : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    PiStep  : Single;
begin
  PiStep:=TwoPi*(360.0/(NumCirclePoints-1))/360.0;

  PCenter.X:=(X2+X1)*0.5;
  PCenter.Y:=(Y2+Y1)*0.5;
  XRadius:=(X2-X1)*0.5;
  YRadius:=(Y2-Y1)*0.5;

  for t:=0 to NumCirclePoints-1 do
  begin
    SinCos(t*PiStep,tmpSin,tmpCos);
    P[t].x:=PCenter.X+(XRadius*tmpSin);
    P[t].y:=PCenter.Y+(YRadius*tmpCos);
    Calculate3DPosition(P[t],Z);
    //Calc3DPoint(P[t],PCenter.X+(XRadius*tmpSin),PCenter.Y+(YRadius*tmpCos),Z);
  end;
end;

procedure TTeeCanvas3D.EllipseWithZ(const X1, Y1, X2, Y2: TCoordinate;
                                    const Z:Integer);
Var P     : TCirclePoints;
    P1,P2 : TPoint;
begin
  if FIsOrthogonal then
  begin
    Calc3DPoint(P1,X1,Y1,Z);
    Calc3DPoint(P2,X2,Y2,Z);
    Ellipse(P1.X,P1.Y,P2.X,P2.Y);
  end
  else
  if FIs3D then
  begin
    SetLength(P,NumCirclePoints);

    GetCirclePoints(P,X1,Y1,X2,Y2);

    if FBrush.Style<>bsClear then
       {$IFDEF FMX}
       PolygonWithZ(P,Z)
       {$ELSE}
       PolygonFloatWithZ(P,Z)
       {$ENDIF}
    else
    if FPen.Style<>psClear then
       PolylineWithZ(P,Z);

    P:=nil;
  end
  else
    Ellipse(X1,Y1,X2+1,Y2+1);
end;

{$IFDEF D17}
type
  TCanvasAccess=class(TCanvas);
{$ENDIF}

Function TTeeCanvas3D.GetPixel(X, Y: Integer):TColor;
{$IFDEF FMX}
{$IFDEF D18}
var tmpData : FMXTBitmapData;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
    {$IFDEF D18}
    if Assigned(FBitmap) and FBitmap.Map(TMapAccess.{$IFDEF D20}Read{$ELSE}maRead{$ENDIF},tmpData) then
    begin
      result:=tmpData.GetPixel(X,Y);
      FBitmap.Unmap(tmpData);
    end
    else
    {$ELSE}
    if FCanvas.Buffered then
       {$IFDEF D17}
       result:=PAlphaColorArray(TCanvasAccess(FCanvas).FBufferBits)[(Y * FCanvas.Width)+X]
       {$ELSE}
       result:=PAlphaColorArray(FCanvas.BufferBits)[(Y * FCanvas.Width)+X]
       {$ENDIF}
    else
    {$ENDIF}
       result:=clWhite;  // ie: GDI+ GdipBitmapGetPixel
  {$ELSE}

  result:=FCanvas.Pixels[x,y];
  {$ENDIF}
end;

procedure TTeeCanvas3D.SetPixel(X, Y: Integer; Value: TColor);
{$IFDEF FMX}
{$IFDEF D18}
var tmpData : FMXTBitmapData;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF FMX}
    {$IFDEF D18}
    if Assigned(FBitmap) and FBitmap.Map(TMapAccess.{$IFDEF D20}Write{$ELSE}maWrite{$ENDIF},tmpData) then
    begin
      tmpData.SetPixel(X,Y,Value);
      FBitmap.Unmap(tmpData);
    end;
    {$ELSE}
    if FCanvas.Buffered then
       {$IFDEF D17}
       PAlphaColorArray(TCanvasAccess(FCanvas).FBufferBits)[(Y * FCanvas.Width)+X]:=Value;
       {$ELSE}
       PAlphaColorArray(FCanvas.BufferBits)[(Y * FCanvas.Width)+X]:=Value;
       {$ENDIF}
    {$ENDIF}
  {$ELSE}
  {$IFDEF MSWINDOWS}
  Windows.SetPixel(FCanvas.Handle, X, Y, Value);
  {$ELSE}
  FCanvas.Pixels[X,Y]:=Value;
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeeCanvas3D.Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer);
{$IFDEF FMX}
var Center : TPoint;
    Radius : TPoint;
    StartAngle : Single;
    EndAngle : Single;
begin
  if FPen.Style<>psClear then
  begin
    CalcArcAngles(Left,Top,Right,Bottom,StartX,StartY,EndX,EndY,StartAngle,EndAngle);

    Center.X:=(Left+Right)*0.5;
    Center.Y:=(Top+Bottom)*0.5;
    Radius.X:=(Right-Left)*0.5;
    Radius.Y:=(Bottom-Top)*0.5;

    if Bottom>Top then
       FCanvas.DrawArc(Center,Radius,-StartAngle,StartAngle-EndAngle,Opacity)
    else
       FCanvas.DrawArc(Center,Radius,StartAngle,EndAngle-StartAngle,Opacity);
  end;
end;
{$ELSE}
begin
  {$IFDEF MSWINDOWS}
  Windows.Arc(FCanvas.Handle, Left, Top, Right, Bottom, StartX, StartY, EndX, EndY);
  {$ELSE}
  FCanvas.Arc(Left,Top,Right,Bottom,StartX, StartY, EndX, EndY);
  {$ENDIF}
end;
{$ENDIF}

{$IFDEF LCL}
Function Slice(const A:Array of TPoint; Count:Integer):TPointArray;
var t : Integer;
begin
  SetLength(result,Count);
  for t:=0 to Count-1 do result[t]:=A[t];
end;
{$ENDIF}

procedure TTeeCanvas.ArcTo(const X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer;
                           const ClockWise:Boolean=False);
{$IFNDEF FMX}
var tmpDC : TTeeCanvasHandle;
{$ENDIF}
begin
  {$IFNDEF FMX}
  tmpDC:=Handle;

  {$IFDEF MSWINDOWS}
  if ClockWise then
     SetArcDirection(tmpDC,AD_CLOCKWISE);

  Windows.ArcTo(tmpDC,X1,Y1,X2,Y2,X3,Y3,X4,Y4);

  if ClockWise then
     SetArcDirection(tmpDC,AD_COUNTERCLOCKWISE);

  {$ELSE}
  FCanvas.ArcTo(X1,Y1,X2,Y2,X3,Y3,X4,Y4);
  {$ENDIF}
  {$ENDIF}
end;

{$IFDEF FMX}
function TTeeCanvas.DonutPath(const R:TRect; const StartAngle,EndAngle,Percent,Thick:Single):FMXTPathData;
var tmpCenter : TPoint;
begin
  tmpCenter:=RectCenter(R);

  result:=FMXTPathData.Create;

  if Percent=0 then
     result.MoveTo(tmpCenter);

  result.AddArc(tmpCenter, TeePoint((R.Width - Thick) * 0.5,
          (R.Height - Thick) * 0.5), 360 - EndAngle, EndAngle - StartAngle);

  if Percent<>0 then
     result.AddArc(tmpCenter, TeePoint(0.01*Percent*(R.Width - Thick) * 0.5,
             0.01*Percent*(R.Height - Thick) * 0.5), 360 - StartAngle, -(EndAngle - StartAngle))
  else
     result.LineTo(tmpCenter);

  result.ClosePath;
end;

procedure TTeeCanvas.Donut( const R:TRect; const StartAngle,EndAngle,HolePercent:Double);
begin
end;

procedure TTeeCanvas3D.Donut( const R:TRect; const StartAngle,EndAngle,HolePercent:Double);
var P : FMXTPathData;
    tmpThick  : Single;
begin
  if FPen.Style=psClear then
     tmpThick:=0
  else
     tmpThick:=FCanvas.{$IFDEF D17}Stroke.Thickness{$ELSE}StrokeThickness{$ENDIF};

  P:=DonutPath(R,StartAngle,EndAngle,100*HolePercent,tmpThick);
  try
    if FBrush.Style<>bsClear then
    begin
      PrepareBrush;
      FCanvas.FillPath(P, Opacity);
    end;

    if FPen.Style<>psClear then
       FCanvas.DrawPath(P, Opacity);
  finally
    P.Free;
  end;
end;

{$ENDIF}

// Donut 2D
procedure TTeeCanvas3D.Donut( XCenter,YCenter,XRadius,YRadius:Integer;
                              Const StartAngle,EndAngle,HolePercent:Double);
{$IFDEF FMX}
begin
  Donut(TeeRect(XCenter-XRadius,YCenter-YRadius,XCenter+XRadius,YCenter+YRadius),
        StartAngle*HalfDivPi,EndAngle*HalfDivPi,0.01*HolePercent);
end;
{$ELSE}
{$IFDEF USEWINDOWS}
var tmpXRadius,
    tmpYRadius  : Double;
    tmpDC       : TTeeCanvasHandle;

  Procedure CalcPoint(const Angle:Double; out x,y:Integer);
  var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
      tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
  begin
    SinCos(Angle,tmpSin,tmpCos);
    x:=XCenter+Round(XRadius*tmpCos);
    y:=YCenter-Round(YRadius*tmpSin);
  end;

var x3,y3,
    x4,y4 : Integer;

  procedure CreateDonutPath;

    function IsFullDonut:Boolean;
    begin
      result:=Abs(EndAngle-StartAngle)>(TwoPi-0.01);
    end;

  begin
    BeginPath(tmpDC);

    if (x4<>x3) or (y4<>y3) or IsFullDonut then
    begin
      if HolePercent=0 then
         Pie(XCenter-XRadius,YCenter-YRadius,XCenter+XRadius,YCenter+YRadius,x3,y3,x4,y4)
      else
      begin
        ArcTo(XCenter-XRadius,YCenter-YRadius,XCenter+XRadius,YCenter+YRadius,x3,y3,x4,y4);

        ArcTo(XCenter-Round(tmpXRadius),YCenter-Round(tmpYRadius),
              XCenter+Round(tmpXRadius),YCenter+Round(tmpYRadius),
              x4,y4,x3,y3,True);
      end;
    end
    else
      LineTo(x4,y4);

    EndPath(tmpDC);
  end;

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

  CalcPoint(StartAngle,x3,y3);
  CalcPoint(EndAngle,x4,y4);

  MoveTo(x3,y3);

  tmpDC:=Handle;

  CreateDonutPath;

  if IsGradientVisible then
  begin
    SetCanvasRegion(tmpDC,PathToRegion(tmpDC));
    GradientFill(FBrush.FGradient,TeeRect(XCenter-XRadius,YCenter-YRadius,XCenter+XRadius,YCenter+YRadius));
    UnClipRectangle;

    CreateDonutPath;

    StrokePath(tmpDC);
  end
  else
    StrokeAndFillPath(tmpDC);
end;
{$ELSE}
begin
  FCanvas.RadialPie(XCenter-XRadius,YCenter-YRadius,XCenter+XRadius,YCenter+YRadius,
     Round(StartAngle),Round(EndAngle-StartAngle));
end;
{$ENDIF}
{$ENDIF}

procedure TTeeCanvas3D.Pie(const R:TRect; const StartAngle,EndAngle:Double);
{$IFDEF FMX}
{$ELSE}
var
  tmpXCenter : Single;
  tmpYCenter : Single;

  Procedure CalcPoint(const Angle:Double; out x,y:Integer);
  var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
      tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
  begin
    SinCos(Angle*TeePiStep,tmpSin,tmpCos);
    x:=Round(tmpXCenter+(R.Right-R.Left)*tmpCos*0.5);
    y:=Round(tmpYCenter-(R.Bottom-R.Top)*tmpSin*0.5);
  end;

var
  x3,y3,
  x4,y4 : Integer;
{$ENDIF}
begin
  {$IFDEF FMX}
  Donut(R,StartAngle,EndAngle,0);
  {$ELSE}

  if (Abs(EndAngle-StartAngle-360)<0.05) and (Abs(EndAngle-StartAngle)>180) then
     Ellipse(R)
  else
  begin
    tmpXCenter:=(R.Left+R.Right)*0.5;
    tmpYCenter:=(R.Top+R.Bottom)*0.5;

    CalcPoint(StartAngle,x3,y3);
    CalcPoint(EndAngle,x4,y4);

    if (x3=x4) and (y3=y4) then
    begin
      if (Abs(EndAngle-StartAngle-360)<1) and (Abs(EndAngle-StartAngle)>180) then
      begin
        Ellipse(R);
        Line(Round(tmpXCenter),Round(tmpYCenter),x3,y3);
      end
      else
      Begin
        Pie(R.Left,R.Top,R.Right,R.Bottom,x3,y3,x4,y4);
        Line(Round(tmpXCenter),Round(tmpYCenter),x3,y3);
      end;
      Exit;
    end;

    Pie(R.Left,R.Top,R.Right,R.Bottom,x3,y3,x4,y4);
  end;
  {$ENDIF}
end;

procedure TTeeCanvas3D.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
{$IFDEF FMX}
var Theta  : Single;
    Theta2 : Single;
begin
  CalcPieAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4,Theta,Theta2);
  Pie(TeeRect(X1,Y1,X2,Y2),Theta,Theta2);
end;
{$ELSE}
{$IFDEF MSWINDOWS}
  procedure DoPie;
  begin
    if (X3<>X4) or (Y3<>Y4) then
       Windows.Pie(FCanvas.Handle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
  end;

var tmpGradient : Boolean;
    oldStyle    : TBrushStyle;
    tmpDC       : TTeeCanvasHandle;
begin
  oldStyle:=FBrush.Style;

  tmpGradient:=IsGradientVisible;

  if tmpGradient then
  begin
    tmpDC:=Handle;

    BeginPath(tmpDC);
    DoPie;
    EndPath(tmpDC);

    SetCanvasRegion(tmpDC,PathToRegion(tmpDC));

    GradientFill(FBrush.FGradient,TeeRect(X1,Y1,X2,Y2));
    UnClipRectangle;

    FCanvas.Brush.Style:=bsClear;
  end;

  DoPie;

  if tmpGradient then
     FBrush.Style:=oldStyle;
end;
{$ELSE}
begin
  FCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4);
end;
{$ENDIF}
{$ENDIF}

procedure TCanvas3D.CalcPieAngles(X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer; out Theta,Theta2:Single);
var
  Width, Height: Integer;
  tmpX, tmpY,
  CenterX, CenterY: Single;
begin
  Width := X2 - X1;
  Height := Y2 - Y1;
  CenterX := X1 + (Width*0.5);
  CenterY := Y1 + (Height*0.5);

  tmpY:=CenterY-Y3;
  tmpX:=X3-CenterX;
  Theta:=ArcTan2(tmpY, tmpX);

  if Theta<0 then Theta:=TwoPi+Theta;
  Theta:=Theta*HalfDivPi;
  if Theta=360 then
     Theta:=0;

  tmpY:=CenterY-Y4;
  tmpX:=X4-CenterX;
  Theta2:=ArcTan2(tmpY, tmpX);

  if Theta2<0 then Theta2:=TwoPi+Theta2;
  Theta2 :=Theta2*HalfDivPi;
  if Theta2=0 then
     Theta2:=360;
end;

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

function TTeeCanvas3D.CheckFastBrush:Boolean;
begin
  {$IFDEF FMX}
  result:=False;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  result:=Assigned(@TeeSetDCBrushColor);

  if result then
     SelectObject(Handle, GetStockObject(DC_BRUSH));
  {$ELSE}
  result:=False;
  {$ENDIF}
  {$ENDIF}
end;

type
  TPiePoint={$IFDEF PIEFLOATS}TPointFloat{$ELSE}TPoint{$ENDIF};

  TPieDrawer=class
  private
    z0Rect,
    z1Rect : TRect;
    z0DonutRect,
    z1DonutRect : TRect;
    z0BevelRect,
    z1BevelRect : TRect;
    zPos : Integer;

    {$IFDEF PIELIST}
    count : Integer;
    list  : Array of TPiePoint;
    {$ENDIF}

    middle0, middle1, start0, end0, start1, end1 : TPoint;
    startD0, endD0, startD1, endD1 : TPoint;
    startB0, endB0, startB1, endB1 : TPoint;
    sweepAngle : Integer;
    isDonut, drawEntirePie,
    iDrawSides, hasBevel : Boolean;
    OldColor : TColor;
    iStartAngle, iEndAngle : Integer;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    ICanvas : TTeeCanvas3D;

    Pie3DUsingArcs : Boolean;
    BevelBright : Integer;
    BevelBorder : Boolean;
    HideSides   : Boolean;
    Full360     : Boolean;

    Procedure DrawPoints;
    procedure Loop(Start,Stop:Integer; const R:TRect);
    procedure DrawArc(const R:TRect; const PFrom,PTo:TPoint; Inverted:Boolean=False);
    function BeginUsingArcs:Boolean;
    procedure EndUsingArcs;
    procedure DrawLoops(const Start,Stop:Integer; const R0,R1:TRect);
    Procedure DrawBevel(const startAng, endAng:Integer {; Split: Boolean=False}); overload;
    Procedure DrawBack(const StartAng, EndAng:Integer);
    procedure DrawBottom;
    Procedure DrawFront(const StartAng,EndAng:Integer);
    Procedure DrawLeft;
    procedure DrawRight;
    procedure DrawTopBottom(StartAng,EndAng:Integer; DoBevel:Boolean; const Middle:TPoint; const R,RDonut:TRect);
    procedure DrawTop;
    Procedure DoDrawSides(StartAng,EndAng:Integer);
    function CalcPieStart(const R:TRect):TPoint;
    function CalcPieEnd(const R:TRect):TPoint;
    procedure DrawLighting(const AStyle:TEdgeStyle);
    procedure CalcStartEnd(const P:TPoint);
    procedure PrepareDonut(const R:TRect);
    procedure PrepareBevel(const R:TRect; Amount:Integer);
  end;

  procedure TPieDrawer.PrepareBevel(const R:TRect; Amount:Integer);
  begin
    z0BevelRect := R;
    z1BevelRect := z0Rect;

    {$IFDEF FMX}
    z1BevelRect.Offset(0, Amount);
    {$ELSE}
    OffsetRect(z1BevelRect, 0, Amount);
    {$ENDIF}

    startB0 := CalcPieStart(z0BevelRect);
    endB0 := CalcPieEnd(z0BevelRect);

    startB1 := CalcPieStart(z1BevelRect);
    endB1 := CalcPieEnd(z1BevelRect);

    hasBevel := True;
  end;

  procedure TPieDrawer.PrepareDonut(const R:TRect);
  begin
    z0DonutRect := R;
    z1DonutRect := z0DonutRect;

    {$IFDEF FMX}
    z1DonutRect.Offset(0, zPos);
    {$ELSE}
    OffsetRect(z1DonutRect, 0, zPos);
    {$ENDIF}

    startD0 := CalcPieStart(z0DonutRect);
    endD0 := CalcPieEnd(z0DonutRect);

    startD1 := CalcPieStart(z1DonutRect);
    endD1 := CalcPieEnd(z1DonutRect);

    isDonut := True;
  end;

  procedure TPieDrawer.DrawLighting(const AStyle:TEdgeStyle);
  begin
    if hasBevel then
       ICanvas.DrawLighting(AStyle, zPos, z0BevelRect, z1BevelRect,
                    z0DonutRect,iStartAngle, iEndAngle, middle0, isDonut)
    else
       ICanvas.DrawLighting(AStyle, zPos, z0Rect, z1Rect,
                    z0DonutRect,iStartAngle, iEndAngle, middle0, isDonut);
  end;

  {$IFDEF PIELIST}
  Procedure TPieDrawer.DrawPoints;
  begin
    {$IFDEF FMX}
    ICanvas.Polygon(list);
    {$ELSE}
    ICanvas.PolygonFloat(list);
    {$ENDIF}
  end;

  procedure TPieDrawer.Loop(Start,Stop:Integer; const R:TRect);

    procedure AddPoint(t:Integer);
    begin
      list[count]:=ICanvas.PointFromCircle(R, t, zPos);
      Inc(count);
    end;

  var t : Integer;
  begin
    if Start>Stop then
       for t := Start downto Stop do AddPoint(t)
       else
       for t := Start to Stop do AddPoint(t);
  end;
  {$ENDIF}

  procedure TPieDrawer.DrawArc(const R:TRect; const PFrom,PTo:TPoint; Inverted:Boolean=False);
  begin
    {$IFNDEF FMX}
    if (PFrom.X<>PTo.X) or (PFrom.Y<>PTo.Y) then
    with ICanvas.CalcRect3D(R,zPos) do
         ICanvas.ArcTo(Left,Top,Right,Bottom,PFrom.X,PFrom.Y,PTo.X,PTo.Y, Inverted);
    {$ENDIF}
  end;

  function TPieDrawer.BeginUsingArcs:Boolean;
  begin
    result:=Pie3DUsingArcs {$IFNDEF FMX}{$IFDEF MSWINDOWS}and BeginPath(ICanvas.Handle){$ENDIF}{$ENDIF};
  end;

  procedure TPieDrawer.EndUsingArcs;
  begin
    {$IFDEF FMX}
    //TPathData.Add ClosePolygon;
    //FCanvas.FillPath(Path);
    //FCanvas.DrawPath(Path);
    {$ELSE}
    {$IFDEF MSWINDOWS}
    EndPath(ICanvas.Handle);
    StrokeAndFillPath(ICanvas.Handle);
    {$ENDIF}
    {$ENDIF}
  end;

  procedure TPieDrawer.DrawLoops(const Start,Stop:Integer; const R0,R1:TRect);
  {$IFNDEF PIELIST}
  var p1,p2,p4 : TPoint;
  {$ENDIF}
  begin
    {$IFDEF PIELIST}
    count:=0;
    SetLength(list, 2 + 2*Abs(Stop-Start));
    Loop(Start,Stop,R0);
    Loop(Stop,Start,R1);
    DrawPoints;
    {$ELSE}

    if BeginUsingArcs then
    begin
      p1:=TeePoint(ICanvas.PointFromCircle(R0, Start, zPos));
      MoveTo(p1);

      p2:=TeePoint(ICanvas.PointFromCircle(R1, Start, zPos));
      LineTo(p2);

      DrawArc(R1,p2,TeePoint(ICanvas.PointFromCircle(R1, Stop, zPos)));

      p4:=TeePoint(ICanvas.PointFromCircle(R0, Stop, zPos));
      LineTo(p4);

      DrawArc(R0,p4,p1,True);

      EndUsingArcs;
    end;

    {$ENDIF}
  end;

  Procedure TPieDrawer.DrawBevel(const startAng, endAng:Integer);
  var tmpStartAng, tmpEndAng: Integer;
  begin
    if hasBevel then
    begin
      tmpStartAng := startAng;
      tmpEndAng := endAng;

      if tmpEndAng < tmpStartAng then
         Inc(tmpEndAng, 360);

      if BevelBright>0 then
         ICanvas.Brush.Color:=ApplyBright(ICanvas.Brush.Color,BevelBright)
      else
      if BevelBright<0 then
         ICanvas.Brush.Color:=ApplyDark(ICanvas.Brush.Color,-BevelBright);

      if not BevelBorder then
         ICanvas.Pen.Style:=psClear;

      DrawLoops(tmpStartAng,tmpEndAng,z0BevelRect,z1BevelRect);
    end;
  end;

  Procedure TPieDrawer.DrawBack(const StartAng, EndAng:Integer);
  begin
    if isDonut and (not HideSides) then
    begin
      //MM: 05-jun-12
      if (not drawEntirePie) and (start0.Y >= middle0.Y) and
          (end0.Y >= middle0.Y) and (sweepAngle < 180) then
      else
        DrawLoops(startAng,endAng,z0DonutRect,z1DonutRect);
    end;
  end;

  Procedure TPieDrawer.DrawBottom;
  begin
    if drawEntirePie then
       DrawTopBottom(iStartAngle,iEndAngle,False,middle1,z1Rect,z1DonutRect);
  end;

  Procedure TPieDrawer.DrawFront(const StartAng,EndAng:Integer);
  var tmpEndAng, tmpStartAng: Integer;

    procedure DrawUsingArcs;
    var p1,p2,p4 : TPoint;
    begin
      if hasBevel then
         p1:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z1BevelRect, tmpStartAng, zPos))
      else
         p1:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0Rect, tmpStartAng, zPos));

      ICanvas.MoveTo(p1);

      p2:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z1Rect, tmpStartAng, zPos));
      ICanvas.LineTo(p2);

      DrawArc(z1Rect,p2,{$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z1Rect, tmpEndAng, zPos)));

      if hasBevel then
         p4:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z1BevelRect, tmpEndAng, zPos))
      else
         p4:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0Rect, tmpEndAng, zPos));

      ICanvas.LineTo(p4);

      if hasBevel then
         DrawArc(z1BevelRect,p4,p1,True)
      else
         DrawArc(z0Rect,p4,p1,True);

      EndUsingArcs;
    end;

    function NormalizeAngle(const Angle:Integer):Integer;
    begin
      result:=Angle;

      while result > 360 do Dec(result, 360);
      while result < -360 do Inc(result, 360);
      if result < 0 then result:= 360 + result;
    end;

    procedure DrawDefaults(const AFrom,ATo:Integer);
    begin
      if hasBevel then
         DrawLoops(AFrom,ATo,z1BevelRect,z1Rect)
      else
         DrawLoops(AFrom,ATo,z0Rect,z1Rect);
    end;

  begin
    tmpEndAng := NormalizeAngle(endAng);
    tmpStartAng := NormalizeAngle(startAng);

    if Full360 then // Single Slice 360 degree
    begin
      DrawDefaults(0,180);
      DrawDefaults(180,360);

      if not drawEntirePie then
         DrawBevel(0,360);
    end
    else
    if (start0.Y <= middle0.Y) and (end0.Y <= middle0.Y) then
    begin
      if sweepAngle < 180 then
      begin
        if drawEntirePie then
           DrawDefaults(startAng,endAng);

        if not drawEntirePie then
           DrawBevel(startAng, endAng);
      end
      else
      begin
        DrawDefaults(startAng,endAng);

        if not drawEntirePie then
           DrawBevel(iStartAngle,iEndAngle);
      end;
    end
    else
    begin
      if (start0.Y > middle0.Y) and (end0.Y < middle0.Y) then  //right
      begin
        if drawEntirePie then
           DrawDefaults(0,tmpEndAng);

        if tmpStartAng > 0 then
           if hasBevel then
              DrawLoops(360,tmpStartAng,z1Rect,z1BevelRect)
           else
              DrawLoops(360,tmpStartAng,z1Rect,z0Rect);
      end
      else
      if (start0.Y < middle0.Y) and (end0.Y > middle0.Y) then  //left
      begin
        if drawEntirePie then
           DrawDefaults(180,tmpStartAng);

        tmpEndAng:=NormalizeAngle(iEndAngle);

        if hasBevel then
           DrawLoops(180,tmpEndAng,z1Rect,z1BevelRect)
        else
           DrawLoops(180,tmpEndAng,z1Rect,z0Rect);
      end
      else
      if (start0.Y >= middle0.Y) and (end0.Y > middle0.Y) and (sweepAngle > 180) then //front
      begin
        if hasBevel then
        begin
          DrawLoops(180,0,z1BevelRect,z1Rect);
          DrawLoops(tmpEndAng,180,z1Rect,z1BevelRect);
        end
        else
        begin
          DrawLoops(180,0,z0Rect,z1Rect);
          DrawLoops(tmpEndAng,180,z1Rect,z0Rect);
        end;

        if tmpStartAng > 0 then
           if hasBevel then
              DrawLoops(360,tmpStartAng,z1Rect,z1BevelRect)
           else
              DrawLoops(360,tmpStartAng,z1Rect,z0Rect);
      end
      else
      if (start0.Y >= middle0.Y) and (end0.Y >= middle0.Y) then
      begin
        if tmpEndAng < tmpStartAng then
           Inc(tmpEndAng, 360);

        if BeginUsingArcs then
           DrawUsingArcs
        else
           DrawDefaults(tmpStartAng,tmpEndAng);
      end;

      DrawBevel(iStartAngle, iEndAngle);
    end;
  end;

  Procedure TPieDrawer.DrawLeft;
  begin
    if (sweepAngle < 360) and (drawEntirePie or iDrawSides) then
    begin
      if isDonut then
      begin
        if hasBevel then
           ICanvas.Polygon([endD0,endB0,endB1,end1,endD1])
        else
        begin
          ICanvas.IPoints[0] := endD0;
          ICanvas.IPoints[1] := end0;
          ICanvas.IPoints[2] := end1;
          ICanvas.IPoints[3] := endD1;
          ICanvas.PolygonFour;
        end;
      end
      else
      begin
        if hasBevel then
           ICanvas.Polygon([middle0,endB0,endB1,end1,middle1])
        else
        begin
          ICanvas.IPoints[0] := middle0;
          ICanvas.IPoints[1] := end0;
          ICanvas.IPoints[2] := end1;
          ICanvas.IPoints[3] := middle1;
          ICanvas.PolygonFour;
        end;
      end;
    end;
  end;

  Procedure TPieDrawer.DrawRight;
  begin
    if (sweepAngle < 360) and (drawEntirePie or iDrawSides) then
    begin
      if isDonut then
      begin
        if hasBevel then
           ICanvas.Polygon([startD0,startB0,startB1,start1,startD1])
        else
        begin
          ICanvas.IPoints[0] := startD0;
          ICanvas.IPoints[1] := start0;
          ICanvas.IPoints[2] := start1;
          ICanvas.IPoints[3] := startD1;
          ICanvas.PolygonFour;
        end;
      end
      else
      begin
        if hasBevel then
           ICanvas.Polygon([middle0,startB0,startB1,start1,middle1])
        else
        begin
          ICanvas.IPoints[0] := middle0;
          ICanvas.IPoints[1] := start0;
          ICanvas.IPoints[2] := start1;
          ICanvas.IPoints[3] := middle1;
          ICanvas.PolygonFour;
        end;
      end;
    end;
  end;

  procedure TPieDrawer.DrawTopBottom(StartAng,EndAng:Integer; DoBevel:Boolean; const Middle:TPoint; const R,RDonut:TRect);
  var list : Array of TPointFloat;
      count : Integer;

    procedure Loop(Start,Stop:Integer; const R:TRect);

      procedure AddPoint(const AIndex:Integer);
      begin
        list[count]:=ICanvas.PointFromCircle(R, AIndex, zPos);
        Inc(count);
      end;

    var t : Integer;
    begin
      if Start>Stop then
         for t:=Start downto Stop do
             AddPoint(t)
      else
         for t:=Start to Stop do
             AddPoint(t);
    end;

  begin
    if isDonut then
       SetLength(list, 2 + ((endAng - startAng) * 2))
    else
    begin
      if sweepAngle < 360 then
         SetLength(list, (endAng - startAng) + 3)
      else
         SetLength(list, (endAng - startAng) + 1);
    end;

    count:=0;

    if (sweepAngle<360) and (not isDonut) then
    begin
      list[count]:={$IFNDEF FMX}TeePointFloat{$ENDIF}(Middle);
      Inc(count);
    end;

    if DoBevel then
       Loop(startAng,endAng,z0BevelRect)
    else
       Loop(startAng,endAng,R);

    if isDonut then
       Loop(endAng,startAng,RDonut);

    if (sweepAngle<360) and (not isDonut) then
       list[count]:={$IFNDEF FMX}TeePointFloat{$ENDIF}(Middle);

    {$IFDEF FMX}
    ICanvas.Polygon(list);
    {$ELSE}
    ICanvas.PolygonFloat(list);
    {$ENDIF}
  end;

  Procedure TPieDrawer.DrawTop;
  var p1,p2: TPoint;
  begin
    if BeginUsingArcs then
    begin
      if isDonut then
         ICanvas.MoveTo({$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0DonutRect, iStartAngle, zPos)))
      else
         ICanvas.MoveTo(middle0);

      ICanvas.LineTo(start0);

      if hasBevel then
         DrawArc(z0BevelRect,start0,{$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0BevelRect, iEndAngle, zPos)))
      else
         DrawArc(z0Rect,start0,{$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0Rect, iEndAngle, zPos)));

      if isDonut then
      begin
        p1:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0DonutRect, iEndAngle, zPos));
        ICanvas.LineTo(p1);

        p2:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(z0DonutRect, iStartAngle, zPos));
        DrawArc(z0DonutRect,p1,p2,True);
      end
      else
         ICanvas.LineTo(middle0);

      EndUsingArcs;
    end
    else
      DrawTopBottom(iStartAngle,iEndAngle,hasBevel,middle0,z0Rect,z0DonutRect);
  end;

  Procedure TPieDrawer.DoDrawSides(StartAng,EndAng:Integer);
  begin
    // For 0 size empty slices:
    if StartAng=EndAng then
       Exit;

    if end0.X > middle0.X then
    begin
      if start0.X < middle0.X then
      begin
        DrawBack(startAng, endAng);
        DrawRight;
        DrawLeft;

        if endAng-startAng > 180 then
        begin
          startAng:=Max(startAng,180);
          endAng:=Min(endAng,360);
        end;

        DrawFront(startAng, endAng);
      end
      else
      begin
        if sweepAngle > 180 then
        begin
          DrawBack(startAng, endAng);
          DrawRight;
          DrawLeft;
          DrawFront(startAng, endAng);
        end
        else
        begin
          DrawLeft;
          DrawBack(startAng, endAng);
          DrawFront(startAng, endAng);
          DrawRight;
        end;
      end;
    end
    else
    begin
      if start0.X < middle0.X then
      begin
        if sweepAngle > 180 then
        begin
          DrawBack(startAng, endAng);
          DrawLeft;
          DrawRight;
          DrawFront(startAng, endAng);
        end
        else
        begin
          DrawRight;
          DrawBack(startAng, endAng);
          DrawFront(startAng, endAng);
          DrawLeft;
        end;
      end
      else
      begin
        if sweepAngle > 180 then
        begin
          DrawBack(startAng, endAng);
          DrawLeft;
          DrawFront(startAng, endAng);
          DrawRight;
        end
        else
        begin
          DrawBack(startAng, endAng);
          DrawFront(startAng, endAng);
          DrawRight;
          DrawLeft;
        end;
      end;
    end;
  end;

  function TPieDrawer.CalcPieStart(const R:TRect):TPoint;
  begin
    result:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(R, iStartAngle, zPos));
  end;

  function TPieDrawer.CalcPieEnd(const R:TRect):TPoint;
  begin
    result:={$IFNDEF FMX}TeePoint{$ENDIF}(ICanvas.PointFromCircle(R, iEndAngle, zPos));
  end;

  procedure TPieDrawer.CalcStartEnd(const P:TPoint);
  begin
    ICanvas.Calc3DPoint(middle0, P.X, P.Y, zPos);
    ICanvas.Calc3DPoint(middle1, P.X, P.Y + zPos, zPos);

    start0 := CalcPieStart(z0Rect);
    end0 := CalcPieEnd(z0Rect);

    start1 := CalcPieStart(z1Rect);
    end1 := CalcPieEnd(z1Rect);

    sweepAngle := iEndAngle - iStartAngle;
  end;

procedure TTeeCanvas3D.Pie3D( XCenter,YCenter,XRadius,YRadius,Z0,Z1:Integer;
                              Const StartAngle,EndAngle:Double;
                              DarkSides,DrawSides:Boolean; DonutPercent:Integer=0;
                              Gradient:TCustomTeeGradient=nil;
                              BevelPercent:Integer=0;
                              EdgeStyle:TEdgeStyle=edNone;
                              Transparency:TTeeTransparency=0;
                              BevelBright:Integer=0;
                              BevelBorder:Boolean=True;
                              HideSides:Boolean=False);

  function CreateBlend(const ADrawer:TPieDrawer):TTeeBlend;
  {$IFNDEF FMX}
  var tmp : Integer;
      tmpRect : TRect;
  {$ENDIF}
  begin
    {$IFDEF FMX}
    result:=BeginBlending(TeeZeroRect,Transparency);
    {$ELSE}

    if SupportsAlpha then
       result:=BeginBlending(TeeZeroRect,Transparency)
    else
    begin
      tmp:=FPen.Width div 2;

      tmpRect:=TeeRect(ADrawer.z0Rect.Left-tmp, ADrawer.z0Rect.Top-tmp,
                       ADrawer.z0Rect.Right+tmp+1, ADrawer.z1Rect.Bottom+tmp);

      result:=BeginBlending(tmpRect,Transparency);
    end;
    {$ENDIF}
  end;

  function RectFrom(const P:TPoint; const X,Y:Integer):TRect;
  begin
    result.Left:=P.X-X;
    result.Right:=P.X+X;
    result.Top:=P.Y-Y;
    result.Bottom:=P.Y+Y;
  end;

var
  tmpP: TPoint;
  invisiblePen : Boolean;
  tmpBlend     : TTeeBlend;
  bevelAmount  : Single;
  IDrawer      : TPieDrawer;  // <-- Android needs this subclass to avoid compiler limits
begin
  IDrawer:=TPieDrawer.Create;
  IDrawer.ICanvas:=Self;

  IDrawer.Pie3DUsingArcs:=Pie3DUsingArcs;
  IDrawer.BevelBright:=BevelBright;
  IDrawer.BevelBorder:=BevelBorder;
  IDrawer.HideSides:=HideSides;

  IDrawer.zPos := z1 - z0;

  IDrawer.z0Rect := TeeRect(XCenter-XRadius, YCenter-YRadius, XCenter+XRadius, YCenter+YRadius);
  IDrawer.z1Rect := IDrawer.z0Rect;

  {$IFDEF FMX}
  IDrawer.z1Rect.Offset(0, IDrawer.zPos);
  {$ELSE}
  OffsetRect(IDrawer.z1Rect, 0, IDrawer.zPos);
  {$ENDIF}

  IDrawer.iDrawSides := drawSides;
  IDrawer.Full360 := startAngle=endAngle;

  IDrawer.iStartAngle := Round(startAngle * HalfDivPi);
  IDrawer.iEndAngle := Round(endAngle * HalfDivPi);

  if not IDrawer.Full360 then
     IDrawer.Full360:=(IDrawer.iEndAngle-IDrawer.iStartAngle)=360;

  if IDrawer.iEndAngle < IDrawer.iStartAngle then
     Inc(IDrawer.iEndAngle, 360);

  if Brush.Style=bsSolid then
     IDrawer.OldColor := Brush.Color
  else
     IDrawer.OldColor := BackColor;

  IDrawer.drawEntirePie := (Transparency > 0) and (not HideSides);

  tmpP.X:=XCenter;
  tmpP.Y:=YCenter;

  if donutPercent > 0 then
     IDrawer.PrepareDonut(RectFrom(tmpP,Round(donutPercent * xRadius * 0.01),Round(donutPercent * yRadius * 0.01)));

  if bevelPercent > 0 then
  begin
    bevelAmount := bevelPercent * IDrawer.zPos * 0.01;
    IDrawer.PrepareBevel(RectFrom(tmpP,Round(xRadius - bevelAmount),Round(yRadius - bevelAmount)),
                         Round(bevelAmount));
  end;

  IDrawer.CalcStartEnd(tmpP);

  if Transparency>0 then
     tmpBlend:=CreateBlend(IDrawer)
  else
     tmpBlend:=nil;

  try
    IDrawer.DrawBottom;

    invisiblePen:= (Pen.Color = IDrawer.OldColor);

    if darkSides then
    begin
      if Brush.Style<>bsClear then
         Brush.Color:=ApplyDark(Brush.Color, 32);

      if invisiblePen then
         Pen.Color:=ApplyDark(Pen.Color, 32);
    end;

    IDrawer.DoDrawSides(IDrawer.iStartAngle, IDrawer.iEndAngle);

    if darkSides then
    begin
      if Brush.Style=bsSolid then
         Brush.Color := IDrawer.OldColor
      else
         BackColor := IDrawer.OldColor;

      if invisiblePen then
         Pen.Color := IDrawer.OldColor;
    end;

    IDrawer.DrawTop; // Always draw Top (also when having Transparency)

    if (Assigned(Gradient) and Gradient.Visible) or (EdgeStyle<>edNone) then
       IDrawer.DrawLighting(edgeStyle);

  finally
    if Transparency>0 then
       EndBlending(tmpBlend);
  end;

  IDrawer.Free;
end;

procedure TTeeCanvas3D.Plane3D(Const A,B:TPoint; Z0,Z1:Integer);
begin
  Calc3DPoint(IPoints[0],A.X,A.Y,Z0);
  Calc3DPoint(IPoints[1],B.X,B.Y,Z0);
  Calc3DPoint(IPoints[2],B.X,B.Y,Z1);
  Calc3DPoint(IPoints[3],A.X,A.Y,Z1);
  PolygonFour;
end;

procedure TTeeCanvas3D.InternalCylinder(Vertical:Boolean; Left,Top,Right,Bottom,
                            Z0,Z1:Integer; Dark3D:Boolean; ConePercent:Integer);

  function InternalCalc3D(const P:TPoint3D):{$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF}; overload;
  begin
    {$IFDEF FMX}
    result:=Calculate3DPosition(P);
    {$ELSE}
    result.x:=P.x;
    result.y:=P.y;
    Calculate3DPosition(result,P.z);
    {$ENDIF}
  end;

  function InternalCalc3D(const x,y:Single; z:Integer):{$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF}; overload;
  begin
    {$IFDEF FMX}
    result:=Calculate3DPosition(x,y,z);
    {$ELSE}
    result.x:=x;
    result.y:=y;
    Calculate3DPosition(result,z);
    {$ENDIF}
  end;

  procedure GetMinMax(const P:TPointFloatArray; out Min,Max:Integer);
  var v : Single;
      t,L : Integer;
  begin
    L:=Length(P);

    Min:=0;
    Max:=0;

    if Vertical then
    begin
      v:=P[0].x;

      for t:=1 to L-1 do
      if P[t].X<v then
      begin
        v:=P[t].X;
        Min:=t;
      end;

      v:=P[0].x;

      for t:=1 to L-1 do
      if P[t].X>v then
      begin
        v:=P[t].X;
        Max:=t;
      end;
    end
    else
    begin
      v:=P[0].y;

      for t:=1 to L-1 do
      if P[t].Y<v then
      begin
        v:=P[t].Y;
        Min:=t;
      end;

      v:=P[0].Y;

      for t:=1 to L-1 do
      if P[t].Y>v then
      begin
        v:=P[t].Y;
        Max:=t;
      end;
    end;
  end;

var
  Poly1,
  Poly2 : Array of TPoint3D;

var Radius,
    ZRadius,
    tmpMid,
    tmpMidZ : Integer;
    Step    : Double;

  procedure DrawCover;
  var l        : Integer;
      t        : Integer;
      tmpPoly3 : TPointFloatArray;

    procedure NewPoint;
    begin
      SetLength(tmpPoly3,L+1);
      tmpPoly3[L]:=InternalCalc3D(Poly1[t]);
      Inc(L);
    end;

    function CoverAtLeftOrBottom:Boolean;
    var tmpPoly : TFourPoints;
    begin
      if Vertical then
         result:=Bottom<Top
      else
      begin
        Calc3DPoint(tmpPoly[0],0,0,0);
        Calc3DPoint(tmpPoly[1],0,10,0);
        Calc3DPoint(tmpPoly[2],0,10,10);
        result:=TeeCull(tmpPoly);

        if Right<Left then
           result:=not result;
      end;
    end;

  var tmpPoly2 : TPointFloatArray;
      tmpMin,
      tmpMax   : Integer;
  begin
    SetLength(tmpPoly2,TeeNumCylinderSides);

    if Length(Poly2)>0 then
    begin
      for t:=0 to TeeNumCylinderSides-1 do
          tmpPoly2[t]:=InternalCalc3D(Poly2[t]);

      if not CoverAtLeftOrBottom then
        {$IFDEF FMX}
        Polygon(tmpPoly2);
        {$ELSE}
        PolygonFloat(tmpPoly2);
        {$ENDIF}

    end
    else
      for t:=0 to TeeNumCylinderSides-1 do
          tmpPoly2[t]:=InternalCalc3D(Poly1[t]);

    GetMinMax(tmpPoly2,tmpMin,tmpMax);

    t:=tmpMin;
    L:=0;

    if tmpMax>=tmpMin then
    begin
      repeat
        SetLength(tmpPoly3,L+1);
        tmpPoly3[L]:=tmpPoly2[t];
        Inc(L);
        Inc(t);
      until t>tmpMax;
    end
    else
    begin
      repeat
        SetLength(tmpPoly3,L+1);
        tmpPoly3[L]:=tmpPoly2[t];
        Inc(L);
        Inc(t);
      until t>=TeeNumCylinderSides;

      t:=0;
      repeat
        SetLength(tmpPoly3,L+1);
        tmpPoly3[L]:=tmpPoly2[t];
        Inc(L);
        Inc(t);
      until t>tmpMax;
    end;

    if ConePercent=0 then
    begin
      SetLength(tmpPoly3,L+1);
      tmpPoly3[L]:=InternalCalc3D(tmpMid,Top,tmpMidZ);
    end
    else
    begin
      t:=tmpMax;

      if tmpMax>=tmpMin then
      begin
        repeat
          NewPoint;
          Dec(t);
        until t<tmpMin;
      end
      else
      begin
        repeat
          NewPoint;
          Dec(t);
        until t<0;

        t:=Length(tmpPoly2)-1;
        repeat
          NewPoint;
          Dec(t);
        until t<tmpMin;
      end;
    end;

    Gradient.Visible:=True;
    Gradient.MidColor:=clNone;

    if Dark3D then
    begin
      Gradient.StartColor:=ApplyBright(Brush.Color,DarkColorQuantity);
      Gradient.EndColor:=ApplyDark(Brush.Color,DarkerColorQuantity);
    end
    else
    begin
      Gradient.StartColor:=Brush.Color;
      Gradient.EndColor:=ApplyDark(Brush.Color,DarkColorQuantity);
    end;

    if Vertical then
       Gradient.Direction:=gdLeftRight
    else
       Gradient.Direction:=gdTopBottom;

    {$IFDEF FMX}
    Polygon(tmpPoly3);
    {$ELSE}
    PolygonFloat(tmpPoly3);
    {$ENDIF}

    tmpPoly3:=nil;

    if CoverAtLeftOrBottom then
    begin
      if Length(Poly2)>0 then
      for t:=0 to TeeNumCylinderSides-1 do
          tmpPoly2[t]:=InternalCalc3D(Poly1[t]);

      {$IFDEF FMX}
      Polygon(tmpPoly2);
      {$ELSE}
      PolygonFloat(tmpPoly2);
      {$ENDIF}
    end;

    tmpPoly2:=nil;
  end;

  Procedure CalcPointZ(Index:Integer; out X,Z:Integer);
  var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
      tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
  begin
    SinCos(Index*Step,tmpSin,tmpCos);
    X:=tmpMid+Round(tmpSin*Radius);
    Z:=tmpMidZ-Round(tmpCos*ZRadius);
  end;

var t : Integer;
begin
  if ConePercent=0 then
     ConePercent:=TeeDefaultConePercent;

  ZRadius:=(Z1-Z0) div 2;
  tmpMidZ:=(Z1+Z0) div 2;

  Step:=TwoPi/TeeNumCylinderSides;

  SetLength(Poly1,TeeNumCylinderSides);

  if Vertical then
  begin
    Radius:=(Right-Left) div 2;
    tmpMid:=(Right+Left) div 2;

    for t:=0 to TeeNumCylinderSides-1 do
    begin
      Poly1[t].Y:=Bottom;
      CalcPointZ(t,Poly1[t].X,Poly1[t].Z);
    end;
  end
  else
  begin
    Radius:=(Bottom-Top) div 2;
    tmpMid:=(Bottom+Top) div 2;

    for t:=0 to TeeNumCylinderSides-1 do
    begin
      Poly1[t].X:=Left;
      CalcPointZ(t,Poly1[t].Y,Poly1[t].Z);
    end;
  end;

  if ConePercent<>0 then
  begin
    if ConePercent<>100 then
    begin
      Radius:=Round(Radius*ConePercent*0.01);
      ZRadius:=Round(ZRadius*ConePercent*0.01);
    end;

    SetLength(Poly2,TeeNumCylinderSides);

    if Vertical then
       for t:=0 to TeeNumCylinderSides-1 do
       begin
         Poly2[t].Y:=Top;
         CalcPointZ(t,Poly2[t].X,Poly2[t].Z);
       end
    else
       for t:=0 to TeeNumCylinderSides-1 do
       begin
         Poly2[t].X:=Right;
         CalcPointZ(t,Poly2[t].Y,Poly2[t].Z);
       end;
  end;

  DrawCover;

  Poly1:=nil;
  Poly2:=nil;
end;

procedure TTeeCanvas3D.Cylinder(Vertical:Boolean; Left,Top,Right,Bottom,
                                Z0,Z1:Integer; Dark3D:Boolean=True);
Begin
  InternalCylinder(Vertical,Left,Top,Right,Bottom,Z0,Z1,Dark3D,100);
end;

procedure TTeeCanvas3D.Pyramid(Vertical:Boolean; Left,Top,Right,Bottom,
                               z0,z1:Integer; DarkSides:Boolean=True);
Var OldColor : TColor;
    P0,P1,
    P2,P3,
    PTop     : TPoint;
    tmpC     : Boolean;
begin
  if FBrush.Style=bsSolid then OldColor:=FBrush.Color
                          else OldColor:=BackColor;

  if Vertical then
  begin
    if Top<>Bottom then
    Begin
      Calc3DPoint(P0,Left,Bottom,Z0);
      Calc3DPoint(P1,Right,Bottom,Z0);
      Calc3DPoint(PTop,(Left+Right) div 2,Top,(Z0+Z1) div 2);
      Calc3DPoint(P2,Left,Bottom,Z1);
      Calc3DPoint(P3,Right,Bottom,Z1);

      Polygon([P0,PTop,P1]);

      tmpC:=TeeCull(P0,PTop,P2);
      if Top<Bottom then tmpC:=not tmpC;
      if tmpC then Polygon([ P0,PTop,P2] );

      if DarkSides then
         InternalDark(OldColor,DarkerColorQuantity);

      tmpC:=TeeCull(P1,PTop,P3);
      if Top>=Bottom then tmpC:=not tmpC;
      if tmpC then Polygon([ P1,PTop,P3 ] );

      if Top<Bottom then
      begin
        Calc3DPoint(P2,Left,Bottom,Z1);
        if TeeCull(PTop,P2,P3) then
           Polygon([ PTop,P2,P3 ] );
      end;
    end;

    Calc3DPoint(P0,Left,Bottom,Z0);
    Calc3DPoint(P1,Right,Bottom,Z0);
    Calc3DPoint(P2,Left,Bottom,Z1);

    tmpC:=TeeCull(P0,P1,P2);
    if Top>=Bottom then tmpC:=not tmpC;

    if tmpC then
    begin
      if DarkSides then
         InternalDark(OldColor,DarkColorQuantity);

      RectangleY(Left,Bottom,Right,Z0,Z1);
    end;
  end
  else
  begin
    if Left<>Right then
    Begin
      Calc3DPoint(P0,Left,Top,Z0);
      Calc3DPoint(P1,Left,Bottom,Z0);
      Calc3DPoint(PTop,Right,(Top+Bottom) div 2,(Z0+Z1) div 2);
      Calc3DPoint(P2,Left,Top,Z1);
      Calc3DPoint(P3,Left,Bottom,Z1);

      Polygon([P0,PTop,P1]);

      tmpC:=TeeCull(P2,PTop,P3);
      if Left<Right then tmpC:=not tmpC;
      if tmpC then Polygon([ P2,PTop,P3] );

      if DarkSides then
         InternalDark(OldColor,DarkColorQuantity);

      tmpC:=TeeCull(P0,PTop,P2);
      if Left<Right then tmpC:=not tmpC;
      if tmpC then Polygon([ P0,PTop,P2 ] );

      tmpC:=TeeCull(P3,PTop,P1);
      if Left<Right then tmpC:=not tmpC;
      if tmpC then Polygon([ P3,PTop,P1 ] );
    end;

    Calc3DPoint(P0,Left,Top,Z0);
    Calc3DPoint(P1,Left,Bottom,Z0);
    Calc3DPoint(P2,Left,Top,Z1);

    tmpC:=TeeCull(P0,P1,P2);
    if Left>=Right then tmpC:=not tmpC;

    if tmpC then
    begin
      if DarkSides then
         InternalDark(OldColor,DarkerColorQuantity);

      RectangleZ(Left,Top,Bottom,Z0,Z1);
    end;
  end;
end;

Procedure TCanvas3D.PyramidTrunc(Const R:TRectF; const StartZ,EndZ,TruncX,TruncZ:Double;
                                    TopCover:Boolean=True; BottomCover:Boolean=True);
var MidX : Single;
    MidZ : Single;

  procedure Calc3D(out P:TPointFloat; const X,Y,Z:Double);
  begin
    {$IFDEF FMX}
    P:=Calculate3DPosition(X,Y,Z);
    {$ELSE}
    P.x:=X;
    P.y:=Y;
    Calculate3DPosition(P,Z);
    {$ENDIF}
  end;

  Procedure PyramidFrontWall(const StartZ,EndZ:Double; IsBack:Boolean);
  var P : TFourPointFloats;
      tmp : Boolean;
  begin
    Calc3D(P[0],MidX-TruncX,R.Top,StartZ);
    Calc3D(P[1],MidX+TruncX,R.Top,StartZ);
    Calc3D(P[2],R.Right,R.Bottom,EndZ);
    Calc3D(P[3],R.Left,R.Bottom,EndZ);

    tmp:=TeeCull(P[1],P[2],P[3]);

    if IsBack then
       tmp:=not tmp;

    if R.Bottom<R.Top then
       tmp:=not tmp;

    if tmp then
       {$IFDEF FMX}
       Polygon(P);
       {$ELSE}
       PolygonFloat(P);
       {$ENDIF}
  end;

  Procedure PyramidSideWall(const HorizPos1,HorizPos2:Double; IsLeft:Boolean);
  var P : TFourPointFloats;
      tmp : Boolean;
  begin
    Calc3D(P[0],HorizPos2,R.Top,MidZ-TruncZ);
    Calc3D(P[1],HorizPos2,R.Top,MidZ+TruncZ);
    Calc3D(P[2],HorizPos1,R.Bottom,EndZ);
    Calc3D(P[3],HorizPos1,R.Bottom,StartZ);

    tmp:=TeeCull(P[3], P[2], P[1]);

    if not IsLeft then
       tmp:=not tmp;

    if R.Bottom<R.Top then
       tmp:=not tmp;

    if tmp then
       {$IFDEF FMX}
       Polygon(P);
       {$ELSE}
       PolygonFloat(P);
       {$ENDIF}
  end;

  Procedure DrawBottom;
  var P : TFourPointFloats;
  begin
    if BottomCover then
    begin
      Calc3D(P[0],R.Left,R.Bottom,StartZ);
      Calc3D(P[1],R.Right,R.Bottom,StartZ);
      Calc3D(P[2],R.Right,R.Bottom,EndZ);
      Calc3D(P[3],R.Left,R.Bottom,EndZ);

      {$IFDEF FMX}
      Polygon(P);
      {$ELSE}
      PolygonFloat(P);
      {$ENDIF}
    end;
  end;

  Procedure DrawTop;
  var P : TFourPointFloats;
  begin
    if TopCover and (TruncX<>0) then
    begin
      Calc3D(P[0],MidX-TruncX,R.Top,MidZ-TruncZ);
      Calc3D(P[1],MidX+TruncX,R.Top,MidZ-TruncZ);
      Calc3D(P[2],MidX+TruncX,R.Top,MidZ+TruncZ);
      Calc3D(P[3],MidX-TruncX,R.Top,MidZ+TruncZ);

      {$IFDEF FMX}
      Polygon(P);
      {$ELSE}
      PolygonFloat(P);
      {$ENDIF}
    end;
  end;

begin
  MidX:=(R.Left+R.Right)*0.5;
  MidZ:=(StartZ+EndZ)*0.5;

  if R.Bottom>R.Top then
     DrawBottom
  else
     DrawTop;

  if (TruncX=0) and (StartZ=EndZ) then
     PyramidFrontWall(MidZ+TruncZ,EndZ,False)
  else
  begin
    PyramidFrontWall(MidZ+TruncZ,EndZ,True);
    PyramidSideWall(R.Left,MidX-TruncX,True);

    PyramidFrontWall(MidZ-TruncZ,StartZ,False);
    PyramidSideWall(R.Right,MidX+TruncX,False);
  end;

  if R.Bottom>R.Top then
     DrawTop
  else
     DrawBottom;
end;

procedure TTeeCanvas3D.PlaneFour3D(Var Points:TFourPoints; Z0,Z1:Integer);
begin
  IPoints:=Points;

  Calc3DTPoint(IPoints[0],Z0);
  Calc3DTPoint(IPoints[1],Z0);
  Calc3DTPoint(IPoints[2],Z1);
  Calc3DTPoint(IPoints[3],Z1);

  PolygonFour;
end;

Function TTeeCanvas3D.GetPixel3D(X,Y,Z: Integer):TColor;
begin
  Calc3DPos(X,Y,Z);
  result:=GetPixel(x,y);
end;

procedure TTeeCanvas3D.SetPixel3D(X,Y,Z:Integer; Value: TColor);
{$IFNDEF FMX}
var FDC : HDC;
{$ENDIF}
begin
  Calc3DPos(X,Y,Z);

  if FPen.Width=1 then
     SetPixel(X,Y,Value)
  else
  begin { simulate a big dot pixel }
    {$IFDEF FMX}
    Pixels[X,Y]:=Value;
    {$ELSE}
    {$IFDEF MSWINDOWS}
    FDC:=FCanvas.Handle;
    Windows.MoveToEx(FDC,X,Y,nil);
    Windows.LineTo(FDC,X,Y);
    {$ELSE}
    Pixels[X,Y]:=Value;
    {$ENDIF}
    {$ENDIF}
  end;
end;

Function TTeeCanvas3D.GetSupports3DText:Boolean;
begin
  result:=False;
end;

Function TTeeCanvas3D.GetIsNoBMPGrid:Boolean;
begin
  result:=False;
end;

Function TTeeCanvas3D.GetSupportsFullRotation:Boolean;
begin
  result:=False;
end;

Function TTeeCanvas3D.GetSupportsXORMode:Boolean;
begin
  result:={$IFDEF FMX}False{$ELSE}True{$ENDIF};
end;

Function TTeeCanvas3D.GetTextAlign:TCanvasTextAlign;
begin
  {$IFDEF FMX}
  result:=FTextAlign;
  {$ELSE}
  {$IFDEF USEWINDOWS}
  {$IFNDEF OSX} // Pending: CrossVCL
  result:=Windows.GetTextAlign(FCanvas.Handle);
  {$ENDIF}
  {$ELSE}
  case FCanvas.TextStyle.Alignment of
    taRightJustify: result:=TA_RIGHT;
    taCenter : result:=TA_CENTER;
  else
    result:=TA_LEFT;
  end;
  {$ENDIF}
  {$ENDIF}
end;

Function TTeeCanvas3D.GetUseBuffer:Boolean;
begin
  result:=FBufferedDisplay;
end;

Procedure TTeeCanvas3D.SetUseBuffer(Value:Boolean);
begin
  if FBufferedDisplay<>Value then
  begin
    FBufferedDisplay:=Value;

    if (not FBufferedDisplay) and Assigned(FBitmap) then
    begin
      if not IKeepBitmap then
      begin
        DeleteBitmap;
        FCanvas:=nil;
        FDirty:=True;
      end;
    end
    else
    if FBufferedDisplay and Assigned(FBitmap) then
       SetCanvas(FBitmap.Canvas);
  end;
end;

Function TTeeCanvas3D.GetMonochrome:Boolean;
begin
  result:=FMonochrome;
end;

Procedure TTeeCanvas3D.SetBitmap(ABitmap:TBitmap);
begin
  FBitmap:=ABitmap;
end;

Procedure TTeeCanvas3D.SetMonochrome(Value:Boolean);
begin
  if FMonochrome<>Value then
  begin
    FMonochrome:=Value;
    FDirty:=True;
    if Assigned(F3DOptions) then F3DOptions.Repaint;
  end;
end;

procedure TTeeCanvas3D.Triangle3D( Const Points:TTrianglePoints3D;
                                   Const Colors:TTriangleColors3D);
var P : TTrianglePoints;
begin
  P[0]:=Calc3DTPoint3D(Points[0]);
  P[1]:=Calc3DTPoint3D(Points[1]);
  P[2]:=Calc3DTPoint3D(Points[2]);

  if Brush.Style<>bsClear then Brush.Color:=Colors[0];
  Polygon(P);
end;

procedure TTeeCanvas3D.TriangleWithZ(Const P1,P2,P3:TPoint; Z:Integer);
var Points : TTrianglePoints;
begin
  Calc3DPoint(Points[0],P1.X,P1.Y,Z);
  Calc3DPoint(Points[1],P2.X,P2.Y,Z);
  Calc3DPoint(Points[2],P3.X,P3.Y,Z);
  Polygon(Points);
end;

function TCanvas3D.GetArrowPoints(const ArrowPercent:Single;
                                     const FromPoint,ToPoint:TPoint;
                                     ArrowWidth,
                                     ArrowHeight,Z:Integer):TPointArray;
var x    : Double;
    y    : Double;
    SinA : Double;
    CosA : Double;

    Function CalcArrowPoint:TPoint;
    Begin
      result.X:=Round( x*CosA + y*SinA);
      result.Y:=Round(-x*SinA + y*CosA);
      result:=Calculate3DPosition(result,Z);
    end;

Var tmpHoriz  : Integer;
    tmpVert   : Integer;
    dx        : TCoordinate;
    dy        : TCoordinate;
    tmpHoriz4 : Double;
    xb        : Double;
    yb        : Double;
    l         : Double;

   { These are the Arrows points coordinates }
//    To3D,pc,pd,pe,pf,pg,ph:TPoint;

    (*           pc
                   |\
    ph           pf| \
      |------------   \ ToPoint
 From |------------   /
    pg           pe| /
                   |/
                 pd
    *)
begin
  dx:=ToPoint.x-FromPoint.x;
  dy:=FromPoint.y-ToPoint.y;
  l:=TeeDistance(dx,dy);

  if l>0 then  { if at least one pixel... }
  begin
    SetLength(result,7);

    tmpHoriz:=ArrowWidth;
    tmpVert :=Math.Min(Round(l),ArrowHeight);
    SinA:= dy / l;
    CosA:= dx / l;

    xb:= ToPoint.x*CosA - ToPoint.y*SinA;
    yb:= ToPoint.x*SinA + ToPoint.y*CosA;

    x := xb - tmpVert;
    y := yb - tmpHoriz*0.5;
    result[3]:=CalcArrowPoint;

    y := yb + tmpHoriz*0.5;
    result[5]:=CalcArrowPoint;

    tmpHoriz4:=tmpHoriz*(ArrowPercent*0.005);
    y := yb - tmpHoriz4;
    result[2]:=CalcArrowPoint;

    y := yb + tmpHoriz4;
    result[6]:=CalcArrowPoint;

    x := FromPoint.x*cosa - FromPoint.y*sina;
    y := yb - tmpHoriz4;
    result[1]:=CalcArrowPoint;

    y := yb + tmpHoriz4;
    result[0]:=CalcArrowPoint;

    result[4]:=Calculate3DPosition(ToPoint,Z);

    // ph0,pg1,pe2,pc3,To3D4,pd5,pf6
  end
  else
    result:=nil;
end;

Procedure TTeeCanvas3D.Arrow( Filled:Boolean;
                              Const FromPoint,ToPoint:TPoint;
                              ArrowWidth,ArrowHeight,Z0,Z1:Integer;
                              const ArrowPercent:Double);
var tmpP : TPointArray;
begin
  tmpP:=GetArrowPoints(ArrowPercent,FromPoint,ToPoint,ArrowWidth,ArrowHeight,(Z0+Z1) div 2);

  if Assigned(tmpP) then  // if at least one pixel...
  Begin
    if Filled then
       Polygon(tmpP)
    else
    begin
      Line(Calculate3DPosition(FromPoint,(Z0+Z1) div 2),tmpP[4]);
      LineTo(tmpP[5]);
      Line(tmpP[4],tmpP[3]);
    end;

    tmpP:=nil;
  end;
end;

{ TTeeNeutralCanvas }

Constructor TTeeNeutralCanvas.Create;
begin
  inherited;
  ITransp:=1;
end;

Function TTeeNeutralCanvas.BeginBlending(const R:TRect; Transparency:TTeeTransparency):TTeeBlend;

  procedure PushTransp;
  var L : Integer;
  begin
    L:=Length(ITranspStack);
    SetLength(ITranspStack,L+1);
    ITranspStack[L]:=ITransp;
  end;

begin
  PushTransp;
  ITransp:=ITransp*(100-Transparency)*0.01;

  result:=nil;  // Dummy
end;

procedure TTeeNeutralCanvas.EndBlending(const Blend:TTeeBlend);

  procedure PopTransp;
  var L : Integer;
  begin
    L:=Length(ITranspStack);

    Assert(L>0,'Canvas Transparency Stack might be corrupt');

    ITransp:=ITranspStack[L-1];
    SetLength(ITranspStack,L-1);
  end;

begin
  PopTransp;
end;

Procedure TTeeNeutralCanvas.ClipPolygon(const Points:Array of TPoint; NumPoints:Integer;
                                        DiffRegion:Boolean=False);
begin
end;

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

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

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

Procedure TTeeNeutralCanvas.Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
var t : Integer;
begin
  if Length(Points)>0 then
  begin
    MoveTo(Points[0]);

    for t:=1 to Length(Points)-1 do
        LineTo(Points[t]);
  end;
end;

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

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

procedure TTeeNeutralCanvas.SetInterCharSize(ASize:Integer);
begin
  // Not used by any derived Canvas class (yet).
end;

procedure TTeeNeutralCanvas.SetPenMode(const APen:TTeePen);
begin
  // Not used by any derived Canvas class (yet).
end;

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


{ TTeeShadow }

Constructor TTeeShadow.Create(AOnChange:TNotifyEvent);
begin
  inherited Create;

  FSmooth:=True;
  FDirection:=sdRightBottom;

  BlurSize:=4;

  InitValues(True,clSilver,0);
  FTransparency:=50;

  IOnChange:=AOnChange;
end;

Procedure TTeeShadow.Assign(Source:TPersistent);
begin
  if Source is TTeeShadow then
  With TTeeShadow(Source) do
  begin
    Self.FColor        :=Color;
    Self.FDirection    :=Direction;
    Self.FHorizSize    :=HorizSize;
    Self.FSmooth       :=Smooth;
    Self.FSmoothBlur   :=SmoothBlur;
    Self.FTransparency :=Transparency;
    Self.FVertSize     :=VertSize;
    Self.FVisible      :=Visible;
  end
  else
    inherited;
end;

procedure TTeeShadow.Changed;
begin
  if Assigned(IOnChange) then IOnChange(Self);
end;

Procedure TTeeShadow.InitValues(AVisible:Boolean; AColor:TColor; ASize:Integer);
begin
  FVisible:=AVisible;
  DefaultVisible:=FVisible;

  FColor:=AColor;
  DefaultColor:=FColor;

  FHorizSize:=ASize;
  FVertSize:=ASize;
  DefaultSize:=ASize;
end;

Procedure TTeeShadow.SetIntegerProperty(Var Variable:Integer; Const Value:Integer);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    Changed;
  end;
end;

Procedure TTeeShadow.SetClip(const Value:Boolean);
begin
  if FClip<>Value then
  begin
    FClip:=Value;
    Changed;
  end;
end;

Procedure TTeeShadow.SetColor(Value:TColor);
begin
  if FColor<>Value then
  begin
    FColor:=Value;
    Changed;
  end;
end;

procedure TTeeShadow.SetDirection(const Value: TShadowDirection);
begin
  if FDirection<>Value then
  begin
    FDirection:=Value;
    Changed;
  end;
end;

Procedure TTeeShadow.SetHorizSize(Value:Integer);
begin
  SetIntegerProperty(FHorizSize,Value);
end;

Procedure TTeeShadow.SetVertSize(Value:Integer);
begin
  SetIntegerProperty(FVertSize,Value);
end;

Procedure TTeeShadow.SetVisible(Value:Boolean);
begin
  if FVisible<>Value then
  begin
    FVisible:=Value;
    Changed;
  end;
end;

function TTeeShadow.GetSize: Integer;
begin
  result:=Math.Max(Abs(HorizSize),Abs(VertSize));
end;

procedure TTeeShadow.SetSize(const Value: Integer);
begin
  HorizSize:=Value;
  VertSize:=Value;
end;

procedure TTeeShadow.SetSmooth(const Value: Boolean);
begin
  if FSmooth<>Value then
  begin
    FSmooth:=Value;
    Changed;
  end;
end;

procedure TTeeShadow.SetSmoothBlur(const Value: Integer);
begin
  if FSmoothBlur<>Value then
  begin
    FSmoothBlur:=Value;
    Changed;
  end;
end;

procedure TTeeShadow.SetTransparency(Value: TTeeTransparency);
begin
  if FTransparency<>Value then
  begin
    FTransparency:=Value;
    Changed;
  end;
end;

type
  TCanvas3DAccess=class(TCanvas3D);

Function TTeeShadow.PrepareCanvas(const ACanvas:TCanvas3D; const R:TRect; Angle:Integer=0):Boolean;
{$IFNDEF FMX}
var tmpR : TRect;
    tmpP : TFourPoints;
    tmpW,
    tmpH : Integer;
{$ENDIF}
begin
  result:=(Color<>clNone) and ((HorizSize<>0) or (VertSize<>0));

  if result then
  begin
    ITransp:=Transparency;

    if ITransp=0 then
       ITransp:=TCanvas3DAccess(ACanvas).Transparency;

    if ITransp>0 then
    begin
      {$IFDEF FMX}
      IBlend:=ACanvas.BeginBlending(TeeZeroRect,Transparency);
      {$ELSE}
      if ACanvas.SupportsAlpha then
         IBlend:=ACanvas.BeginBlending(TeeZeroRect,Transparency)
      else
      begin
        if Smooth then
        begin
          tmpW:=2*Abs(HorizSize);
          tmpH:=2*Abs(VertSize);

          tmpR:=TeeRect(R.Left-tmpW,R.Top-tmpH,R.Right+tmpW,R.Bottom+tmpH);
        end
        else
          tmpR:=OffsetRectangle(R);

        if Angle<>0 then
        begin
          RectToFourPoints(tmpR,Angle,tmpP);
          tmpR:=PolygonBounds(tmpP);
        end;

        IBlend:=ACanvas.BeginBlending(tmpR,Transparency);
      end;
      {$ENDIF}
    end;
  end;
end;

procedure TTeeShadow.FinishBlending(const ACanvas:TTeeCanvas);
begin
  if ITransp>0 then
     ACanvas.EndBlending(IBlend);
end;

Function TeeGetDeviceCaps(Handle:TTeeCanvasHandle; Cap:Integer):Integer;
begin
  {$IFDEF FMX}
  result:=0;

//  case Cap of
//    0: result:=Platform.GetScreenSize.X;
//    1: result:=Platform.GetScreenSize.Y;
//  else
//    result:=1;
//  end;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  result:=GetDeviceCaps(Handle,Cap);
  {$ELSE}
  result:=0;
  {$ENDIF}
  {$ENDIF}
end;

function TeeBitsperPixel(AHandle:TTeeCanvasHandle):Integer;
begin
  {$IFDEF FMX}
  result:=32;
  {$ELSE}
  result:=TeeGetDeviceCaps(AHandle,BITSPIXEL)*TeeGetDeviceCaps(AHandle,PLANES);
  {$ENDIF}
end;

{$IFNDEF FMX}
{$IFNDEF LCL}
procedure TeeCalcLines(out Lines:TRGBArray; Bitmap:TBitmap);
var y   : Integer;
    Dif : TeeNativeInt;
    tmp : Integer;
begin
  Bitmap.PixelFormat:=pf24bit;

  tmp:=Bitmap.Height;

  SetLength(Lines,tmp);

  if tmp>0 then
  with Bitmap do
  begin
    Lines[0]:=ScanLine[0];

    if tmp>1 then
    begin
      Lines[1]:=ScanLine[1];

      Dif:=TeeNativeInt(Lines[1])-TeeNativeInt(Lines[0]);

      for y:=2 to tmp-1 do
          Lines[y]:=Pointer(TeeNativeInt(Lines[y-1])+Dif);
    end;
  end;
end;
{$ENDIF}
{$ENDIF}

procedure TeeCalcLines(out Lines:TRGBAArray; Bitmap:TBitmap);
{$IFDEF LCL}
begin
// TLazIntfImage.GetDataLineStart...
end;
{$ELSE}
var y   : Integer;
    Dif : TeeNativeInt;
    tmp : Integer;
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  // TODO: ??
  Lines:=nil;
  Exit;
  {$ENDIF}
  {$ENDIF}

  {$IFNDEF FMX}
  Bitmap.PixelFormat:=pf32bit;
  {$ENDIF}

  tmp:=Bitmap.Height;

  SetLength(Lines,tmp);

  if tmp>0 then
  with Bitmap do
  begin
    {$IFDEF FMX}
    {$IFNDEF D17}
    Lines[0]:=PRGBAs(ScanLine[0]);
    {$ENDIF}
    {$ELSE}
    Lines[0]:=ScanLine[0];
    {$ENDIF}

    if tmp>1 then
    begin
      {$IFDEF FMX}
      {$IFNDEF D17}
      Lines[1]:=PRGBAs(ScanLine[1]);
      {$ENDIF}
      {$ELSE}
      Lines[1]:=ScanLine[1];
      {$ENDIF}

      Dif:=TeeNativeInt(Lines[1])-TeeNativeInt(Lines[0]);

      for y:=2 to tmp-1 do
          Lines[y]:=Pointer(TeeNativeInt(Lines[y-1])+Dif);
    end;
  end;
end;
{$ENDIF}

{$IFDEF FMX}
var FBlur : TFilter;
{$ENDIF}

procedure TeeShadowSmooth(const Bitmap,Back:TBitmap;
          Left, Top, Width, Height:Integer;
          BlurSize:Integer;
          const Smoothness:Single;
          ACanvas:TCanvas3D; Clip:Boolean);
{$IFDEF LCL}
begin
// ?
end;
{$ELSE}
{$IFDEF FMX}
var tmpBlur : Single;
begin
  if not Assigned(FBlur) then
     FBlur := {$IFDEF D17}TFilterManager.{$ENDIF}FilterByName('GaussianBlur');

  tmpBlur:=BlurSize*0.05;
  if tmpBlur<0 then tmpBlur:=0 else if tmpBlur>9 then tmpBlur:=9;

  {$IFDEF UPTOXE35}
  if GlobalUseHWEffects then
  begin
    {$IFDEF D17}
    FBlur.ValuesAsBitmap['Input'] := Bitmap;
    FBlur.ValuesAsFloat['BlurAmount'] := tmpBlur;
    Bitmap.Assign(FBlur.ValuesAsBitmap['Output']);
    {$ELSE}
    FBlur.Values['Input'] := VarFromBitmap(Bitmap);
    FBlur.Values['BlurAmount'] := tmpBlur;
    Bitmap.Assign(TBitmap(VarToBitmap(FBlur.Values['Output'])));
    {$ENDIF}
  end
  else
  {$ENDIF}
  begin
    // Software mode
    Blur(ACanvas.ReferenceCanvas, Bitmap, Trunc(tmpBlur));
  end;

  ACanvas.Draw(Left,Top,Bitmap);
end;
{$ELSE}

var
  r,g,b : Integer;
  WeightSum    : Integer;
  InvWeightSum : Double;

const
  Inv255=1/255;

var
  tmpSmoothness : Single;
  Weights : packed Array[0..16383] of Integer;

  procedure CalcWeights(Len:Integer);
  const
    WeightFactor=100000;

  var i,
      len2 : Integer;
      tmpLen : Single;
  begin
    len2:=len*2+1;

    Weights[len]:=WeightFactor;

    for i:=0 to len-1 do
    begin
      if len=0 then
         Weights[i]:=WeightFactor
      else
      begin
        tmpLen:=-Sqr(i-len) / len;

         Weights[i]:=Round(WeightFactor *
                     Math.Power(tmpSmoothness, tmpLen));
      end;

      Weights[len2-i-1]:=Weights[i];
    end;
  end;

var t : packed Array of packed Array of TRGB;
    w, h,
    row, col,
    x, y,
    tmpMinCol,
    tmpMaxCol,
    tmpMinRow,
    tmpMaxRow,
    i,
    hr2,
    vr2 : Integer;

    tmp,
    tmpEnd   : Integer;

    {$IFNDEF FMX}
    Lines24,
    IBackLines24 : TRGBArray;
    {$ENDIF}

    Lines32,
    IBackLines32 : TRGBAArray;

    tmpBitmap32,
    tmpBack32 : Boolean;

    tmpDC : TTeeCanvasHandle;
begin
  {$IFNDEF FMX}
  {$IFNDEF LCL}
  {$IFDEF OSX}
  Exit;  // Pending: CrossVCL
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}

  w:=Width;
  h:=Height;

  tmpBitmap32:=TeeBitsperPixel(Bitmap.Canvas.Handle)=32;

  if tmpBitmap32 then
     TeeCalcLines(Lines32,Bitmap)
  {$IFNDEF FMX}
  {$IFNDEF LCL}
  else
     TeeCalcLines(Lines24,Bitmap)
  {$ENDIF}
  {$ENDIF}
  ;

  SetLength(t,h,w);

  tmpSmoothness:=0.01*(Smoothness+180);

  hr2:=(BlurSize*2 + 1);
  vr2:=hr2;

  CalcWeights(BlurSize);

  if Left<0 then tmpMinCol:= -Left
            else tmpMinCol:=0;

  if Top<0 then tmpMinRow:= -Top
           else tmpMinRow:=0;

  if Assigned(Back) then
  begin
    if w+Left >= Back.Width then
       tmpMaxCol:=Back.Width-Left-1
    else
       tmpMaxCol:=w-1;

    if h+Top >= Back.Height then
       tmpMaxRow:=Back.Height-Top-1
    else
    if h+Top < 0 then
       tmpMaxRow:=0
    else
       tmpMaxRow:=h-1;
  end
  else
  begin
    tmpMaxCol:=w-1;
    tmpMaxRow:=h-1;
  end;

  //wMinusHr2:=w-hr2;

  //hMinusVr2:=h-vr2;
  //if hMinusVr2+Top<0 then
  //   hMinusVr2:=-Top;

  // Horizontal
  for row:=tmpMinRow to tmpMaxRow do
  begin
    for col:=tmpMinCol to tmpMaxCol do
    begin
      r:=0;
      g:=0;
      b:=0;
      WeightSum:=0;

      x:=col-BlurSize;

      if x < 0 then
      begin
        i:= -x;
        x:=0;
      end
      else
        i:=0;

      while i < hr2 do
      begin
        if x >= w then
           break;

        tmp:=Weights[i];

        {$IFOPT R+}
        {$DEFINE WASRANGE}
        {$R-}
        {$ENDIF}

        if tmpBitmap32 then
        with Lines32[row,x] do
        begin
          Inc(r, Red * tmp);
          Inc(g, Green * tmp);
          Inc(b, Blue * tmp);
        end
        {$IFNDEF FMX}
        else
        with Lines24[row,x] do
        begin
          Inc(r, Red * tmp);
          Inc(g, Green * tmp);
          Inc(b, Blue * tmp);
        end
        {$ENDIF}
        ;

        {$IFDEF WASRANGE}
        {$R+}
        {$ENDIF}

        Inc(WeightSum,tmp);

        Inc(i);
        Inc(x);
      end;

      with t[row, col] do
      if WeightSum<>0 then
      begin
        Red:= r div WeightSum;
        Green:= g div WeightSum;
        Blue:= b div WeightSum;
      end
    end;
  end;

  if Assigned(Back) then
  begin
    tmpBack32:=TeeBitsperPixel(Back.Canvas.Handle)=32;

    if tmpBack32 then
       TeeCalcLines(IBackLines32,Back)
    {$IFNDEF FMX}
    {$IFNDEF LCL}
    else
       TeeCalcLines(IBackLines24,Back)
    {$ENDIF}
    {$ENDIF};
  end
  else
    tmpBack32:=False;

  if Clip then
     tmpDC:=ACanvas.Handle
  else
     tmpDC:=0;

  // Vertical
  for col:=tmpMinCol to tmpMaxCol do
  begin
    for row:=tmpMinRow to tmpMaxRow do
    begin
      {$IFNDEF FMX}
      if (not Clip) or PtVisible(tmpDC,col+Left,row+Top) then  // clip test (slow!)
      {$ENDIF}
      begin
        r:=0;
        g:=0;
        b:=0;
        WeightSum:=0;

        y:=row-BlurSize;

        if y < 0 then
        begin
          i:= -y;
          y:=0;
        end
        else
          i:=0;

        tmpEnd:=tmpMaxRow-y;
        if tmpEnd>vr2 then
           tmpEnd:=vr2;

        while i < tmpEnd do
        begin
          tmp:=Weights[i];

          with t[y, col] do
          begin
            Inc(r, Red * tmp);
            Inc(g, Green * tmp);
            Inc(b, Blue * tmp);
          end;

          Inc(WeightSum,tmp);

          Inc(i);
          Inc(y);
        end;

        if WeightSum<>0 then // TV52012992
        begin
          if Assigned(Back) then
          begin
             {$IFOPT R+}
             {$DEFINE WASRANGE}
             {$R-}
             {$ENDIF}

             if tmpBack32 then
             with IBackLines32[row+Top,col+Left] do
             begin
                if (r=g) and (r=b) then
                begin
                  if (Red=Green) and (Red=Blue) then
                  begin
                    Red  :=Round(Inv255*Red*r/WeightSum);
                    Green:=Red;
                    Blue :=Red;
                  end
                  else
                  begin
                    InvWeightSum:= r*Inv255/WeightSum;

                    Red  :=Round(InvWeightSum * Red);
                    Green:=Round(InvWeightSum * Green);
                    Blue :=Round(InvWeightSum * Blue);
                  end;
                end
                else
                begin
                  InvWeightSum:= Inv255/WeightSum;

                  Red  :=Round(r * InvWeightSum * Red);
                  Green:=Round(g * InvWeightSum * Green);
                  Blue :=Round(b * InvWeightSum * Blue);
                end;
             end
             {$IFNDEF FMX}
             else
             with IBackLines24[row+Top,col+Left] do
             begin
                if (r=g) and (r=b) then
                begin
                  InvWeightSum:= r*Inv255/WeightSum;

                  Red  :=Round(InvWeightSum * Red);
                  Green:=Round(InvWeightSum * Green);
                  Blue :=Round(InvWeightSum * Blue);
                end
                else
                begin
                  InvWeightSum:= Inv255/WeightSum;

                  Red  :=Round(r * InvWeightSum * Red);
                  Green:=Round(g * InvWeightSum * Green);
                  Blue :=Round(b * InvWeightSum * Blue);
                end;
             end
             {$ENDIF}

             {$IFDEF WASRANGE}
             {$R+}
             {$ENDIF}
          end
          else
          begin
            InvWeightSum:=1/WeightSum;

            if tmpBitmap32 then
            with Lines32[row,col] do
            begin
              Red  :=Round(r * InvWeightSum);
              Green:=Round(g * InvWeightSum);
              Blue :=Round(b * InvWeightSum);
            end
            {$IFNDEF FMX}
            else
            with Lines24[row,col] do
            begin
              Red  :=Round(r * InvWeightSum);
              Green:=Round(g * InvWeightSum);
              Blue :=Round(b * InvWeightSum);
            end
            {$ENDIF};
          end;
        end;
      end;
    end;
  end;

  t:=nil;

  Lines32:=nil;
  IBackLines32:=nil;

  {$IFNDEF FMX}
  Lines24:=nil;
  IBackLines24:=nil;
  {$ENDIF}
end;
{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}
{$IFNDEF IOS}
{$IFNDEF ANDROID}
procedure TTeeShadow.InternalDrawSmooth(const ACanvas:TCanvas3D; const Rect:TRect;
                                Ellipse:Boolean;
                                RoundSize:Integer;
                                const P:Array of TPoint;
                                DonutPercent:Integer=0;
                                const StartAngle:Double=0;
                                const EndAngle:Double=0);
var tmpR : TRect;
    ISmoothBitmap : TBitmap;

  procedure DrawEllipse;
  begin
    {$IFDEF FMX}
    ISmoothBitmap.Canvas.FillEllipse(tmpR,1);
    {$ELSE}
    ISmoothBitmap.Canvas.Ellipse(tmpR)
    {$ENDIF}
  end;

var tmpW,
    tmpH : TCoordinate;
    t    : Integer;

    {$IFDEF FMX}
    FMXP : TPathData;
    {$ENDIF}

    tmpMinW,
    tmpMinH : Integer;
    tmpP    : TPointArray;
begin
  tmpMinW:=BlurSize {Max(4,tmpHorizSize)};
  tmpMinH:=BlurSize {Max(4,tmpVertSize)};

  if Length(P)>0 then
  begin
    SetLength(tmpP,Length(P));

    for t:=0 to Length(tmpP)-1 do
        tmpP[t]:=P[t];

    // Calc min XY
    tmpW:=tmpP[0].X;
    tmpH:=tmpP[0].Y;

    for t:=1 to Length(tmpP)-1 do
    begin
      if tmpP[t].X<tmpW then tmpW:=tmpP[t].X;
      if tmpP[t].Y<tmpH then tmpH:=tmpP[t].Y;
    end;

    // Subtract min xy origin
    for t:=0 to Length(tmpP)-1 do
    with tmpP[t] do
    begin
      X:=X-tmpW+1;
      Y:=Y-tmpH+1;
    end;

    with PolygonBounds(tmpP) do
    begin
      tmpW:=Max(1,Right-Left+tmpMinW);
      tmpH:=Max(1,Bottom-Top+tmpMinH);
    end;
  end
  else
  begin
    tmpW:=Max(1,Rect.Right-Rect.Left {+tmpMinW});
    tmpH:=Max(1,Rect.Bottom-Rect.Top {+tmpMinH});
  end;

  {$IFDEF FMX}
  if (tmpW>$4000) or (tmpH>$4000) then
     Exit;
  {$ENDIF}

  ISmoothBitmap:=TBitmap.Create{$IFNDEF D19}{$IFDEF FMX}(0,0){$ENDIF}{$ENDIF};
  // ISmoothBitmap.PixelFormat:=TeePixelFormat;

  TeeSetBitmapSize(ISmoothBitmap,
                    {$IFDEF FMX}Round{$ENDIF}(tmpW),
                    {$IFDEF FMX}Round{$ENDIF}(tmpH));

  tmpR:=TeeRect(tmpMinW div 2,tmpMinH div 2,
                ISmoothBitmap.Width-(tmpMinW div 2),
                ISmoothBitmap.Height-(tmpMinH div 2));

  with ISmoothBitmap.Canvas do
  begin
    {$IFDEF FMX}
    BeginScene;
    Fill.Color:=Color;
    {$ELSE}
    Brush.Color:=Color;
    Pen.Style:=psClear;
    {$ENDIF}
  end;

  if Length(tmpP)>0 then
     {$IFDEF FMX}
     begin
       FMXP:=PointsPath(tmpP,True);
       ISmoothBitmap.Canvas.FillPath(FMXP,1);
       FMXP.Free;
     end
     {$ELSE}
     ISmoothBitmap.Canvas.Polygon(tmpP)
     {$ENDIF}
  else
  if Ellipse then
  begin
    DrawEllipse;

    if DonutPercent<>0 then
    begin
      ISmoothBitmap.Canvas.{$IFDEF FMX}Fill{$ELSE}Brush{$ENDIF}.Color:=clWhite;
      {$IFDEF FMX}InflateRectF{$ELSE}InflateRect{$ENDIF}(tmpR,-Round((100-DonutPercent)*(Rect.Right-Rect.Left)*0.005),
                       -Round((100-DonutPercent)*(Rect.Bottom-Rect.Top)*0.005));
      DrawEllipse;
    end;
  end
  else
  {$IFDEF FMX}
  ISmoothBitmap.Canvas.FillRect(tmpR,RoundSize div 2,RoundSize div 2,AllCorners,1);
  {$ELSE}
  if RoundSize=0 then
     ISmoothBitmap.Canvas.FillRect(tmpR)
  else
    with tmpR do
       ISmoothBitmap.Canvas.RoundRect(Left,Top,Right,Bottom,RoundSize,RoundSize);
  {$ENDIF}

  {$IFDEF FMX}
  ISmoothBitmap.Canvas.EndScene;
  {$ENDIF}

  with Rect{$IFDEF FMX}.Round{$ENDIF} do
    TeeShadowSmooth(ISmoothBitmap,TTeeCanvas3D(ACanvas).FBitmap,
                    Left {$IFDEF FMX}+1{$ENDIF},
                    Top {$IFDEF FMX}+1{$ENDIF},
                    {$IFDEF FMX}Round{$ENDIF}(tmpW),
                    {$IFDEF FMX}Round{$ENDIF}(tmpH),
                    BlurSize,SmoothBlur,ACanvas,Clip);

  tmpP:=nil;
  ISmoothBitmap.Free;
end;
{$ENDIF}
{$ENDIF}
{$ENDIF}

procedure TTeeShadow.Hide;
begin
  Visible:=False;
end;

function TTeeShadow.ShouldDraw:Boolean;
begin
  result:=Visible and ((HorizSize<>0) or (VertSize<>0));
end;

procedure TTeeShadow.Draw(const ACanvas:TCanvas3D; const P:Array of TPoint; Count:Integer);
var R : TRect;
    t : Integer;
    tmp : TPointArray;
begin
  SetLength(tmp,Count);
  try
    for t:=0 to Count-1 do
    begin
      tmp[t].X:=P[t].X+HorizSize;
      tmp[t].Y:=P[t].Y+VertSize;
    end;

    if ACanvas.SupportsAlpha then
       R:=TeeZeroRect
    else
       R:=PolygonBounds(tmp);

    if PrepareCanvas(ACanvas,R) then
    begin
      ACanvas.SmoothShadow(Self,R,False,0,tmp);
      FinishBlending(ACanvas);
    end;
  finally
    tmp:=nil;
  end;
end;

procedure TTeeShadow.Draw(const ACanvas: TCanvas3D; const P:Array of TPoint);
begin
  Draw(ACanvas,P,Length(P));
end;

Procedure TTeeShadow.RectToFourPoints(Const ARect:TRect; const Angle:Double; out P:TFourPoints);
var tmpSin    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos    : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCenter : TPoint;

    {$IFDEF FMX}
    Procedure RotatePoint(out P:TPoint; const AX,AY:Single);
    begin
      P.X:=tmpCenter.x+(  AX*tmpCos + AY*tmpSin );
      P.Y:=tmpCenter.y+( -AX*tmpSin + AY*tmpCos );
    end;
    {$ELSE}
    Procedure RotatePoint(out P:TPoint; const AX,AY:Integer);
    begin
      P.X:=tmpCenter.x+Round(  AX*tmpCos + AY*tmpSin );
      P.Y:=tmpCenter.y+Round( -AX*tmpSin + AY*tmpCos );
    end;
    {$ENDIF}

var tmp : Double;
    tmpRect : TRect;
begin
  tmpCenter:=RectCenter(ARect);
  tmp:=Angle*TeePiStep;
  SinCos(tmp,tmpSin,tmpCos);
  tmpRect:=ARect;

  With tmpRect do
  begin
    Left:=Left-tmpCenter.X;
    Right:=Right-tmpCenter.X;
    Top:=Top-tmpCenter.Y;
    Bottom:=Bottom-tmpCenter.Y;

    RotatePoint(P[0],Left,Top);
    RotatePoint(P[1],Right,Top);
    RotatePoint(P[2],Right,Bottom);
    RotatePoint(P[3],Left,Bottom);
  end;
end;

function TTeeShadow.OffsetRectangle(const R:TRect):TRect;
var tmpW,
    tmpH : Integer;
begin
  result:=R;

  tmpW:=HorizSize;
  tmpH:=VertSize;

  if Direction=sdCenter then
     InflateRect(result,Abs(tmpW),Abs(tmpH))
  else
  begin
    case Direction of
      sdLeftTop    : begin tmpW:=-tmpW; tmpH:=-tmpH; end;
      sdLeftBottom : tmpW:=-tmpW;
      sdRightTop   : tmpH:=-tmpH;
    end;

    {$IFDEF FMX}
    result.Offset(tmpW,tmpH);
    {$ELSE}
    OffsetRect(result,tmpW,tmpH);
    {$ENDIF}
  end;
end;

procedure TTeeShadow.Draw(const ACanvas:TCanvas3D; Const Rect:TRect;
                          RoundSize:Integer=0; Angle:Integer=0);

var OldPen  : TPenStyle;
    OldPenC : TColor;
    tmpR    : TRect;
    tmpP    : TFourPoints;
begin
  OldPen:=ACanvas.Pen.Style;
  OldPenC:=ACanvas.Pen.Color;

  if PrepareCanvas(ACanvas,Rect,Angle) then
  begin
    tmpR:=OffsetRectangle(Rect);

    if Angle=0 then
       ACanvas.SmoothShadow(Self,tmpR,False,RoundSize,[])
    else
    begin
      RectToFourPoints(tmpR,Angle,tmpP);
      ACanvas.SmoothShadow(Self,PolygonBounds(tmpP),False,RoundSize,tmpP);
    end;

    FinishBlending(ACanvas);
  end;

  ACanvas.Pen.Style:=OldPen;
  ACanvas.Pen.Color:=OldPenC;
end;

procedure TTeeShadow.DrawEllipse(const ACanvas: TCanvas3D; const Rect: TRect;
                                 DonutPercent:Integer=0;
                                 const StartAngle:Double=0; const EndAngle:Double=360);
begin
  if PrepareCanvas(ACanvas,Rect) then
  begin
    ACanvas.SmoothShadow(Self,OffsetRectangle(Rect),True,0,[],DonutPercent,StartAngle,EndAngle);
    FinishBlending(ACanvas);
  end;
end;

procedure TTeeShadow.Show;
begin
  Visible:=True;
end;

function TTeeShadow.IsColorStored: Boolean;
begin
  result:=FColor<>DefaultColor;
end;

function TTeeShadow.IsHorizStored: Boolean;
begin
  result:=FHorizSize<>DefaultSize;
end;

function TTeeShadow.IsVertStored: Boolean;
begin
  result:=FVertSize<>DefaultSize;
end;

function TTeeShadow.IsVisibleStored: Boolean;
begin
  result:=FVisible<>DefaultVisible;
end;

{ TTeeGradientColor }

procedure TTeeGradientColor.Assign(Source:TPersistent);
begin
  if Source is TTeeGradientColor then
  with TTeeGradientColor(Source) do
  begin
    Self.FColor:=FColor;
    Self.FOffset:=FOffset;

    {$IFNDEF FMX}
    Self.FTransp:=FTransp;
    {$ENDIF}
  end
  else
    inherited;
end;

function TTeeGradientColor.GetTransp:TTeeTransparency;
begin
  {$IFDEF FMX}
  result:=TTeeCanvas.ColorAlpha(FColor);
  {$ELSE}
  result:=FTransp;
  {$ENDIF}
end;

function TTeeGradientColor.IsTranspStored:Boolean;
begin
  {$IFDEF FMX}
  result:=False;
  {$ELSE}
  result:=FTransp<>0;
  {$ENDIF}
end;

procedure TTeeGradientColor.SetTransp(const Value:TTeeTransparency);
begin
  {$IFDEF FMX}
  FColor:=TTeeCanvas.ColorFrom(FColor,Value);
  {$ELSE}
  FTransp:=Value;
  {$ENDIF}
end;

{ TTeeGradientColors }

Function TTeeGradientColors.Get(Index:Integer):TTeeGradientColor;
begin
  while Count<=Index do Add;
  result:=TTeeGradientColor(GetItem(Index));
end;

Procedure TTeeGradientColors.Put(Index:Integer; Const Value:TTeeGradientColor);
begin
  while Count<=Index do Add;
  SetItem(Index,Value);
end;

procedure TTeeGradientColors.Update(Item: TCollectionItem);
begin
  inherited;
  Repaint;
end;

procedure TTeeGradientColors.Repaint;
var tmp : TPersistent;
begin
  tmp:={$IFDEF D7}Owner{$ELSE}GetOwner{$ENDIF};
  if Assigned(tmp) then
     TCustomTeeGradient(tmp).DoChanged;
end;

function TTeeGradientColors.Add(const AOffset:Double; const AColor:TColor; ATransparency:TTeeTransparency=0):TTeeGradientColor;
var t : Integer;
    tmp : Double;
begin
  // Restrict Offset between 0 and 1:
  tmp:=AOffset;
  if tmp<0 then tmp:=0 else
  if tmp>1 then tmp:=1;

  // Find position (ordered ascending by Offset):
  t:=0;

  while t<Count do
    if Item[t].FOffset>tmp then
       break
    else
       Inc(t);

  // Insert at correct position:
  result:=Insert(t) as TTeeGradientColor;

  // Set properties:
  with result do
  begin
    FOffset:=tmp;
    FColor:=AColor;

    {$IFNDEF FMX}
    FTransp:=ATransparency;
    {$ENDIF}
  end;
end;

{ TCustomTeeGradient }

Constructor TCustomTeeGradient.Create(ChangedEvent:TNotifyEvent);
Begin
  inherited Create;
  InitDefaults;
  IChanged:=ChangedEvent;
End;

Destructor TCustomTeeGradient.Destroy;
begin
  FreeAndNil(FSub);
  FColors.Free;
  inherited;
end;

function TCustomTeeGradient.GetColors:TTeeGradientColors;
begin
  if not Assigned(FColors) then
  begin
    FColors:=TTeeGradientColors.Create(Self,TTeeGradientColor);
    ResetColors;
  end;

  result:=FColors;
end;

procedure TCustomTeeGradient.ResetColors;
var Old : TNotifyEvent;
begin
  if Assigned(FColors) then
  begin
    Old:=IChanged;
    IChanged:=nil;

    FColors.Clear;
    FColors.Add(0,IDefStart);
    FColors.Add(1,IDefEnd);

    IChanged:=Old;
  end;
end;

procedure TCustomTeeGradient.InitDirection(const ADirection:TGradientDirection);
begin
  FDirection:=ADirection;
  IDefDirection:=FDirection;
end;

procedure TCustomTeeGradient.InitDefaults;
begin
  FVisible:=False;

  InitDirection(gdTopBottom);

  IDefStart:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};
  IDefEnd:={$IFDEF FMX}TAlphaColors.Yellow{$ELSE}clYellow{$ENDIF};
  IDefMid:=clNone;
  IMidColor:=IDefStart;

  if Assigned(FColors) then
     ResetColors;

  FBalance    :=50;
  FAngle      :=0;
  FRadialX    :=0;
  FRadialY    :=0;
end;

Function TCustomTeeGradient.GetStartColor:TColor;
begin
  if Assigned(FColors) then
     if FColors.Count>0 then
        result:=FColors[0].Color
     else
        result:=clNone
  else
     result:=IDefStart;
end;

Function TCustomTeeGradient.GetEndColor:TColor;
var L : Integer;
begin
  if Assigned(FColors) then
  begin
    L:=FColors.Count;

    if L>0 then
       result:=FColors[L-1].Color
    else
       result:=clNone;
  end
  else
    result:=IDefEnd;
end;

Function TCustomTeeGradient.GetMidColor:TColor;
begin
  if Assigned(FColors) then
     if {IHasMiddle and} (FColors.Count>2) then
        result:=FColors[1].Color
     else
        result:=clNone
  else
     result:=IDefMid;
end;

Procedure TCustomTeeGradient.DoChanged;
begin
  if Assigned(IChanged) then IChanged(Self);
end;

Procedure TCustomTeeGradient.SetVisible(Value:Boolean);
Begin
  if FVisible<>Value then
  begin
    FVisible:=Value;

    {$IFDEF FMX}
    if Assigned(IBrush) then
       if FVisible then
          IBrush.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF}
       else
          IBrush.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
    {$ENDIF}

    DoChanged;
  end;
End;

Procedure TCustomTeeGradient.SetAngle(Value:Integer);
begin
  SetIntegerProperty(FAngle,Value);
end;

Procedure TCustomTeeGradient.SetBalance(Value:Integer);
begin
  SetIntegerProperty(FBalance,Value);
end;

procedure TCustomTeeGradient.SetColors(const Value:TTeeGradientColors);
begin
  if Assigned(Value) then
     Colors.Assign(Value)
  else
     FreeAndNil(FColors);
end;

Procedure TCustomTeeGradient.SetDirection(Value:TGradientDirection);
Begin
  if FDirection<>Value then
  Begin
    FDirection:=Value;
    DoChanged;
  end;
End;

Procedure TCustomTeeGradient.SetIntegerProperty(Var Variable:Integer; Value:Integer);
begin
  if Variable<>Value then
  begin
    Variable:=Value;
    DoChanged;
  end;
end;

Procedure TCustomTeeGradient.SetStartColor(Value:TColor);
Begin
  Colors[0].Color:=Value;
  DoChanged;
End;

Procedure TCustomTeeGradient.SetEndColor(Value:TColor);
Begin
  Colors[Colors.Count-1].Color:=Value;

  if FStartIsEnd then
     Colors[0].Color:=Value;

  DoChanged;
End;

Procedure TCustomTeeGradient.SetMidColor(Value:TColor);
var L : Integer;
Begin
  L:=Colors.Count;

  if Value=clNone then
  begin
    if L>2 then // Remove mid color
    begin
      Colors.Delete(1);
      DoChanged;
    end;
  end
  else
  begin
    if L<3 then // Add mid color
       (Colors.Insert(1) as TTeeGradientColor).FOffset:=0.5;

    if Colors[1].FColor<>Value then
    begin
      Colors[1].FColor:=Value;
      IMidColor:=Value;

      DoChanged;
    end;
  end;
end;

Procedure TCustomTeeGradient.Assign(Source:TPersistent);
Begin
  if Source is TCustomTeeGradient then
  with TCustomTeeGradient(Source) do
  Begin
    if Source=Self then Exit;

    Self.FAngle     :=FAngle;
    Self.FBalance   :=FBalance;
    Self.FDirection :=FDirection;
    Self.FRadialX   :=FRadialX;
    Self.FRadialY   :=FRadialY;

    Self.Colors     :=FColors;

    Self.FStartIsEnd:=FStartIsEnd;

    if Assigned(FSub) then
      Self.SubGradient:=FSub
    else
      FreeAndNil(FSub);

    Self.FVisible   :=FVisible;
    //Self.IHasMiddle :=IHasMiddle;
  end
  else inherited;
end;

function TCustomTeeGradient.GetSub:TSubGradient;
begin
  if not Assigned(FSub) then
  begin
    FSub:=TSubGradient.Create(IChanged);
    FSub.FTransparency:=50;
  end;

  result:=FSub;
end;

function TCustomTeeGradient.IsColorsStored:Boolean;

  function IsNotDefault(AItem:TTeeGradientColor; const AOffset:Double):Boolean;
  begin
    result:=(AItem.Offset<>AOffset){$IFNDEF FMX} or (AItem.Transparency<>0){$ENDIF};
  end;

begin
  if not Assigned(FColors) then
  begin
    result:=False;
    Exit;
  end;

  result:=Colors.Count>3; // Always save if more than 3 colors

  if not result then
     if Colors.Count=3 then
        result:=IsNotDefault(Colors[0],0) or IsNotDefault(Colors[1],0.5) or IsNotDefault(Colors[2],1)
     else
     if Colors.Count=2 then
        result:=IsNotDefault(Colors[0],0) or IsNotDefault(Colors[1],1)
     else
        result:=True;
end;

function TCustomTeeGradient.IsSubStored:Boolean;
begin
  result:=Assigned(FSub);
end;

function TCustomTeeGradient.IsVisibleStored:Boolean;
begin
  result:=FVisible<>IDefVisible;
end;

function TCustomTeeGradient.IsDirectionStored:Boolean;
begin
  result:=FDirection<>IDefDirection;
end;

function TCustomTeeGradient.IsEndStored:Boolean;
begin
  result:=Assigned(FColors) and (EndColor<>IDefEnd);
end;

function TCustomTeeGradient.IsMidStored:Boolean;
begin
  result:=Assigned(FColors) and (MidColor<>IDefMid);
end;

function TCustomTeeGradient.IsStartStored:Boolean;
begin
  result:=Assigned(FColors) and (StartColor<>IDefStart);
end;

procedure TCustomTeeGradient.SetSub(const Value:TSubGradient);
begin
  GetSub.Assign(Value);
  FSub.FTransparency:=Value.Transparency;
end;

{$IFDEF MSWINDOWS}
{$DEFINE USEFASTBRUSH}
{$ENDIF}

Procedure TCustomTeeGradient.DrawRadial(const Canvas:TTeeCanvas; Rect:TRect);
{$IFDEF FMX}
begin
  with Canvas.FCanvas do
  begin
    Fill.Kind:=TBrushKind.{$IFDEF D20}Gradient{$ELSE}bkGradient{$ENDIF};
    Fill.Gradient.Color:=StartColor;
    Fill.Gradient.Color1:=EndColor;
    Fill.Gradient.Style:=TGradientStyle.{$IFDEF D20}Radial{$ELSE}gsRadial{$ENDIF};

    FillRect(Rect,0,0,AllCorners,1);
  end;

  if Assigned(FSub) and FSub.Visible then
     DrawSubGradient(Canvas,Rect);
end;
{$ELSE}

var tmpRect : TRect;

  Procedure DoRadialGradient;
  var Step,
      SizeX,
      SizeY,
      t,
      e0,e1,e2 : Integer;
      xc,yc,
      InvStep,
      tmpD,
      tmpX,
      tmpY,
      sx,
      sy : Double;
      dr,
      dg,
      db : Double;
      OldBrushColor : TColor;
      OldPenStyle   : TPenStyle;
      {$IFDEF USEFASTBRUSH}
      IsFastBrush   : Boolean;
      tmpDc         : TTeeCanvasHandle;
      {$ENDIF}
  begin
    RectSize(tmpRect,SizeX,SizeY);

    Step:=Math.Min(255,Math.Max(SizeX,SizeY));
    InvStep:=1/Step;

    sx:=0.5*SizeX*InvStep;
    sy:=0.5*SizeY*InvStep;

    e0:=GetRValue(StartColor);
    e1:=GetGValue(StartColor);
    e2:=GetBValue(StartColor);

    if Balance=50 then
    begin
      dr:=(GetRValue(EndColor)-e0)*InvStep;
      dg:=(GetGValue(EndColor)-e1)*InvStep;
      db:=(GetBValue(EndColor)-e2)*InvStep;
    end
    else
    begin
      dr:=(GetRValue(EndColor)-e0);
      dg:=(GetGValue(EndColor)-e1);
      db:=(GetBValue(EndColor)-e2);
    end;

    OldPenStyle:=Canvas.Pen.Style;
    Canvas.Pen.Style:=psClear;

    OldBrushColor:=Canvas.Brush.Color;
    try
      Canvas.Brush.Color:=EndColor;
      Canvas.FillRect(tmpRect);

      if ((SizeX mod 2)=1) or ((SizeY mod 2)=1) then Dec(Step);

      xc:=1+RadialX+((tmpRect.Left+tmpRect.Right)*0.5);
      yc:=1+RadialY+((tmpRect.Top+tmpRect.Bottom)*0.5);

      {$IFDEF USEFASTBRUSH}
      IsFastBrush:=Assigned(@TeeSetDCBrushColor);
      if IsFastBrush then
      begin
        tmpDC:=Canvas.Handle;
        SelectObject(tmpDC,GetStockObject(DC_BRUSH));
      end
      else
        tmpDC:=0;
      {$ENDIF}

      for t:=Step downto 0 do
      begin
        if Balance=50 then
          {$IFDEF USEFASTBRUSH}
          if IsFastBrush then
             TeeSetDCBrushColor(tmpDC,RGB(e0+Round(dr*t),e1+Round(dg*t),e2+Round(db*t)))
          else
          {$ENDIF}
             Canvas.Brush.Color:=RGB(e0+Round(dr*t),e1+Round(dg*t),e2+Round(db*t))
        else
        begin
          tmpD:=TeeSigmoid(t,Balance,Step);
          {$IFDEF USEFASTBRUSH}
          if IsFastBrush then
             TeeSetDCBrushColor(tmpDC,(( e0 + Round(tmpD*dr) ) or
                              (( e1 + Round(tmpD*dg) ) shl 8) or
                              (( e2 + Round(tmpD*db) ) shl 16)))
          else
          {$ENDIF}
            Canvas.Brush.Color:=(( e0 + Round(tmpD*dr) ) or
                                (( e1 + Round(tmpD*dg) ) shl 8) or
                                (( e2 + Round(tmpD*db) ) shl 16));
        end;

        tmpX:=t*sx;
        tmpY:=t*sy;
        Canvas.Ellipse(Round(xc-tmpX),Round(yc-tmpY),Round(xc+tmpX),Round(yc+tmpY));
      end;

    finally
      Canvas.Brush.Color:=OldBrushColor;
      Canvas.Pen.Style:=OldPenStyle;
    end;
  end;

var old : Boolean;
begin
  tmpRect:=OrientRectangle(Rect);

  Canvas.ClipRectangle(tmpRect);

  old:=Canvas.Gradient.Visible;
  Canvas.Gradient.Visible:=False;

  DoRadialGradient;

  Canvas.Gradient.Visible:=old;

  Canvas.UnClipRectangle;

  if Assigned(FSub) and FSub.Visible then
     DrawSubGradient(Canvas,Rect);
end;
{$ENDIF}

{$IFNDEF FMX}
procedure TCustomTeeGradient.DrawRotated(const Canvas:TTeeCanvas; const R:TRect);
var b   : TBitmap;
    tmp : TTeeCanvas3D;
    Sin,
    Cos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    Old,
    h,
    w   : Integer;
begin
  b:=TBitmap.Create;
  try
    w:=R.Right-R.Left;
    h:=R.Bottom-R.Top;

    SinCos((360-FAngle)*TeePiStep,Sin,Cos);

    if Sin*Cos>0 then
       TeeSetBitmapSize(b,2+Abs(Round(w*Cos+h*Sin)),2+Abs(Round(w*Sin+h*Cos)))
    else
       TeeSetBitmapSize(b,2+Abs(Round(w*Cos-h*Sin)),2+Abs(Round(w*Sin-h*Cos)));

    tmp:=TTeeCanvas3D.Create;
    try
      tmp.UseBuffer:=False;
      tmp.ReferenceCanvas:=b.Canvas;

      Old:=FAngle;
      FAngle:=0;
      try
        Draw(tmp,TeeRect(b.Width,b.Height));
      finally
        FAngle:=Old;
      end;
    finally
      tmp.Free;
    end;

    TeeGradientRotate(Self,b);

    with Canvas do
    begin
      ClipRectangle(R);

      Draw(R.Left-((b.Width-w) div 2),R.Top-((b.Height-h) div 2),b);

      UnClipRectangle;
    end;
  finally
    b.Free;
  end;
end;
{$ENDIF}

procedure TCustomTeeGradient.DrawSubGradient(const Canvas:TTeeCanvas; Const Rect:TRect;
                                             RoundRectSize:Integer=0);
var tmpBlend : TTeeBlend;
begin
  tmpBlend:=Canvas.BeginBlending(Rect,FSub.Transparency);
  try
    if FSub.Direction=gdRadial then
       FSub.DrawRadial(Canvas,Rect)
    else
       FSub.Draw(Canvas,Rect,RoundRectSize);
  finally
    Canvas.EndBlending(tmpBlend);
  end;
end;

procedure TCustomTeeGradient.UseMiddleColor;
begin
  MidColor:=IMidColor;
end;

procedure TCustomTeeGradient.Draw(const Canvas:TCanvas3D; var P:TPointArray; Z:Integer; Is3D:Boolean);
var P2 : TPointArray;
    t  : Integer;
begin
  Canvas.Gradient.Assign(Self);

  if Is3D then
  begin
    SetLength(P2,Length(P));
    try
      for t:=0 to Length(P)-1 do
          P2[t]:=Canvas.Calculate3DPosition(P[t],Z);

      Canvas.Polygon(P2);
    finally
      P2:=nil;
    end;
  end
  else
    Canvas.Polygon(P);
end;

// obsolete?
Procedure TCustomTeeGradient.Draw( const Canvas:TTeeCanvas; Const Rect:TRect;
                                   RoundRectSize:Integer=0);
begin
  Canvas.Gradient.Assign(Self);

  if RoundRectSize>0 then
     Canvas.RoundRect(Rect,RoundRectSize,RoundRectSize)
  else
     Canvas.FillRect(Rect);

  if Assigned(FSub) and FSub.Visible then
     DrawSubGradient(Canvas,Rect,RoundRectSize);
end;

procedure TCustomTeeGradient.SetRadialX(const Value: Integer);
begin
  SetIntegerProperty(FRadialX,Value);
end;

procedure TCustomTeeGradient.SetRadialY(const Value: Integer);
begin
  SetIntegerProperty(FRadialY,Value);
end;

procedure TCustomTeeGradient.Draw(const Canvas: TTeeCanvas; var P: TFourPoints);
begin
  Canvas.Pen.Style:=psClear;
  Canvas.Gradient.Assign(Self);
  Canvas.Polygon(P);
end;

Procedure TCustomTeeGradient.Draw(const Canvas:TCanvas3D; var P:TFourPoints; Z:Integer);
var P2 : TFourPoints;
begin
  P2[0]:=Canvas.Calculate3DPosition(P[0],Z);
  P2[1]:=Canvas.Calculate3DPosition(P[1],Z);
  P2[2]:=Canvas.Calculate3DPosition(P[2],Z);
  P2[3]:=Canvas.Calculate3DPosition(P[3],Z);
  Draw(Canvas,P2);
end;

{ TSubGradient }
procedure TSubGradient.SetTransparency(const Value:TTeeTransparency);
begin
  if FTransparency<>Value then
  begin
    FTransparency:=Value;
    DoChanged;
  end;
end;

{ TTeeFont }

{$IFDEF FMX}
procedure TTeeFont.Changed;
begin
  if Assigned(OnChanged) then
    OnChanged(Self);
end;

function TTeeFont.GetColor:TColor;
begin
  result:=FBrush.Color;
end;

procedure TTeeFont.SetColor(const Value:TColor);
begin
  FBrush.Color:=Value;
end;

function TTeeFont.GetName:String;
begin
  result:=Family;
end;

procedure TTeeFont.SetName(const Value:String);
begin
  Family:=Value;
end;

procedure TTeeFont.DoReadHeight(Reader: TReader);
begin
  Size:=-Reader.ReadInteger*72/96; // <-- VCL compatibility reason
end;

{$IFNDEF D24}
// Skip new FMX XE 10.1 Berlin Font StyleExt property
procedure TTeeFont.DoReadStyleExt(AStream: TStream);
var tmpByte : Byte;
    tmpInt : Int32;
begin
  AStream.Read(tmpByte, 1 {SizeOf(TFontStyles)});
  AStream.Read(tmpInt, 4 {SizeOf(TFontWeight)});
  AStream.Read(tmpInt, 4 {SizeOf(TFontSlant)});
  AStream.Read(tmpInt, 4 {SizeOf(TFontStretch)});
end;
{$ENDIF}

procedure TTeeFont.DefineProperties(Filer:TFiler);
begin
  inherited;
  Filer.DefineProperty('Height',DoReadHeight,nil,False);

  {$IFNDEF D24}
  Filer.DefineBinaryProperty('StyleExt',DoReadStyleExt,nil,False);
  {$ENDIF}
end;
{$ENDIF}

Constructor TTeeFont.Create(ChangedEvent:TNotifyEvent=nil);
begin
  inherited Create;

  FQuality:=fqDefault;

  FBrush:=TTeeBrush.Create(nil);

  InitColor(clBlack);

  {$IFNDEF FMX}
  if CharSet<>DEFAULT_CHARSET then
     CharSet:=DEFAULT_CHARSET;
  {$ENDIF}

  {$IFNDEF FMX}
  // FMX Size and Family are already default
  if Size<>GetDefaultFontSize then
     Size:=GetDefaultFontSize;

  {$IFDEF FMX}Family{$ELSE}Name{$ENDIF}:=GetDefaultFontName;

  {$ENDIF}

  // Set change events here, at the end to avoid access to not-yet created brush:
  FBrush.OnChange:=ChangedEvent;
  {$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF}:=ChangedEvent; { at the create end... 5.02 }
end;

Destructor TTeeFont.Destroy;
begin
  FBrush.Free;
  FOutLine.Free;
  FShadow.Free;
  FEmboss.Free;

  inherited;
end;

Procedure TTeeFont.Assign(Source:TPersistent);
begin
  if Source is TTeeFont then
  With TTeeFont(Source) do
  begin
    Self.FBrush.Assign(FBrush);
    Self.FDefault      :=FDefault;
    Self.FDepth        :=FDepth;
    Self.Emboss        :=FEmboss;
    Self.FInterCharSize:=FInterCharSize;
    Self.OutLine       :=FOutLine;
    Self.FQuality      :=FQuality;
    Self.Shadow        :=FShadow;

    {$IFNDEF FMX}
    Self.FSizeDecimals :=FSizeDecimals;
    {$ENDIF}
  end;

  inherited;
end;

Procedure TTeeFont.InitColor(const Value:TColor);
var Old : TNotifyEvent;
begin
  FBrush.InitColor(Value);

  Old:={$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF};
  {$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF}:=nil; // to avoid Invalidate;

  Color:=Value;
  IDefColor:=Value;

  {$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF}:=Old;
end;

Procedure TTeeFont.InitStyle(const Value:TFontStyles);
var Old : TNotifyEvent;
begin
  Old:={$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF};
  {$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF}:=nil; // to avoid Invalidate;

  Style:=Value;
  IDefStyle:=Value;

  {$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF}:=Old;
end;

Procedure TTeeFont.SetInterCharSize(Value:Integer);
begin
  if Value<>FInterCharSize then
  begin
    FInterCharSize:=Value;
    Changed;
  end;
end;

function TTeeFont.GetOutLine: TTeeHiddenPen;
begin
  if not Assigned(FOutLine) then
     FOutLine:=TTeeHiddenPen.Create({$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF});

  result:=FOutLine;
end;

Procedure TTeeFont.SetOutLine(const Value:TTeeHiddenPen);
begin
  if Assigned(Value) then OutLine.Assign(Value)
                     else FreeAndNil(FOutLine);
end;

function TTeeFont.GetPicture: TTeePicture;
begin
  result:=FBrush.Image;
end;

Function TTeeFont.GetShadow:TTeeShadow;
begin
  if not Assigned(FShadow) then
     FShadow:=TTeeShadow.Create({$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF});

  result:=FShadow;
end;

Function TTeeFont.GetEmboss:TTeeEmboss;
begin
  if not Assigned(FEmboss) then
     FEmboss:=TTeeEmboss.Create({$IFDEF FMX}OnChanged{$ELSE}OnChange{$ENDIF});

  result:=FEmboss;
end;

Procedure TTeeFont.SetPicture(const Value:TTeePicture);
begin
  FBrush.Image:=Value;
end;

Procedure TTeeFont.SetQuality(const Value:TFontQuality);
begin
  if FQuality<>Value then
  begin
    FQuality:=Value;
    Changed;
  end;
end;

function TTeeFont.GetSizeFloat:Single;
begin
  result:=Size {$IFNDEF FMX}+FSizeDecimals{$ENDIF};
end;

procedure TTeeFont.SetSizeFloat(const Value:Single);
{$IFNDEF FMX}
var tmpV : Integer;
{$ENDIF}
begin
  if SizeFloat<>Value then
  begin
    {$IFDEF FMX}
    Size:=Value;
    {$ELSE}
    tmpV:=Floor(Value);
    FSizeDecimals:=Value-tmpV;
    Size:=tmpV;
    {$ENDIF}
  end;
end;

function TTeeFont.IsSizeFloatStored:Boolean;
begin
  result:={$IFDEF FMX}False{$ELSE}FSizeDecimals<>0{$ENDIF};
end;

Procedure TTeeFont.SetShadow(const Value:TTeeShadow);
begin
  if Assigned(Value) then Shadow.Assign(Value)
                     else FreeAndNil(FShadow);
end;

Procedure TTeeFont.SetEmboss(const Value:TTeeEmboss);
begin
  if Assigned(Value) then Emboss.Assign(Value)
                     else FreeAndNil(FEmboss);
end;

procedure TTeeFont.SetBrush(const Value:TTeeBrush);
begin
  FBrush.Assign(Value);
end;

procedure TTeeFont.SetDefault(const Value:Boolean);
begin
  if FDefault<>Value then
  begin
    FDefault:=Value;
    Changed;
  end;
end;

procedure TTeeFont.SetDepth(const Value:Double);
begin
  if FDepth<>Value then
  begin
    FDepth:=Value;
    Changed;
  end;
end;

function TTeeFont.IsDepthStored:Boolean;
begin
  result:=FDepth<>0;
end;

function TTeeFont.IsHeightStored: Boolean;
begin
  result:=Size<>GetDefaultFontSize;
end;

function TTeeFont.IsNameStored: Boolean;
begin
  result:={$IFDEF FMX}Family{$ELSE}Name{$ENDIF}<>GetDefaultFontName;
end;

function TTeeFont.IsStyleStored: Boolean;
begin
  result:=Style<>IDefStyle;
end;

Function TTeeFont.IsColorStored:Boolean;
begin
  result:=Color<>IDefColor;
end;

function TTeeFont.GetGradient: TTeeGradient;
begin
  result:=FBrush.Gradient;
end;

procedure TTeeFont.SetGradient(const Value: TTeeGradient);
begin
  FBrush.Gradient:=Value;
end;

{ Util functions }

Procedure SwapExtended(Var a,b:Extended); {$IFDEF D10}inline;{$ENDIF}
var tmp : Extended;
begin
  tmp:=a; a:=b; b:=tmp;
end;

Procedure SwapDouble(Var a,b:Double); {$IFDEF D10}inline;{$ENDIF}
var tmp : Double;
begin
  tmp:=a; a:=b; b:=tmp;
end;

Procedure SwapSingle(Var a,b:Single); {$IFDEF D10}inline;{$ENDIF}
var tmp : Single;
begin
  tmp:=a; a:=b; b:=tmp;
end;

Procedure SwapInteger(Var a,b:Integer); {$IFDEF D10}inline;{$ENDIF}
var tmp : Integer;
begin
  tmp:=a; a:=b; b:=tmp;
  // asm bswap eax  // <-- D2005 and BDS2006 can't inline assembler
end;

{$IFNDEF TEEVCL}
procedure FreeAndNil(var Obj);
//var P : TObject;
begin
  P:=TObject(Obj);
  TObject(Obj):=nil;
  P.Free;
end;

type
  Exception=class(TObject)
  private
    FMessage: string;
  public
    Constructor Create(S:String);
  end;

  EConvertError=class(Exception);

Constructor Exception.Create(S:String);
begin
  FMessage:=S;
end;

function StrToInt(const S: string): Integer;
var E: Integer;
begin
  Val(S, Result, E);
  if E <> 0 then
     raise EConvertError.Create('Invalid integer: '+S); // Do not localize
end;

function ColorToRGB(Color: TColor): Longint;
begin
  if Color < 0 then
    Result := GetSysColor(Color and $000000FF) else
    Result := Color;
end;

{$IFNDEF LINUX}
function SafeLoadLibrary(const Filename: string; ErrorMode: UINT = SEM_NOOPENFILEERRORBOX): HMODULE;
var
  OldMode: UINT;
  FPUControlWord: Word;
begin
  OldMode := SetErrorMode(ErrorMode);
  try
    asm
      FNSTCW  FPUControlWord
    end;
    try
      Result := LoadLibrary(PChar(Filename));
    finally
      asm
        FNCLEX
        FLDCW FPUControlWord
      end;
    end;
  finally
    SetErrorMode(OldMode);
  end;
end;
{$ELSE}
function SafeLoadLibrary(const FileName: string; Dummy: LongWord): HMODULE;
var
  FPUControlWord: Word;
begin
  asm
    FNSTCW  FPUControlWord
  end;
  try
    Result := LoadLibrary(PChar(Filename));
  finally
    asm
      FNCLEX
      FLDCW FPUControlWord
    end;
  end;
end;
{$ENDIF}

{$IFDEF MSWINDOWS}
var
{ Win32 platform identifier.  This will be one of the following values:

    VER_PLATFORM_WIN32s
    VER_PLATFORM_WIN32_WINDOWS
    VER_PLATFORM_WIN32_NT

  See WINDOWS.PAS for the numerical values. }

  Win32Platform: Integer = 0;

{ Win32 OS version information -

  see TOSVersionInfo.dwMajorVersion/dwMinorVersion/dwBuildNumber }

  Win32MajorVersion: Integer = 0;
  Win32MinorVersion: Integer = 0;
  Win32BuildNumber: Integer = 0;


procedure InitPlatformId;
var
  OSVersionInfo: TOSVersionInfo;
begin
  OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  if GetVersionEx(OSVersionInfo) then
    with OSVersionInfo do
    begin
      Win32Platform := dwPlatformId;
      Win32MajorVersion := dwMajorVersion;
      Win32MinorVersion := dwMinorVersion;
      if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
        Win32BuildNumber := dwBuildNumber and $FFFF
      else
        Win32BuildNumber := dwBuildNumber;
      //Win32CSDVersion := szCSDVersion;
    end;
end;
{$ENDIF}

{$ENDIF}

Procedure TeeDefaultFont;
{$IFDEF FMX}
{$IFDEF D20}
var FontSvc : IFMXSystemFontService;
{$ENDIF}
{$ENDIF}
Begin
  {$IFDEF FMX}

  {$IFDEF D20}
  if TPlatformServices.Current.SupportsPlatformService(IFMXSystemFontService, IInterface(FontSvc)) then
  begin
    GetDefaultFontSize:=Round(FontSvc.GetDefaultFontSize);
    GetDefaultFontName:=FontSvc.GetDefaultFontFamilyName;
  end
  else
  begin
    GetDefaultFontSize:=StrToInt(TeeMsg_DefaultFontSize);
    GetDefaultFontName:=TeeMsg_DefaultFontName;
  end;

  {$ELSE}
  GetDefaultFontSize:=StrToInt(TeeMsg_DefaultFontSize);

  {$IFDEF D161}
  GetDefaultFontName:=Platform.GetDefaultFontFamilyName;
  {$ELSE}
  GetDefaultFontName:=TeeMsg_DefaultFontName;
  {$ENDIF}

  {$ENDIF}

  {$ELSE}
  GetDefaultFontName:=TeeMsg_DefaultFontName;
  GetDefaultFontSize:=StrToInt(TeeMsg_DefaultFontSize);

  {$IFDEF USESYSLOCALE}
  if (GetDefaultFontSize=8) and (SysLocale.PriLangID=LANG_JAPANESE) then
      GetDefaultFontSize:=9;

  if (GetDefaultFontName='Arial') and // Do not localize
     (SysLocale.PriLangID=LANG_JAPANESE) then
       GetDefaultFontName:='‚l‚r ‚oƒSƒVƒbƒN';
  {$ENDIF}

  {$ENDIF}
end;

{$IFNDEF FMX}

{$IFNDEF LINUX}

const
  MSIMG32DLL='msimg32.dll'; // Do not localize

type
  AlphaBlendType=function(DC: HDC; p2, p3, p4, p5: Integer;
                          DC6: HDC; p7, p8, p9, p10: Integer;
                          p11: TBlendFunction): BOOL; stdcall;
{$ENDIF}

{$IFNDEF LINUX}
var
  AlphaBlendProc      : AlphaBlendType=nil;
{$ENDIF}

var
  TeeMSIMG32:THandle=0;

Function CheckWinAlphaBlend:Boolean;
begin
  {$IFDEF MSWINDOWS}
  TeeMSIMG32:=TeeLoadLibrary(MSIMG32DLL);

  if TeeMSIMG32<>0 then
     @AlphaBlendProc:=GetProcAddress(TeeMSIMG32,'AlphaBlend') // Do not localize
  else
     @AlphaBlendProc:=nil;

  result:=@AlphaBlendProc<>nil;
  {$ELSE}
  result:=False;
  {$ENDIF}
end;

Procedure TryLoadSetDC;
begin
  {$IFNDEF LCL}
  if LoadGDI32 then
  begin
    @TeeSetDCBrushColor:=GetProcAddress(TeeGDI32,'SetDCBrushColor'); // Do not localize
    @TeeSetDCPenColor:=GetProcAddress(TeeGDI32,'SetDCPenColor'); // Do not localize
  end
  else
  {$ENDIF}
  begin
    TeeSetDCBrushColor:=nil;
    TeeSetDCPenColor:=nil;
  end;
end;
{$ENDIF}

{$IFNDEF LCL}
var
  AlphaBlendChecked   : Boolean=False;
  CanUseWinAlphaBlend : Boolean=False;
{$ENDIF}

// Optional TransparentMask bitmap overrides the "Percent" parameter.
// It holds transparency at each Width x Height pixel,
// ie: An RGB pixel 128,128,128 means 50% transparency
procedure TeeBlendBitmaps(Const Percent: Double;
                          ABitmap,BBitmap:TBitmap; BOrigin:TPoint;
                          TransparentMask:TBitmap=nil);
{$IFDEF LCL}
begin
// TLazIntfImage.AlphaBlend ?
end;
{$ELSE}

{$IFDEF FMX}
begin
//  Stroke/Fill.Alpha or TBlendEffect ?
end;
{$ELSE}

const
  BytesPerPixel=3;

var tmpW     : Integer;
    tmpH     : Integer;

    BlendFunc: BLENDFUNCTION;

    tmpX     : Integer;
    tmpY     : Integer;

    Line0A   : PByteArray;
    Line0B   : PByteArray;
    DifA     : TeeNativeInt;
    DifB     : TeeNativeInt;

    p        : PChar;
    pc       : PChar;
    pt       : PChar;
    Percent1 : Integer;
    tmpBBitmap : TBitmap;
begin
  if BOrigin.Y<0 then BOrigin.Y:=0;
  if BOrigin.X<0 then BOrigin.X:=0;

  tmpW:=Math.Min(ABitmap.Width,BBitmap.Width-BOrigin.X);
  tmpH:=Math.Min(ABitmap.Height,BBitmap.Height-BOrigin.Y);

  if (tmpW>0) and (tmpH>0) then
  begin

    {$IFNDEF LCL}
    if not AlphaBlendChecked then
    begin
      CanUseWinAlphaBlend:=IsWindowsNT and CheckWinAlphaBlend;
      AlphaBlendChecked:=True;
    end;

    if CanUseWinAlphaBlend and (not Assigned(TransparentMask)) then
    begin
      // Alphablend fails in Win98

      BlendFunc.BlendOp:=AC_SRC_OVER;
      BlendFunc.BlendFlags:=0;
      BlendFunc.SourceConstantAlpha:=255-Round(Percent*2.55);
      BlendFunc.AlphaFormat:=0;
      AlphaBlendProc(BBitmap.Canvas.Handle, BOrigin.X, BOrigin.Y, tmpW, tmpH,
                     ABitmap.Canvas.Handle,0,0,tmpW,tmpH, BlendFunc);

      {$IFDEF TEECANVASDEBUG}
      BBitmap.Canvas.Brush.Style:=bsClear;
      BBitmap.Canvas.Pen.Color:=clLime;
      BBitmap.Canvas.Rectangle(BOrigin.X,BOrigin.Y,BOrigin.X+tmpW,BOrigin.Y+tmpH);
      {$ENDIF}

      Exit;
    end;
    {$ENDIF}

    ABitmap.PixelFormat:=TeePixelFormat;
    BBitmap.PixelFormat:=TeePixelFormat;

    if Assigned(TransparentMask) then
       TransparentMask.PixelFormat:=TeePixelFormat; // 24bit RGB

    {$IFNDEF LINUX}
    tmpBBitmap:=BBitmap;
    {$ELSE}
    tmpBBitmap:=TBitmap.Create;
    TeeSetBitmapSize(tmpBBitmap,BBitmap.Width,BBitmap.Height);

    QPixmap_setOptimization( BBitmap.Handle, QPixmapOptimization_BestOptim );
    QPixmap_setOptimization( tmpBBitmap.Handle, QPixmapOptimization_BestOptim );
    BitBlt( tmpBBitmap.Handle, 0, 0, BBitmap.Handle, 0, 0,
            BBitmap.Width, BBitmap.Height, RasterOp_CopyROP, true );
    {$ENDIF}

    Percent1:=256-Round((100-Percent)*2.56);

    {$IFNDEF LCL}
    Line0A:=ABitmap.ScanLine[0];
    Line0B:=tmpBBitmap.ScanLine[BOrigin.Y];

    if tmpH>1 then
    begin
      DifA:=TeeNativeInt(ABitmap.ScanLine[1])-TeeNativeInt(Line0A);
      DifB:=TeeNativeInt(tmpBBitmap.ScanLine[BOrigin.Y+1])-TeeNativeInt(Line0B);
    end
    else
    begin
      DifA:=0;
      DifB:=0;
    end;
    {$ENDIF}

    for tmpY:=0 to tmpH-1 do
    begin
      // faster version
      p:=PChar(@PByteArray(TeeNativeInt(Line0A)+DifA*tmpY)[0]);
      pc:=PChar(@PByteArray(TeeNativeInt(Line0B)+DifB*tmpY)[BOrigin.X]);

      if Assigned(TransparentMask) then
      begin
        pt:=PChar(TransparentMask.ScanLine[tmpY]);

        for tmpX:=0 to tmpW-1 do
        begin
          if PColor(pc)^=$FFFFFF then  // Already white color
             PByte(pc+2)^:=$FF-1;

          PByte(pc)^:=Byte(pc^)+((Byte(pt^)*(Byte(p^)-Byte(pc^))) shr 8);
          Inc(pc);
          PByte(pc)^:=Byte(pc^)+((Byte((pt+1)^)*(Byte((p+1)^)-Byte(pc^))) shr 8);
          Inc(pc);
          PByte(pc)^:=Byte(pc^)+((Byte((pt+2)^)*(Byte((p+2)^)-Byte(pc^))) shr 8);
          Inc(pc);

          Inc(p,3);
          Inc(pt,3);
        end
      end
      else
      for tmpX:=0 to tmpW-1 do
      begin
        if PColor(pc)^=$FFFFFF then  // Already white color
           PByte(Byte(pc+2))^:=$FF-1;

        PByte(pc)^:=Byte(pc^)+((Percent1*(Byte(p^)-Byte(pc^))) shr 8);
        Inc(pc);
        PByte(pc)^:=Byte(pc^)+((Percent1*(Byte((p+1)^)-Byte(pc^))) shr 8);
        Inc(pc);
        PByte(pc)^:=Byte(pc^)+((Percent1*(Byte((p+2)^)-Byte(pc^))) shr 8);
        Inc(pc);

        Inc(p,3);
      end;
    end;

    {$IFDEF LINUX}
    BitBlt( BBitmap.Handle, 0, 0,
            tmpBBitmap.Handle, 0, 0, BBitmap.Width, BBitmap.Height, RasterOp_CopyROP, True );
    FreeAndNil(tmpBBitmap);
    {$ENDIF}

  end;
end;
{$ENDIF}
{$ENDIF}

{ TTeeBlend }

Constructor TTeeBlend.Create(const ACanvas: TTeeCanvas; const R: TRect);
begin
  inherited Create;

  {$IFNDEF FMX}
  FCanvas:=ACanvas;

  if Assigned(FCanvas) then
     SetRectangle(R);
  {$ENDIF}
end;

Destructor TTeeBlend.Destroy;
begin
  FBitmap.Free;
  inherited;
end;

Procedure TTeeBlend.SetRectangle(Const R:TRect);
{$IFNDEF FMX}
var tmp  : TCanvas;
    tmpW : Integer;
    tmpH : Integer;
    tmpR : TRect;
{$ENDIF}
begin
  {$IFNDEF FMX}
  tmpR:=OrientRectangle(R);  //tmpR added to support GDI+

  with tmpR do
  begin
    if Left<0 then Left:=0;
    if Top<0 then Top:=0;
    if Right<0 then Right:=0;
    if Bottom<0 then Bottom:=0;

    if Right-Left<1 then Right:=Left+1;
    if Bottom-Top<1 then Bottom:=Top+1;

    if Right>FCanvas.Bounds.Right then
       Right:=FCanvas.Bounds.Right;

    if Bottom>FCanvas.Bounds.Bottom then
       Bottom:=FCanvas.Bounds.Bottom;
  end;

  RectSize(tmpR, tmpW, tmpH);

  FRect := tmpR;

  IValidSize:=(tmpW>0) and (tmpH>0);

  if IValidSize then
  begin
    // speed optimization, recreate FBitmap instead of reusing it
    FBitmap.Free;

    FBitmap:=TBitmap.Create;

    With FBitmap do
    begin
      {$IFDEF IGNOREPALETTE}
      IgnorePalette:=True;
      {$ENDIF}
      PixelFormat:=TeePixelFormat;
    end;

    TeeSetBitmapSize(FBitmap,tmpW,tmpH);

    if (FCanvas is TTeeCanvas3D) and Assigned(TTeeCanvas3D(FCanvas).Bitmap) then
       tmp:=TTeeCanvas3D(FCanvas).Bitmap.Canvas
    else
       tmp:=FCanvas.ReferenceCanvas;

    FBitmap.Canvas.CopyRect( TeeRect(tmpW,tmpH), tmp, FRect );
  end;
  {$ENDIF}
end;

// BCB5 BUG. Package TeeXC5.bpr should include -$O- in PFLAGS (disable optim.)
// Otherwise, there's an AV here when compiling apps only when "using packages"
procedure TTeeBlend.DoBlend(Transparency: TTeeTransparency);
{$IFNDEF FMX}
var tmp       : TBitmap;
    tmpNew    : Boolean;
    tmpPoint  : TPoint;
    tmpAlign  : TCanvasTextAlign;
{$ENDIF}
begin
  {$IFNDEF FMX}
  if not FCanvas.Metafiling then
  if IValidSize then
  begin
    tmp:=TTeeCanvas3D(FCanvas).Bitmap;

    tmpNew:=not Assigned(tmp);

    if tmpNew then
    begin
      tmp:=TBitmap.Create;
      {$IFDEF IGNOREPALETTE}
      tmp.IgnorePalette:=True;
      {$ENDIF}

      TeeSetBitmapSize(tmp,FRect.Right-FRect.Left,FRect.Bottom-FRect.Top);

      tmp.Canvas.CopyRect(TeeRect(tmp.Width,tmp.Height),FCanvas.ReferenceCanvas,FRect);

      tmpPoint:=TeeZeroPoint;
    end
    else
      tmpPoint:=FRect.TopLeft;

    tmpAlign:=FCanvas.TextAlign;

    TeeBlendBitmaps(100-Transparency,FBitmap,tmp,tmpPoint);

    if FCanvas.TextAlign<>tmpAlign then
    begin
      FCanvas.AssignFont(FCanvas.FFont);
      FCanvas.TextAlign:=tmpAlign;
    end;

    if tmpNew then
    begin
      FCanvas.Draw(FRect.Left,FRect.Top,tmp);
      tmp.Free;
    end;
  end;
  {$ENDIF}
end;

{ TWhitePen }

Constructor TWhitePen.Create(const OnChangeEvent: TNotifyEvent);
begin
  inherited;
  InitColor(clWhite);
end;

// Returns point "ATo" minus ADist pixels from AFrom point.
Function PointAtDistance(const AFrom,ATo:TPoint; const ADist:Single):TPoint;
var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  result:=ATo;

  if ATo.X<>AFrom.X then
  begin
    SinCos(Math.ArcTan2((ATo.Y-AFrom.Y),(ATo.X-AFrom.X)),tmpSin,tmpCos);

    {$IFDEF FMX}
    result.X:=result.X-(ADist*tmpCos);
    result.Y:=result.Y-(ADist*tmpSin);
    {$ELSE}
    Dec(result.X,Round(ADist*tmpCos));
    Dec(result.Y,Round(ADist*tmpSin));
    {$ENDIF}
  end
  else
  begin
    {$IFDEF FMX}
    if ATo.Y<AFrom.Y then result.Y:=result.Y+ADist
                     else result.Y:=result.Y-ADist;
    {$ELSE}
    if ATo.Y<AFrom.Y then Inc(result.Y,Round(ADist))
                     else Dec(result.Y,Round(ADist));
    {$ENDIF}
  end;
end;

{$IFNDEF FMX}
Function PointAtDistance(const AFrom,ATo:TPointFloat; const ADist:Single):TPointFloat;
var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  result:=ATo;

  if ATo.X<>AFrom.X then
  begin
    SinCos(Math.ArcTan2((ATo.Y-AFrom.Y),(ATo.X-AFrom.X)),tmpSin,tmpCos);

    result.X:=result.X-(ADist*tmpCos);
    result.Y:=result.Y-(ADist*tmpSin);
  end
  else
  if ATo.Y<AFrom.Y then result.Y:=result.Y+ADist
                   else result.Y:=result.Y-ADist;
end;
{$ENDIF}

Function TeeDistance(const x,y:Double):Double; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=Sqrt(Sqr(x)+Sqr(y));
end;

// Anders Melander Resample.pas
// www.melander.dk  anders@aztech.dk
// Improvements suggested by David Ullrich.
// Modified and optimized for speed by Steema Software.

type
  TFilterValue=Single;

  // Type of a filter for use with Stretch
  TFilterProc=function(Value: TFilterValue): TFilterValue;

  // Physical bitmap pixel
  TColorRGB = packed record
    r, g, b : Byte;
  end;
  PColorRGB = ^TColorRGB;

  {$IFDEF USE_SCANLINE}
  // Physical bitmap scanline (row)
  TRGBList = packed array[0..0] of TColorRGB;
  PRGBList = ^TRGBList;
  {$ENDIF}

procedure SmoothStretch(const Src, Dst: TBitmap; Filter: TFilterProc; const fwidth: TFilterValue); overload;
{$IFDEF FMX}
begin
  Dst.Canvas.DrawBitmap(Src,TeeRect(Src.Width,Src.Height),TeeRect(Dst.Width,Dst.Height),1,True);
end;
{$ELSE}

type
  // Contributor for a pixel
  TContributor=record
    Pixel  : Integer; // Source pixel
    Weight : Integer; // Pixel weight
  end;

  TContributorArray=Array[0..0] of TContributor;
  PContributorArray=^TContributorArray;

const
  WeightMultiplier=1024;
  MaxRGB=255*WeightMultiplier; // Avoid "Range Check" error

var
  ContribN : Array of Integer;   // Removed "packed"
  ContribP : Array of PContributorArray; // Removed "packed"

  procedure CalcSampling(const SrcSize,DstSize:Integer; const Scale:TFilterValue);
  var i : Integer;
      j : Integer;
      TwoSizeMinusOne : Integer;
      WeightScale : TFilterValue;
      fScale,
      Width,
      Center : TFilterValue;
      tmpStart,
      tmpEnd : Single;
  begin
    fScale:=1.0/Scale;
    Width:=fWidth*fScale;

    if Scale >= 1 then
       WeightScale:=WeightMultiplier
    else
       WeightScale:=WeightMultiplier*Scale;

    TwoSizeMinusOne:=SrcSize+SrcSize-1;

    for i:=0 to DstSize-1 do
    begin
      ContribN[i]:=0;
      Center:= i * fscale;

      tmpStart:=Center-Width;
      tmpEnd:=Center+Width;

      for j:=Floor(tmpStart) to Ceil(tmpEnd) do

      {$IFOPT R+}
      {$DEFINE WASRANGE}
      {$R-}
      {$ENDIF}

      with ContribP[i,ContribN[i]] do

      {$IFDEF WASRANGE}
      {$R+}
      {$ENDIF}
      begin
        if Scale >= 1 then
           Weight:=Round(WeightScale * Filter( (Center - j) ) )
        else
           Weight:=Round(WeightScale * Filter( (Center - j) * Scale) );

        if Weight<>0 then
        begin
          if j < 0 then
             Pixel := -j
          else
          if j >= SrcSize then
             Pixel := TwoSizeMinusOne - j
          else
             Pixel := j;

          Inc(ContribN[i]);

          // Necessary?
          if Pixel<0 then
             Pixel:=-Pixel;
        end
//        else
//          Pixel:=0;
      end;
    end;
  end;

  {$IFNDEF USE_SCANLINE}
  function Color2RGB(const Color: TColor): TColorRGB;
  begin
    Result.r := Color AND $000000FF;
    Result.g := (Color AND $0000FF00) SHR 8;
    Result.b := (Color AND $00FF0000) SHR 16;
  end;

  function RGB2Color(const Color: TColorRGB): TColor;
  begin
    Result := Color.r OR (Color.g SHL 8) OR (Color.b SHL 16);
  end;
  {$ENDIF}

var
  Work      : TBitmap;
  SrcHeight,
  DstWidth  : Integer;

  procedure FilterHorizontally;
  var {$IFDEF USE_SCANLINE}
      SourceLine : PRGBList;
      {$ELSE}
      DestColor  : TColorRGB;
      {$ENDIF}

      k : Integer;
      i : Integer;
      j : Integer;

      rgbr,
      rgbg,
      rgbb  : Integer;

      DestPixel  : PColorRGB;
      tmp : PContributorArray;
  begin
    {$IFNDEF USE_SCANLINE}
    DestPixel:=@DestColor;
    {$ENDIF}

    for k:=0 to SrcHeight-1 do
    begin
      {$IFDEF USE_SCANLINE}
      SourceLine := Src.ScanLine[k];
      DestPixel  := Work.ScanLine[k];
      {$ENDIF}

      for i:=0 to DstWidth-1 do
      begin
        tmp:=ContribP[i];

        rgbr:=0;
        rgbg:=0;
        rgbb:=0;

        {$IFOPT R+}
        {$DEFINE WASRANGE}
        {$R-}
        {$ENDIF}

        for j:=0 to ContribN[i]-1 do
        with {$IFDEF USE_SCANLINE}
             PColorRGB(TeeNativeInt(SourceLine)+tmp[j].Pixel*3)^ do
             {$ELSE}
             Color2RGB(Src.Canvas.Pixels[tmp[j].Pixel,k]) do
             {$ENDIF}
        begin
          Inc(rgbr, r * tmp[j].Weight);
          Inc(rgbg, g * tmp[j].Weight);
          Inc(rgbb, b * tmp[j].Weight);
        end;

        {$IFDEF WASRANGE}
        {$R+}
        {$ENDIF}

        with DestPixel^ do
        begin
          if rgbr > MaxRGB then
             r := 255
          else
          if rgbr < 0 then
             r := 0
          else
             r := rgbr shr 10;

          if rgbg > MaxRGB then
             g := 255
          else
          if rgbg < 0 then
             g := 0
          else
             g := rgbg shr 10;

          if rgbb > MaxRGB then
             b := 255
          else
          if rgbb < 0 then
             b := 0
          else
             b := rgbb shr 10;
        end;

        {$IFDEF USE_SCANLINE}
        Inc(DestPixel);
        {$ELSE}
        Work.Canvas.Pixels[i,k]:=RGB2Color(DestPixel^);
        {$ENDIF}
      end;
    end;
  end;

var
  DstHeight : Integer;

  procedure FilterVertically;
  var {$IFDEF USE_SCANLINE}
      DestLine   : PRGBList;
      Delta      : TeeNativeInt;
      {$ENDIF}
      k : Integer;
      i : Integer;
      j,
      rgbr,
      rgbg,
      rgbb  : Integer;

      {$IFDEF USE_SCANLINE}
      DestDelta  : TeeNativeInt;
      SourceLine : PRGBList;
      {$ELSE}
      DestColor : TColorRGB;
      {$ENDIF}

      DestPixel  : PColorRGB;
      tmp : PContributorArray;
  begin
    {$IFDEF USE_SCANLINE}
    SourceLine:= Work.ScanLine[0];
    Delta:= TeeNativeInt(Work.ScanLine[1]) - TeeNativeInt(SourceLine);
    DestLine:= Dst.ScanLine[0];
    DestDelta:= TeeNativeInt(Dst.ScanLine[1]) - TeeNativeInt(DestLine);
    {$ELSE}
    DestPixel:=@DestColor;
    {$ENDIF}

    for k:= 0 to DstWidth-1 do
    begin
      {$IFDEF USE_SCANLINE}
      DestPixel:=Pointer(DestLine);
      {$ENDIF}

      for i:= 0 to DstHeight-1 do
      begin
        tmp:=ContribP[i];

        rgbr:=0;
        rgbg:=0;
        rgbb:=0;

        {$IFOPT R+}
        {$DEFINE WASRANGE}
        {$R-}
        {$ENDIF}

        for j:= 0 to ContribN[i]-1 do
        with {$IFDEF USE_SCANLINE}
             PColorRGB(TeeNativeInt(SourceLine)+tmp[j].Pixel*Delta)^ do
             {$ELSE}
             Color2RGB(Work.Canvas.Pixels[k, tmp[j].Pixel]) do
             {$ENDIF}
        begin
          Inc(rgbr, r * tmp[j].Weight);
          Inc(rgbg, g * tmp[j].Weight);
          Inc(rgbb, b * tmp[j].Weight);
        end;

        {$IFDEF WASRANGE}
        {$R+}
        {$ENDIF}
        
        with DestPixel^ do
        begin
          if rgbr > MaxRGB then
             r := 255
          else
          if rgbr < 0 then
             r := 0
          else
             r := rgbr shr 10;

          if rgbg > MaxRGB then
             g := 255
          else
          if rgbg < 0 then
             g := 0
          else
             g := rgbg shr 10;

          if rgbb > MaxRGB then
             b := 255
          else
          if rgbb < 0 then
             b := 0
          else
             b := rgbb shr 10;
        end;

        {$IFDEF USE_SCANLINE}
        Inc(TeeNativeInt(DestPixel), DestDelta);
        {$ELSE}
        Dst.Canvas.Pixels[k, i] := RGB2Color(DestPixel^);
        {$ENDIF}
      end;

      {$IFDEF USE_SCANLINE}
      Inc(SourceLine);
      Inc(DestLine);
      {$ENDIF}
    end;
  end;

var tmpW,
    tmpH,
    Scale : TFilterValue; // Zoom scale factor
    i,
    tmpSize,
    tmpSize2,
    SrcWidth : Integer;
begin
  if (not Assigned(Dst)) or (not Assigned(Src)) then Exit;

  DstWidth := Dst.Width;
  DstHeight := Dst.Height;
  SrcWidth := Src.Width;
  SrcHeight := Src.Height;

  if (SrcWidth <= 1) or (SrcHeight <= 1) then Exit;
  if (DstWidth <= 1) or (DstHeight <= 1) then Exit;

  // Create intermediate image to hold horizontal zoom
  Work:=TBitmap.Create;
  try
    {$IFDEF USE_SCANLINE}
    // This implementation only works on 24-bit images because it uses
    // TBitmap.Scanline
    Src.PixelFormat := TeePixelFormat;
    Dst.PixelFormat := TeePixelFormat;
    Work.PixelFormat := TeePixelFormat;
    {$ENDIF}

    TeeSetBitmapSize(Work,DstWidth,SrcHeight);

    tmpSize:=Max(DstWidth,DstHeight);

    SetLength(ContribN, tmpSize);
    SetLength(ContribP, tmpSize);

    tmpW:=(DstWidth - 1) / (SrcWidth - 1);
    tmpH:=(DstHeight - 1) / (SrcHeight - 1);
    Scale:=Min(tmpW, tmpH);

    tmpSize2:=Trunc((fwidth * (1.0 / Scale)) * 2.0) + 3;

    for i:= 0 to tmpSize-1 do
        GetMem(ContribP[i],tmpSize2*SizeOf(TContributor));

    // Pre-calculate filter contributions for a row
    CalcSampling(SrcWidth,DstWidth,(DstWidth - 1) / (SrcWidth - 1));

    // Apply filter to sample horizontally from Src to Work
    FilterHorizontally;

    // Pre-calculate filter contributions for a column
    CalcSampling(SrcHeight,DstHeight,(DstHeight - 1) / (SrcHeight - 1));

    // Apply filter to sample vertically from Work to Dst
    if DstHeight>2 then  // AV fix
       FilterVertically;

    for i:=0 to Length(ContribP)-1 do
        FreeMem(ContribP[i]);

    ContribP:=nil;
    ContribN:=nil;
  finally
    Work.Free;
  end;
end;

// Lanczos3 filter
function Lanczos3Filter(Value: TFilterValue): TFilterValue;

  function SinC(const Value: TFilterValue): TFilterValue; {$IFDEF D9}inline;{$ENDIF}
  begin
    Result := Sin(Value) / Value
  end;

const PiDiv3 = Pi/3.0;
begin
  if Value=0 then
     result:=1
  else
  begin
    if Value < 0 then
       Value := -Value;

    if Value < 3 then
       result:=SinC(Value*Pi) * SinC(Value*PiDiv3)
    else
       result:=0;
  end;
end;

function TriangleFilter(Value: TFilterValue): TFilterValue;
begin
  if Value < 0.0 then
     Value := -Value;

  if Value < 1.0 then
     Result := 1.0 - Value
  else
     Result := 0.0;
end;
{$ENDIF}

procedure SmoothStretch(Src, Dst: TBitmap); overload; {$IFDEF D9}inline;{$ENDIF}
begin
  SmoothStretch(Src,Dst,ssBestQuality);
end;

procedure SmoothStretch(Src, Dst: TBitmap; Option:TSmoothStretchOption); overload;
begin
  {$IFDEF FMX}
  SmoothStretch(Src,Dst,nil,0);
  {$ELSE}
  if Option=ssBestQuality then
     SmoothStretch(Src,Dst,Lanczos3Filter,3)  // Slower but big quality.
  else
     SmoothStretch(Src,Dst,TriangleFilter,1);  // Faster, but not big quality.
  {$ENDIF}
end;

{$IFNDEF FMX}

{$IFNDEF LINUX}
Function TeeLoadLibrary(Const FileName:String):HInst;
begin
  result:=SafeLoadLibrary(FileName);
end;

// Free Library, but do not free library in Windows 95 (lock bug)
Procedure TeeFreeLibrary(hLibModule: HMODULE);
begin
  if (Win32Platform=VER_PLATFORM_WIN32_WINDOWS) and
     (Win32MinorVersion=0) then
  else
     FreeLibrary(hLibModule);
end;
{$ENDIF}
{$ENDIF}

{$IFNDEF TEEVCL}
Procedure TPersistent.Assign(Source:TPersistent);
begin
  raise Exception.Create('Cannot assign TPersistent'); // Do not localize
end;

procedure TGraphicObject.Changed;
begin
end;

function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
  result:=0;
end;

procedure TCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
end;

procedure TCanvas.CopyRect(const Dest: TRect; const Canvas: TCanvas; const Source: TRect);
begin
end;

function TCanvas.TextExtent(const Text: string): TSize;
begin
  result.cx:=0;
  result.cy:=0;
end;

procedure TCanvas.Draw(X, Y: Integer; const Graphic: TGraphic);
begin
end;

procedure TCanvas.StretchDraw(const Rect: TRect; const Graphic: TGraphic);
begin
end;

Constructor TCustomPanel.Create(AOwner: TComponent);
begin
end;

procedure TCustomPanel.AssignTo(Dest: TPersistent);
begin
end;

procedure TCustomPanel.CreateParams(var Params: TCreateParams);
begin
end;

Procedure TCustomPanel.DefineProperties(Filer:TFiler);
begin
end;
{$ENDIF}

{ TFilterRegion }

procedure TFilterRegion.Assign(Source:TPersistent);
begin
  if Source is TFilterRegion then
  with TFilterRegion(Source) do
  begin
    Self.FLeft:=FLeft;
    Self.FTop:=FTop;
    Self.FWidth:=FWidth;
    Self.FHeight:=FHeight;
  end
  else
    inherited;
end;

function TFilterRegion.GetRectangle:TRect;
begin
  result:=TeeRect(FLeft,FTop,FLeft+FWidth,FTop+FHeight);
end;

procedure TFilterRegion.SetRectangle(const Rect: TRect);
begin
  {$IFDEF FMX}
  FLeft:=Round(Rect.Left);
  FTop:=Round(Rect.Top);
  FWidth:=Round(Rect.Right-Rect.Left);
  FHeight:=Round(Rect.Bottom-Rect.Top);
  {$ELSE}
  FLeft:=Rect.Left;
  FTop:=Rect.Top;
  FWidth:=Rect.Right-Rect.Left;
  FHeight:=Rect.Bottom-Rect.Top;
  {$ENDIF}
end;

{ TFilter }
Constructor TTeeFilter.Create(Collection: TCollection);
begin
  inherited;
  AllowRegion:=True;
  FEnabled:=True;
end;

Destructor TTeeFilter.Destroy;
begin
  {$IFNDEF FMX}
  Lines:=nil;
  {$ENDIF}

  LinesRGBA:=nil;

  FRegion.Free;
  inherited;
end;

function TeeRectI(Width,Height:Integer):TRectI;
begin
  result.Left:=0;
  result.Top:=0;
  result.Right:=Width;
  result.Bottom:=Height;
end;

procedure TTeeFilter.Apply(const Bitmap:TBitmap);
var tmp : TRectI;
begin
  if not Assigned(FRegion) then
     Apply(Bitmap,TeeRectI(Bitmap.Width-1,Bitmap.Height-1))
  else
  begin
    tmp.Left:=FRegion.Left;
    tmp.Top:=FRegion.Top;

    if FRegion.Width=0 then
       tmp.Right:=Max(tmp.Left,Bitmap.Width-1)
    else
       tmp.Right:=Min(Bitmap.Width-1,tmp.Left+FRegion.Width);

    if Region.Height=0 then
       tmp.Bottom:=Max(tmp.Top,Bitmap.Height-1)
    else
       tmp.Bottom:=Min(Bitmap.Height-1,tmp.Top+FRegion.Height);

    Apply(Bitmap,tmp);
  end;
end;

procedure TTeeFilter.Apply(const Bitmap:TBitmap; const R:TRectI);
begin
  if not ReuseLines then
     CalcLines(Bitmap);
end;

class procedure TTeeFilter.ApplyTo(const Bitmap: TBitmap);
begin
  with Create(nil) do
  try
    Apply(Bitmap);
  finally
    Free;
  end;
end;

class function TTeeFilter.Description:String;
begin
  result:=ClassName;
end;

procedure TTeeFilter.CalcLines(const Bitmap:TBitmap);
begin
  {$IFDEF LCL}
  TeeCalcLines(LinesRGBA,Bitmap);
  {$ELSE}
  {$IFDEF FMX}
  TeeCalcLines(LinesRGBA,Bitmap);
  {$ELSE}
  if RGBA then
     TeeCalcLines(LinesRGBA,Bitmap)
  else
     TeeCalcLines(Lines,Bitmap);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeeFilter.CreateEditor(Creator:IFormCreator; AChanged:TNotifyEvent);
begin // No properties (abstract)
  FChanged:=AChanged;
end;

function TTeeFilter.Empty:Boolean;
begin
  {$IFDEF FMX}
  result:=Length(LinesRGBA)=0;
  {$ELSE}
  if RGBA then
     result:=Length(LinesRGBA)=0
  else
     result:=Length(Lines)=0;
  {$ENDIF}
end;

procedure TTeeFilter.PrepareForGallery;
begin
end;

procedure TTeeFilter.Assign(Source: TPersistent);
begin
  if Source is TTeeFilter then
  begin
    Enabled:=TTeeFilter(Source).Enabled;
    Region:=TTeeFilter(Source).Region;
  end
  else
     inherited;
end;

function TTeeFilter.GetRegion: TFilterRegion;
begin
  if not Assigned(FRegion) then
     FRegion:=TFilterRegion.Create;

  result:=FRegion;
end;

procedure TTeeFilter.SetRegion(const Value:TFilterRegion);
begin
  if Assigned(Value) then
     Region.Assign(Value)
  else
     FreeAndNil(FRegion);
end;

procedure TTeeFilter.SetEnabled(const Value:Boolean);
begin
  if FEnabled<>Value then
  begin
    FEnabled:=Value;

    if Assigned(FChanged) then
       FChanged(Self);
  end;
end;

{ TFilterItems }

function TFilterItems.Get(Index: Integer): TTeeFilter;
begin
  result:=TTeeFilter(GetItem(Index));
end;

procedure TFilterItems.Put(Index: Integer; const Value: TTeeFilter);
begin
  SetItem(Index,Value);
end;

function TFilterItems.Add(Filter:TFilterClass):TTeeFilter;
begin
  result:=Filter.Create(Self) as TTeeFilter;
end;

procedure TFilterItems.ApplyTo(const ABitmap: TBitmap);
var t : Integer;
begin
  for t:=0 to Count-1 do
  with Item[t] do
  if Enabled then
     Apply(ABitmap);
end;

procedure TFilterItems.Assign(Source: TPersistent);
var t   : Integer;
    tmp : TTeeFilter;
begin
  if Source is TFilterItems then
  begin
    Clear;

    BeginUpdate;
    try

      with TFilterItems(Source) do
      for t:=0 to Count-1 do
      begin
        tmp:=TFilterClass(Item[t].ClassType).Create(Self);
        tmp.Assign(Item[t]);
      end;
    finally
      EndUpdate;
    end;
  end
  else inherited;
end;

{$IFDEF D6}
procedure TFilterItems.Notify(Item: TCollectionItem; Action: {$IFDEF D18}Classes.{$ENDIF}TCollectionNotification);
begin
  inherited;

  if Assigned(FOnChange) then
     FOnChange(Self);
end;
{$ENDIF}

procedure TFilterItems.Update(Item: TCollectionItem);
begin
  inherited;
  Changed:=True;
end;

{ TTeePicture }
Destructor TTeePicture.Destroy;
begin
  FFilters.Free;
  IBitmap.Free;
  inherited;
end;

procedure TTeePicture.DefineProperties(Filer:TFiler);
begin
  inherited;
  Filer.DefineProperty('FilterItems',DoReadFilters,DoWriteFilters,FiltersStored); // Do not localize
end;

procedure TTeePicture.Assign(Source: TPersistent);
begin
  if Source is TTeePicture then
     Filters:=TTeePicture(Source).FFilters;

  inherited;
end;

function TTeePicture.Filtered: TGraphic;
begin
  if (not FiltersStored) or (not Assigned(Graphic)) then
     result:=Graphic
  else
  begin
    if not Assigned(IBitmap) then
       IBitmap:=TBitmap.Create{$IFNDEF D19}{$IFDEF FMX}(0,0){$ENDIF}{$ENDIF};

    AssignGraphic(IBitmap,Graphic);

    if Assigned(FFilters) then
       FFilters.ApplyTo(IBitmap);

    result:=IBitmap;
  end;
end;

function TTeePicture.FiltersStored:Boolean;
begin
  result:=Assigned(FFilters) and (FFilters.Count>0);
end;

function TTeePicture.GetFilters:TFilterItems;
begin
  if not Assigned(FFilters) then
     FFilters:=TFilterItems.Create(Self,TTeeFilter);

  result:=FFilters;
end;

type
  TReaderAccess=class(TReader);

class procedure TTeePicture.ReadFilters(const Reader:TReader; const Filters:TFilterItems);
var
  Item: TTeeFilter;
  tmp : String;
  tmpClass : TFilterClass;
begin
  Filters.BeginUpdate;
  try
    if not Reader.EndOfList then
       Filters.Clear;

    Reader.ReadValue;

    while not Reader.EndOfList do
    begin
      if Reader.NextValue in [vaInt8, vaInt16, vaInt32] then
         Reader.ReadInteger;

      Reader.ReadListBegin;

      {$IFDEF FPC}
      tmp:=Reader.Driver.ReadStr;
      {$ELSE}
      tmp:=Reader.ReadStr;
      {$ENDIF}

      tmp:=Reader.ReadString;

      tmpClass:=TFilterClass(FindClass(tmp));
      Item := Filters.Add(tmpClass);

      while not Reader.EndOfList do
            TReaderAccess(Reader).ReadProperty(Item);

      Reader.ReadListEnd;
    end;

    Reader.ReadListEnd;
  finally
    Filters.EndUpdate;
  end;
end;

procedure TTeePicture.DoReadFilters(Reader: TReader);
begin
  ReadFilters(Reader,Filters);
end;

{$IFDEF LCL}
type
  TBinaryObjectWriterAccess=class(TBinaryObjectWriter);

  TFilerAccess=class(TWriter)
  private
    {$HINTS OFF}
    FDriver: Pointer;
    FDestroyDriver: Boolean;
    FRootAncestor: TComponent;
    {$HINTS ON}
    FPropPath: string;
  end;

  TWriterAccess=class(TWriter);
  
{$ELSE}
{$IFNDEF D22}
type
  TFilerAccess=class(TFiler)
  private
    {$HINTS OFF}
    FRootAncestor: TComponent;
    {$HINTS ON}
    FPropPath: string;
  end;

  TWriterAccess=class(TWriter);
{$ENDIF}
{$ENDIF}

class procedure TTeePicture.WriteFilters(const Writer: TWriter; const Filters:TFilterItems);
var
  t : Integer;
  v : TValueType;

  {$IFDEF D22}
  tmpRtti : TRttiContext;
  {$ENDIF}
begin
  v:=vaCollection;

  {$IFDEF LCL}
  TBinaryObjectWriterAccess(Writer.Driver).Write(v, SizeOf(v));
  {$ELSE}
  Writer.Write(v, SizeOf(v));
  {$ENDIF}

  {$IFDEF D22}
  tmpRtti:=TRttiContext.Create;
  {$ENDIF}

  for t:=0 to Filters.Count-1 do
  begin
    Writer.WriteListBegin;

    {$IFDEF D22}
    // Use RTTI from XE8 and up, (no private access using "helper", and
    // no guarantee of private fields order anymore, compiler can re-order them)
    tmpRtti.GetType(TWriter).GetField('FPropPath').SetValue(Writer,'');
    {$ELSE}

    // EXTREME HACK ! SHOULD BE VERIFIED EVERY NEW RTL VERSION
    TFilerAccess(Writer).FPropPath:='';
    {$ENDIF}

    {$IFDEF LCL}
    TBinaryObjectWriterAccess(Writer.Driver).WriteStr('ItemClass');  // Do not localize
    {$ELSE}
    Writer.{$IFDEF FMX}WriteString{$ELSE}WriteStr{$ENDIF}('ItemClass'); // Do not localize
    {$ENDIF}

    Writer.WriteString(Filters[t].ClassName);

    {$IFDEF LCL}
    TWriterAccess(Writer).WriteProperties(Filters[t]);
    {$ELSE}
    {$IFNDEF D22}TWriterAccess{$ENDIF}(Writer).WriteProperties(Filters[t]);
    {$ENDIF}

    Writer.WriteListEnd;
  end;

  Writer.WriteListEnd;
end;

procedure TTeePicture.DoWriteFilters(Writer: TWriter);
begin
  WriteFilters(Writer,Filters);
end;

procedure TTeePicture.SetFilters(const Value: TFilterItems);
begin
  if Assigned(Value) then Filters.Assign(Value)
                     else FreeAndNil(FFilters);
end;

procedure TTeePicture.Repaint;
begin
  {$IFDEF FMX}
  {$IFDEF D17}
  if Assigned(OnChange) then
     OnChange(Self);
  {$ELSE}
  BitmapChanged;
  {$ENDIF}
  {$ELSE}
  Changed(Self);
  {$ENDIF}
end;

function TTeePicture.Valid:Boolean;
begin
  result:=Assigned(Graphic) {$IFDEF FMX}and (not Graphic.IsEmpty){$ENDIF};
end;

class procedure TTeePicture.AssignGraphic(const ABitmap:TBitmap; const AGraphic:TGraphic);
begin
  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  if AGraphic is TIcon then
  begin
    TeeSetBitmapSize(ABitmap,AGraphic.Width,AGraphic.Height);
    DrawIconEx(ABitmap.Canvas.Handle, 0,0, TIcon(AGraphic).Handle, 0, 0, 0, 0, DI_NORMAL);
  end
  else
  {$ENDIF}
  {$ENDIF}
    ABitmap.Assign(AGraphic);
end;

{ TFloatXY }

procedure TFloatXY.SetSingleProperty(var Variable:Single; const Value:Single);
begin
  if Variable<>Value then
  begin
    Variable:=Value;

    if Assigned(FOnChange) then
       FOnChange(Self);
  end;
end;

function TFloatXY.IsXStored:Boolean;
begin
  result:=FX<>IDefaultX;
end;

function TFloatXY.IsYStored:Boolean;
begin
  result:=FY<>IDefaultY;
end;

Procedure TFloatXY.SetX(Const Value:Single);
begin
  SetSingleProperty(FX,Value);
end;

Procedure TFloatXY.SetY(Const Value:Single);
begin
  SetSingleProperty(FY,Value);
end;

Procedure TFloatXY.Assign(Source:TPersistent);
begin
  if Source is TFloatXY then
  With TFloatXY(Source) do
  begin
    Self.FX:=X;
    Self.FY:=Y;
  end
  else inherited;
end;

function TFloatXY.Point:TPointFloat;
begin
  result.X:=FX;
  result.Y:=FY;
end;

{ TFloatXYZ }

function TFloatXYZ.IsZStored:Boolean;
begin
  result:=FZ<>IDefaultZ;
end;

Procedure TFloatXYZ.SetZ(Const Value:Single);
begin
  SetSingleProperty(FZ,Value);
end;

function TFloatXYZ.Point:TPoint3DFloat;
begin
  result.X:=FX;
  result.Y:=FY;
  result.Z:=FZ;
end;

Procedure TFloatXYZ.Assign(Source:TPersistent);
begin
  if Source is TFloatXYZ then
     FZ:=TFloatXYZ(Source).FZ;

  inherited;
end;

{ TConvolveFilter }

procedure TConvolveFilter.Apply(const Bitmap:TBitmap; const R:TRectI);

  procedure CopyBitmap;
  {$IFDEF FMX}
    // Copy BufferFilter to LinesRGBA
  begin
  end;
  {$ELSE}
  var y        : Integer;
      tmpWSize : Integer;
      tmpXOff  : TeeNativeInt;
  begin
    if RGBA then
    begin
      tmpWSize:=SizeOf(TRGBA)*(Rect.Right-Rect.Left-1);
      tmpXOff:=SizeOf(TRGBA)*(Rect.Left+1);

      {$IFDEF MSWINDOWS}
      for y:=Rect.Top+1 to Rect.Bottom-1 do
          CopyMemory(Pointer(TeeNativeInt(LinesRGBA[y])+tmpXOff),
                     Pointer(TeeNativeInt(BufferFilter.LinesRGBA[y-Rect.Top])+SizeOf(TRGBA)),tmpWSize);
      {$ENDIF}
    end
    else
    begin
      tmpWSize:=SizeOf(TRGB)*(Rect.Right-Rect.Left-1);
      tmpXOff:=SizeOf(TRGB)*(Rect.Left+1);

      {$IFDEF MSWINDOWS}
      for y:=Rect.Top+1 to Rect.Bottom-1 do
          CopyMemory(Pointer(TeeNativeInt(Lines[y])+tmpXOff),
                     Pointer(TeeNativeInt(BufferFilter.Lines[y-Rect.Top])+SizeOf(TRGB)),tmpWSize);
      {$ENDIF}
    end;
  end;
  {$ENDIF}

var {$IFNDEF FMX}
    Prev,
    This : PRGBs;
    {$ENDIF}

    PrevA,
    ThisA : PRGBAs;

  procedure DoBlurNoWeight;
  {$IFDEF FMX}
  begin

  end;
  {$ELSE}

  var x,y   : Integer;

      ThisBuffer,
      Next  : PRGBs;

      ThisBufferA,
      NextA : PRGBAs;
  begin
    if RGBA then
    for y:=Rect.Top+1 to Rect.Bottom-1 do
    begin
      NextA:=LinesRGBA[y+1];
      ThisBufferA:=BufferFilter.LinesRGBA[y-Rect.Top];

      for x:=Rect.Left+1 to Rect.Right-1 do

      {$IFOPT R+}
      {$DEFINE WASRANGE}
      {$R-}
      {$ENDIF}

      with ThisBufferA[x-Rect.Left] do
      begin
        Blue:=(PrevA[x-1].Blue+PrevA[x].Blue+PrevA[x+1].Blue+
               ThisA[x-1].Blue+ThisA[x].Blue+ThisA[x+1].Blue+
               NextA[x-1].Blue+NextA[x].Blue+NextA[x+1].Blue) div 9;

        Green:=(PrevA[x-1].Green+PrevA[x].Green+PrevA[x+1].Green+
               ThisA[x-1].Green+ThisA[x].Green+ThisA[x+1].Green+
               NextA[x-1].Green+NextA[x].Green+NextA[x+1].Green) div 9;

        Red:=(PrevA[x-1].Red+PrevA[x].Red+PrevA[x+1].Red+
               ThisA[x-1].Red+ThisA[x].Red+ThisA[x+1].Red+
               NextA[x-1].Red+NextA[x].Red+NextA[x+1].Red) div 9;
      end;

      {$IFDEF WASRANGE}
      {$R+}
      {$ENDIF}

      PrevA:=ThisA;
      ThisA:=NextA;
    end
    else
    for y:=Rect.Top+1 to Rect.Bottom-1 do
    begin
      Next:=Lines[y+1];
      ThisBuffer:=BufferFilter.Lines[y-Rect.Top];

      for x:=Rect.Left+1 to Rect.Right-1 do

      {$IFOPT R+}
      {$DEFINE WASRANGE}
      {$R-}
      {$ENDIF}

      with ThisBuffer[x-Rect.Left] do
      begin
        Blue:=(Prev[x-1].Blue+Prev[x].Blue+Prev[x+1].Blue+
               This[x-1].Blue+This[x].Blue+This[x+1].Blue+
               Next[x-1].Blue+Next[x].Blue+Next[x+1].Blue) div 9;

        Green:=(Prev[x-1].Green+Prev[x].Green+Prev[x+1].Green+
               This[x-1].Green+This[x].Green+This[x+1].Green+
               Next[x-1].Green+Next[x].Green+Next[x+1].Green) div 9;

        Red:=(Prev[x-1].Red+Prev[x].Red+Prev[x+1].Red+
               This[x-1].Red+This[x].Red+This[x+1].Red+
               Next[x-1].Red+Next[x].Red+Next[x+1].Red) div 9;
      end;

      {$IFDEF WASRANGE}
      {$R+}
      {$ENDIF}

      Prev:=This;
      This:=Next;
    end;
  end;
  {$ENDIF}

  procedure DoBlurWeight;
  var x,y : Integer;
      tmp : Integer;
      tmpLeft : Integer;
      tmpTop  : Integer;

      {$IFNDEF FMX}
      Next  : PRGBs;
      {$ENDIF}

      NextA : PRGBAs;
      InvTotalWeight : Single;
  begin
    InvTotalWeight:=1.0 / ( Weights[-1,-1]+Weights[-1, 0]+Weights[-1, 1]+
                            Weights[ 0,-1]+Weights[ 0, 0]+Weights[ 0, 1]+
                            Weights[ 1,-1]+Weights[ 1, 0]+Weights[ 1, 1] ) ;

    tmpTop:={$IFDEF FMX}Round{$ENDIF}(Rect.Top);
    tmpLeft:={$IFDEF FMX}Round{$ENDIF}(Rect.Left);

    {$IFOPT R+}
    {$DEFINE WASRANGE}
    {$R-}
    {$ENDIF}

    if RGBA then
    for y:=tmpTop+1 to {$IFDEF FMX}Round{$ENDIF}(Rect.Bottom)-1 do
    begin
      NextA:=LinesRGBA[y+1];

      for x:=tmpLeft+1 to {$IFDEF FMX}Round{$ENDIF}(Rect.Right)-1 do
      with BufferFilter.LinesRGBA[y-tmpTop,x-tmpLeft] do
      begin
        tmp:=Round(
             ( (Weights[-1,-1]*PrevA[x-1].Blue)+
               (Weights[-1, 0]*PrevA[x].Blue)+
               (Weights[-1, 1]*PrevA[x+1].Blue)+
               (Weights[ 0,-1]*ThisA[x-1].Blue)+
               (Weights[ 0, 0]*ThisA[x].Blue)+
               (Weights[ 0, 1]*ThisA[x+1].Blue)+
               (Weights[ 1,-1]*NextA[x-1].Blue)+
               (Weights[ 1, 0]*NextA[x].Blue)+
               (Weights[ 1, 1]*NextA[x+1].Blue)
              )*InvTotalWeight);

        if tmp>255 then Blue:=255 else
        if tmp<0 then Blue:=0 else
           Blue:=tmp;

        tmp:=Round((
               (Weights[-1,-1]*PrevA[x-1].Green)+
               (Weights[-1, 0]*PrevA[x].Green)+
               (Weights[-1, 1]*PrevA[x+1].Green)+
               (Weights[ 0,-1]*ThisA[x-1].Green)+
               (Weights[ 0, 0]*ThisA[x].Green)+
               (Weights[ 0, 1]*ThisA[x+1].Green)+
               (Weights[ 1,-1]*NextA[x-1].Green)+
               (Weights[ 1, 0]*NextA[x].Green)+
               (Weights[ 1, 1]*NextA[x+1].Green)
              )*InvTotalWeight);

        if tmp>255 then Green:=255 else
        if tmp<0 then Green:=0 else
           Green:=tmp;

        tmp:=Round((
               (Weights[-1,-1]*PrevA[x-1].Red)+
               (Weights[-1, 0]*PrevA[x].Red)+
               (Weights[-1, 1]*PrevA[x+1].Red)+
               (Weights[ 0,-1]*ThisA[x-1].Red)+
               (Weights[ 0, 0]*ThisA[x].Red)+
               (Weights[ 0, 1]*ThisA[x+1].Red)+
               (Weights[ 1,-1]*NextA[x-1].Red)+
               (Weights[ 1, 0]*NextA[x].Red)+
               (Weights[ 1, 1]*NextA[x+1].Red)
              )*InvTotalWeight);

        if tmp>255 then Red:=255 else
        if tmp<0 then Red:=0 else
           Red:=tmp;
      end;

      PrevA:=ThisA;
      ThisA:=NextA;
    end
    {$IFNDEF FMX}
    else
    for y:=Rect.Top+1 to Rect.Bottom-1 do
    begin
      Next:=Lines[y+1];

      for x:=Rect.Left+1 to Rect.Right-1 do
      with BufferFilter.Lines[y-Rect.Top,x-Rect.Left] do
      begin
        tmp:=Round(
             ( (Weights[-1,-1]*Prev[x-1].Blue)+
               (Weights[-1, 0]*Prev[x].Blue)+
               (Weights[-1, 1]*Prev[x+1].Blue)+
               (Weights[ 0,-1]*This[x-1].Blue)+
               (Weights[ 0, 0]*This[x].Blue)+
               (Weights[ 0, 1]*This[x+1].Blue)+
               (Weights[ 1,-1]*Next[x-1].Blue)+
               (Weights[ 1, 0]*Next[x].Blue)+
               (Weights[ 1, 1]*Next[x+1].Blue)
              )*InvTotalWeight);

        if tmp>255 then Blue:=255 else
        if tmp<0 then Blue:=0 else
           Blue:=tmp;

        tmp:=Round((
               (Weights[-1,-1]*Prev[x-1].Green)+
               (Weights[-1, 0]*Prev[x].Green)+
               (Weights[-1, 1]*Prev[x+1].Green)+
               (Weights[ 0,-1]*This[x-1].Green)+
               (Weights[ 0, 0]*This[x].Green)+
               (Weights[ 0, 1]*This[x+1].Green)+
               (Weights[ 1,-1]*Next[x-1].Green)+
               (Weights[ 1, 0]*Next[x].Green)+
               (Weights[ 1, 1]*Next[x+1].Green)
              )*InvTotalWeight);

        if tmp>255 then Green:=255 else
        if tmp<0 then Green:=0 else
           Green:=tmp;

        tmp:=Round((
               (Weights[-1,-1]*Prev[x-1].Red)+
               (Weights[-1, 0]*Prev[x].Red)+
               (Weights[-1, 1]*Prev[x+1].Red)+
               (Weights[ 0,-1]*This[x-1].Red)+
               (Weights[ 0, 0]*This[x].Red)+
               (Weights[ 0, 1]*This[x+1].Red)+
               (Weights[ 1,-1]*Next[x-1].Red)+
               (Weights[ 1, 0]*Next[x].Red)+
               (Weights[ 1, 1]*Next[x+1].Red)
              )*InvTotalWeight);

        if tmp>255 then Red:=255 else
        if tmp<0 then Red:=0 else
           Red:=tmp;
      end;

      Prev:=This;
      This:=Next;
    end
    {$ENDIF};

    {$IFDEF WASRANGE}
    {$R+}
    {$ENDIF}
  end;

var tmpNoWeight : Boolean;
begin
  inherited;

  if Empty then
     Exit;

  with R do
  begin
    if Top<0 then Rect.Top:=0 else Rect.Top:=Top;
    if Left<0 then Rect.Left:=0 else Rect.Left:=Left;

    if Bottom>Bitmap.Height-1 then
       Rect.Bottom:=Bitmap.Height-1
    else
       Rect.Bottom:=Bottom;

    if Right>Bitmap.Width-1 then
       Rect.Right:=Bitmap.Width-1
    else
       Rect.Right:=Right;
  end;

  if RGBA then
  begin
    PrevA:=LinesRGBA[{$IFDEF FMX}Round{$ENDIF}(Rect.Top)];
    ThisA:=LinesRGBA[{$IFDEF FMX}Round{$ENDIF}(Rect.Top+1)];

    {$IFNDEF FMX}
    {$IFNDEF D7}
    Prev:=nil;
    This:=nil;
    {$ENDIF}
    {$ENDIF}
  end
  {$IFNDEF FMX}
  else
  begin
    Prev:=Lines[Rect.Top];
    This:=Lines[Rect.Top+1];

    {$IFNDEF D7}
    PrevA:=nil;
    ThisA:=nil;
    {$ENDIF}
  end
  {$ENDIF};

  if not KeepBuffer then
     CreateBuffer({$IFDEF FMX}Round{$ENDIF}(Rect.Right-Rect.Left),
                  {$IFDEF FMX}Round{$ENDIF}(Rect.Bottom-Rect.Top));

  tmpNoWeight:=(Weights[-1,-1]=1) and (Weights[-1, 0]=1) and (Weights[-1, 1]=1) and
               (Weights[ 0,-1]=1) and (Weights[ 0, 0]=1) and (Weights[ 0, 1]=1) and
               (Weights[ 1,-1]=1) and (Weights[ 1, 0]=1) and (Weights[ 1, 1]=1);

  if tmpNoWeight then
     DoBlurNoWeight
  else
     DoBlurWeight;

  CopyBitmap;

  if not KeepBuffer then
     FreeBuffer;
end;

procedure TConvolveFilter.CreateBuffer(AWidth,AHeight:Integer);
begin
  BufferFilter:=TTeeFilter.Create(nil);
  BufferFilter.RGBA:=RGBA;

  {$IFDEF FMX}
  BufferBitmap:=TBitmap.Create(AWidth,AHeight);
  {$ELSE}
  BufferBitmap:=TBitmap.Create;
  TeeSetBitmapSize(BufferBitmap,AWidth,AHeight);
  {$ENDIF}

  BufferFilter.Apply(BufferBitmap);
end;

procedure TConvolveFilter.FreeBuffer;
begin
  BufferBitmap.Free;
  BufferFilter.Free;
end;

{ TBlurFilter }

Constructor TBlurFilter.Create(Collection:TCollection);
begin
  inherited;
  FAmount:=1;
  FSteps:=1;
end;

procedure TBlurFilter.Apply(const Bitmap:TBitmap; const R: TRectI);
var tmp : Single;
    t   : Integer;
begin
  tmp:=1;

  Weights[-1,-1]:=1; Weights[-1,0]:=tmp; Weights[-1,1]:=1;
  Weights[ 0,-1]:=tmp; Weights[ 0,0]:=Amount*tmp; Weights[ 0,1]:=tmp;
  Weights[ 1,-1]:=1; Weights[ 1,0]:=tmp; Weights[ 1,1]:=1;

  RGBA:={$IFDEF FMX}True{$ELSE}TeeBitsperPixel(Bitmap.Canvas.Handle)=32{$ENDIF};

  CalcLines(Bitmap);
  ReuseLines:=True;

  KeepBuffer:=True;
  CreateBuffer({$IFDEF FMX}Round{$ENDIF}(Abs(R.Right-R.Left)),
               {$IFDEF FMX}Round{$ENDIF}(Abs(R.Bottom-R.Top)));
  try
    for t:=1 to Steps do
        inherited;
  finally
    FreeBuffer;
  end;

  ReuseLines:=False;
end;

class function TBlurFilter.Description: String;
begin
  result:=TeeMsg_Blur;
end;

procedure TBlurFilter.CreateEditor(Creator:IFormCreator; AChanged:TNotifyEvent);
begin
  inherited;
  Creator.AddScroll('Steps',1,20);  // Do not localize
  Creator.AddScroll('Amount',0,50); // Do not localize
end;

function TeeResourceStream(const AName:String):TResourceStream;

  function FoundAt(AInstance:HINST; var Found:Boolean):Boolean;      //#1898 change to HINST (x64 issue) from THandle
  begin
    {$IFDEF FMX}
    Found:=FindResource(AInstance,PChar(AName),RT_RCDATA) <> 0;
    {$ELSE}
    Found:=FindResource(AInstance,{$IFDEF D12}PWideChar{$ELSE}PAnsiChar{$ENDIF}(AName),RT_RCDATA) <> 0;
    {$ENDIF}

    result:=Found;
  end;

var {$IFDEF MSWINDOWS}LibModule : PLibModule;{$ENDIF}
    tmpHandle : THandle;
    tmpFound  : Boolean;
begin
  tmpFound:=False;

  {$IFNDEF FPC}
  if FoundAt(MainInstance,tmpFound) then
     tmpHandle:=MainInstance
  else
  {$ENDIF}
  if FoundAt(HInstance,tmpFound) then
     tmpHandle:=HInstance
  else
  begin
    tmpHandle:=0;

    {$IFDEF MSWINDOWS}
    LibModule := LibModuleList;

    while LibModule <> nil do
    begin
      with LibModule^ do
      begin
        if FoundAt(Instance,tmpFound) then
        begin
          tmpHandle:=Instance;
          break;
        end;
      end;

      LibModule := LibModule.Next;
    end;
    {$ENDIF}
  end;

  if not tmpFound then
     result:=nil
  else
     result:=TResourceStream.Create(tmpHandle,AName,RT_RCDATA);
end;

{$IFNDEF D6}
function Supports(const Instance: IInterface; const IID: TGUID): Boolean;
var Temp : IInterface;
begin
  result := (Instance <> nil) and (Instance.QueryInterface(IID, Temp) = 0);
end;
{$ENDIF}

{ TTeeEmboss }

Constructor TTeeEmboss.Create(AOnChange: TNotifyEvent);
begin
  inherited;

  FVisible:=False;
  DefaultVisible:=FVisible;

  FDirection:=sdLeftTop;
end;

function TTeePen.IsWidthStored: Boolean;
begin
  result:=Width<>DefaultWidth;
end;

{$IFDEF FMX}
function TTeePen.GetColor: TColor;
begin
  result:=Fill.Color;
end;

procedure TTeePen.SetColor(const Value: TColor);
begin
  if Fill.Color<>Value then
  begin
    FFill.Color:=Value;
    Changed;
  end;
end;

procedure TTeePen.SetStyle(const Value: TPenStyle);
begin
  if FStyle<>Value then
  begin
    FStyle := Value;
    Changed;
  end;
end;

procedure TTeePen.SetWidth(const Value: Single);
begin
  if FWidth<>Value then
  begin
    FWidth := Value;
    Changed;
  end;
end;

{ TPicture }

constructor TPicture.Create;
begin
  inherited Create(0,0);
end;

function TPicture.GetBitmap: TBitmap;
begin
  result:=Self;
end;

procedure TPicture.SetBitmap(const Value: TBitmap);
begin
  if Assigned(Value) then
     Assign(Value)
  else
  begin
    Width:=0;
    Height:=0;
  end;
end;
{$ENDIF}

procedure TeeActivateGroup;
begin
  {$IFDEF D6}
  {$IFDEF FMX}
  StartClassGroup(TFmxObject);
  ActivateClassGroup(TFmxObject);
  {$ELSE}
  StartClassGroup(TControl);
  ActivateClassGroup(TControl);
  {$ENDIF}
  {$ENDIF}
end;

function TeeGetClass(const AClassName:String):TClass;
begin
  TeeActivateGroup;
  result:=GetClass(AClassName);
end;

function ObjectToTag(const AObject:TObject):TeeNativeInt; {$IFDEF D9}inline;{$ENDIF}
begin
  result:=TeeNativeInt(AObject);
end;

{$IFDEF TEEVCL}

{ EditColor }
Function EditColor(const AOwner:TComponent; AColor:TColor; const AOptions:TColorDialogOptions):TColor;
{$IFDEF FMX}

var tmpForm : TForm;
    tmp     : TColorPanel;
    tmpP    : TPanel;
    tmpB    : TButton;

{$ENDIF}
Begin
  {$IFDEF FMX}
  tmpForm:=TForm.CreateNew(AOwner);
  try
    {$IFDEF D19}
    tmpForm.BorderStyle:=TFmxFormBorderStyle.{$IFDEF D20}Sizeable{$ELSE}bsSizeable{$ENDIF};
    {$ENDIF}

    tmp:=TColorPanel.Create(tmpForm);
    tmp.Color:=AColor;
    tmp.Align:=TAlignLayout.{$IFDEF D20}Client{$ELSE}alClient{$ENDIF};

    {$IFDEF D18}
    tmp.Margins.Rect:=RectF(10,10,10,10);
    {$ELSE}
    tmp.Padding.Rect:=RectF(10,10,10,10);
    {$ENDIF}

    tmpForm.AddObject(tmp);

    tmpForm.Position:=TFormPosition.{$IFDEF D20}MainFormCenter{$ELSE}{$IFDEF D19}poMainFormCenter{$ELSE}poScreenCenter{$ENDIF}{$ENDIF};

    tmpP:=TPanel.Create(tmpForm);
    tmpP.Height:=38;
    tmpP.Align:=TAlignLayout.{$IFDEF D20}Bottom{$ELSE}alBottom{$ENDIF};

    tmpB:=TButton.Create(tmpForm);
    tmpB.Align:=TAlignLayout.{$IFDEF D20}Right{$ELSE}alRight{$ENDIF};
    tmpB.Text:='Cancel';

    {$IFDEF D18}
    tmpB.Margins.Rect:=RectF(10,8,10,8);
    {$ELSE}
    tmpB.Padding.Rect:=RectF(10,8,10,8);
    {$ENDIF}

    tmpB.ModalResult:=mrCancel;
    tmpP.AddObject(tmpB);

    tmpB:=TButton.Create(tmpForm);
    tmpB.Align:=TAlignLayout.{$IFDEF D20}Right{$ELSE}alRight{$ENDIF};
    tmpB.Text:='OK';

    {$IFDEF D18}
    tmpB.Margins.Rect:=RectF(10,8,10,8);
    {$ELSE}
    tmpB.Padding.Rect:=RectF(10,8,10,8);
    {$ENDIF}

    tmpB.ModalResult:=mrOk;
    tmpP.AddObject(tmpB);

    tmpForm.AddObject(tmpP);

    {$IFDEF D19}
    tmpForm.ClientWidth:=240;
    tmpForm.ClientHeight:=280;
    {$ELSE}
    tmpForm.Width:=240;
    tmpForm.Height:=280;
    {$ENDIF}

    {$IFDEF ANDROID}
    tmpForm.ShowModal(procedure(ModalResult: TModalResult)
                      begin
                        // Problem: Result cannot be set here
                        // A callback proc should be called instead of
                        // returning True or False.

                        (*
                        if ModalResult = mrOK then
                           result:=tmp.Color
                        else
                           result:=AColor;
                        *)

                        tmpForm.DisposeOf;
                      end);

    // ANDROID PROBLEM, we should call a callback passing tmp.Color
    // instead of this:
    result:=AColor;

    {$ELSE}
    if tmpForm.ShowModal=mrOk then
       result:=tmp.Color
    else
       result:=AColor;
    {$ENDIF}

  finally
    {$IFNDEF ANDROID}
    tmpForm.Free;
    {$ENDIF}
  end;
  {$ELSE}
  With TColorDialog.Create(AOwner) do
  try
    Color:=AColor;

    {$IFNDEF LCL}
    Options:=AOptions;
    {$ENDIF}

    {$IFNDEF LCL}
    if not Assigned(TeeCustomEditColors) then
       TeeCustomEditColors:=TStringList.Create
    else
       CustomColors:=TeeCustomEditColors;
    {$ENDIF}

    if Execute then
    begin
      {$IFNDEF LCL}
      TeeCustomEditColors.Assign(CustomColors);
      {$ENDIF}
      result:=Color;
    end
    else
      result:=AColor;
  finally
    Free;
  end;
  {$ENDIF}
end;

Function EditColor(const AOwner:TComponent; AColor:TColor):TColor;
begin
  result:=EditColor(AOwner,AColor,[{$IFNDEF FMX}{$IFDEF LCL}cdExtended{$ELSE}cdFullOpen{$ENDIF}{$ENDIF}]);
end;

{ Show the TColorDialog, return True if color changed }
Function EditColorDialog(const AOwner:TComponent; var AColor:TColor;
                         const AOptions:TColorDialogOptions=[]):Boolean;
var tmpNew : TColor;
begin
  tmpNew:=EditColor(AOwner,AColor,AOptions);

  result:=tmpNew<>AColor;

  if result then
     AColor:=tmpNew;
end;

{$IFDEF TEEVCL}
{$IFNDEF FMX}

{ TTeeButton }

procedure TTeeButton.SetSymbolVisible(Const Value:Boolean);
begin
  if FSymbolVisible<>Value then
  begin
    FSymbolVisible:=Value;
    {$IFDEF FMX}Repaint{$ELSE}Invalidate{$ENDIF};
  end;
end;

procedure TTeeButton.SetSymbolWidth(const Value:Integer);
begin
  if FSymbolWidth<>Value then
  begin
    FSymbolWidth:=Value;
    Invalidate;
  end;
end;

Procedure TTeeButton.DrawSymbol(const ACanvas:TTeeCanvas); // virtual; abstract;
begin
end;

function TTeeButton.SymbolRectangle:TRect;
var tmpH : TCoordinate;
    tmpW : Integer;
begin
  tmpH:={$IFDEF FMX}Height*0.25{$ELSE}Height div 4{$ENDIF};

  if FSymbolWidth=0 then
     if Caption='' then
        tmpW:=0
     else
        tmpW:=19
  else
     tmpW:=FSymbolWidth;

  if tmpW=0 then
  begin
    tmpW:=Max(4,Width div 8);
    result:=TeeRect(tmpW,Height-3*tmpH,Width-tmpW,Height-tmpH);
  end
  else
     result:=TeeRect(Width-tmpW,Height-3*tmpH,Width-6,Height-tmpH);
end;

{$IFDEF MSWINDOWS}
procedure TTeeButton.WMPaint(var Message: TWMPaint);
begin
  ControlState:=ControlState+[csCustomPaint];
  inherited;
  ControlState:=ControlState-[csCustomPaint];
end;
{$ENDIF}

{$IFDEF MSWINDOWS}
procedure TTeeButton.CMTextChanged(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;

procedure TTeeButton.PaintWindow(DC: HDC);
var tmp : TTeeCanvas;
begin
  inherited;

  tmp:=TTeeCanvas3D.Create;
  with tmp do
  try
    ReferenceCanvas:=TCanvas.Create;
    try
      ReferenceCanvas.Handle:=DC;
      DrawSymbol(tmp);
    finally
      ReferenceCanvas.Free;
    end;
  finally
    Free;
  end;
end;

procedure TTeeButton.CMEnabledChanged(var Message: TMessage);
begin
  inherited;
  Repaint;
end;
{$ENDIF}

constructor TTeeButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FSymbolVisible := True;
end;

procedure TTeeButton.LinkProperty(AInstance: TObject;
  const PropName: String);
begin
  Instance:=AInstance;
  Info:=nil;

  if Assigned(Instance) then
  begin
    if PropName<>'' then
       Info:=GetPropInfo(Instance,PropName);

    Invalidate;
  end;
end;
{$ENDIF}
{$ENDIF}

{$IFNDEF FMX}

{ TButtonColor }

Constructor TButtonColor.Create(AOwner:TComponent);
begin
  inherited;
  FFullOpen:=True;

  {$IFDEF LCL}
  Height:=25;
  Width:=75;
  {$ENDIF}
end;

procedure TButtonColor.Click;
var tmp   : TColor;
    tmpOp : TColorDialogOptions;
begin
  tmp:=GetSymbolColor;

  {$IFNDEF FMX}
  {$IFNDEF LCL}
  if FullOpen then
     tmpOp:=[cdFullOpen]
  else
  {$ENDIF}
  {$ENDIF}
     tmpOp:=[];

  {$IFDEF TEEOCX}               {TA05010881}
  if ColorPickerClosed then
  begin
    ColorPickerClosed:=False;
    Enabled:=False;

    if EditColorDialog(Self,tmp,tmpOp) then
    begin
      SymbolColor:=tmp;
      Repaint;

      inherited;
    end;

    ColorPickerClosed:=True;
    Enabled:=True;
  end;
  {$ELSE}
  if EditColorDialog(Self,tmp,tmpOp) then
  begin
    SymbolColor:=tmp;
    Repaint;

    inherited;
  end;
  {$ENDIF}
end;

Function TButtonColor.GetSymbolColor:TColor;
begin
  if Assigned(GetColorProc) then
     result:=GetColorProc
  else
  if Assigned(Info) then result:=GetOrdProp(Instance,Info)
                    else result:=FSymbolColor;
end;

procedure TButtonColor.SetSymbolColor(Const Value:TColor);
begin
  if FSymbolColor<>Value then
  begin
    if Assigned(Info) then SetOrdProp(Instance,Info,Value)
                      else FSymbolColor:=Value;

    Invalidate;
  end;
end;

procedure TButtonColor.DrawSymbol(const ACanvas:TTeeCanvas);
begin
  if Enabled and FSymbolVisible then
  with ACanvas do
  begin
    FSymbolColor:=SymbolColor; //TV52014770

    if FSymbolColor<>clNone then
    begin
      //AssignBrush(Brush,TTeeCanvas.ColorFrom(FSymbolColor,0));

      Brush.Color:=TTeeCanvas.ColorFrom(FSymbolColor,0);

      if not Enabled then
         Pen.Style:=psClear;

      Rectangle(SymbolRectangle);
    end;
  end;
end;

{ TComboFlat }
constructor TComboFlat.Create(AOwner: TComponent);
begin
  inherited;
  Height:=21;
  ItemHeight:=13;
  Style:=csDropDownList;

  {$IFNDEF FMX}
  {$IFDEF D9}  // Delphi 2005,2006,2007...
  if (SysLocale.PriLangID=LANG_JAPANESE) then
  begin
     Font.Name:={TeCanvas.}GetDefaultFontName;
     Font.Size:={TeCanvas.}GetDefaultFontSize;
  end;
  {$ENDIF}
  {$ENDIF}
end;

procedure TComboFlat.Add(const Item:String);
begin
  Items.Add(Item);
end;

function TComboFlat.GetDropWidth:Integer;
begin
  {$IFDEF MSWINDOWS}
  HandleNeeded;
  result:=Perform(CB_GETDROPPEDWIDTH, 0, 0);
  {$ELSE}
  result:=Width;
  {$ENDIF}
end;

function TComboFlat.IsDropWidthStored:Boolean;
begin
  result:=Width<>DropDownWidth;
end;

procedure TComboFlat.SetDropWidth(const Value:Integer);
begin
  {$IFDEF MSWINDOWS}
  HandleNeeded;
  Perform(CB_SETDROPPEDWIDTH, Value, 0);
  {$ENDIF}
end;

{$IFNDEF D6}
procedure TComboFlat.AddItem(Item: String; AObject: TObject);
begin
  Items.AddObject(Item, AObject);
end;
{$ENDIF}

function TComboFlat.CurrentItem:String;
begin
  if ItemIndex=-1 then result:=''
                  else result:=Items[ItemIndex];
end;

function TComboFlat.SelectedObject:TObject;
begin
  if ItemIndex=-1 then result:=nil
                  else result:=Items.Objects[ItemIndex];
end;

{$IFNDEF LCL}
procedure TComboFlat.WMPaint(var Message: TWMPaint);
var R : TRect;
begin
  inherited;

  if (not Inside) and (not Focused) then
  begin
    with TControlCanvas.Create do
    try
      Control:=Self;
      Pen.Color:=clBtnFace;
      Brush.Style:=bsClear;
      R:=ClientRect;

      Rectangle(R);

      InflateRect(R,-1,-1);

      Rectangle(R);

      R.Left:=R.Right-GetSystemMetrics(SM_CYSIZE)+2; // SM_CXSIZE-2;

      Inc(R.Top);
      Dec(R.Bottom);
      Pen.Color:=clWindow;

      Rectangle(R);

      MoveTo(R.Right-1,R.Top);
      LineTo(R.Right-1,R.Bottom);
    finally
      Free;
    end;
  end;
end;

procedure TComboFlat.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  Inside:=True;
  Repaint;
end;

procedure TComboFlat.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  Inside:=False;
  Repaint;
end;

{$IFNDEF LCL}
procedure TComboFlat.CMFocusChanged(var Message: TCMFocusChanged);
begin
  inherited;
  Inside:=False;
  Repaint;
end;
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$ENDIF}

{ TTeeBasePath }

procedure TTeeBasePath.LineTo(const P:TPoint);
begin
  LineTo(P.X,P.Y);
end;

procedure TTeeBasePath.MoveTo(const P:TPoint);
begin
  MoveTo(P.X,P.Y);
end;


{ TTeePath }

Constructor TTeePath.Create(const Canvas:TTeeCanvas);

  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  procedure TryRaiseError;
  var tmp : Cardinal;
  begin
    tmp:=GetLastError;
    raise Exception.Create('Cannot create GDI path. Error: '+IntToStr(tmp)+' '+SysErrorMessage(tmp));
  end;
  {$ENDIF}
  {$ENDIF}

begin
  inherited Create;
  ICanvas:=Canvas;

  {$IFDEF FMX}
  IPath:=FMXTPathData.Create;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  if not BeginPath(ICanvas.Handle) then
     TryRaiseError;
  {$ENDIF}
  {$ENDIF}
end;

{$IFDEF FMX}
Destructor TTeePath.Destroy;
begin
  IPath.Free;
  inherited;
end;
{$ENDIF}

procedure TTeePath.ArcTo(const X,Y,Width,Height,StartAngle,SweepAngle:Single);
begin
  {$IFDEF FMX}
  IPath.AddArc(PointF(X+Width*0.5,Y+Height*0.5),PointF(Width,Height),
                StartAngle,SweepAngle);
  {$ELSE}
  ICanvas.Arc(Round(X),Round(Y),Round(X+Width),Round(Y+Height),StartAngle,StartAngle+SweepAngle);
  {$ENDIF}
end;

procedure TTeePath.Curve(const Points:Array of TPoint; const Tension:Single; Closed:Boolean);
begin
  if Closed then
  begin
    {$IFDEF FMX}
    AddPointsToPath(IPath,Points,True); // Pending IPath.CurveTo / SmoothCurveTo
    {$ELSE}
    {$IFDEF MSWINDOWS}
    Windows.Polygon(ICanvas.Handle, PPoints(@Points)^, Length(Points));

    with Points[Length(Points)-1] do
         Windows.MoveToEx(ICanvas.Handle,X,Y,nil);
    {$ENDIF}
    {$ENDIF}
  end
  else
    {$IFDEF FMX}
    AddPointsToPath(IPath,Points,False); // Pending IPath.Curve
    {$ELSE}
    {$IFDEF MSWINDOWS}
    Windows.PolyLineTo(ICanvas.Handle, PPoints(@Points)^, Length(Points));
    {$ENDIF}
    {$ENDIF}
end;

procedure TTeePath.Close;
begin
  {$IFDEF FMX}
  IPath.ClosePath;
  {$ELSE}
  {$IFDEF MSWINDOWS}
  CloseFigure(ICanvas.Handle);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeePath.Draw(const ACanvas:TTeeCanvas);
var tmpPen : Boolean;
    {$IFNDEF FMX}
    tmpBrush : Boolean;
    R : TRect;
    tmp : Integer;
    {$ENDIF}
begin
  tmpPen:=ACanvas.Pen.Visible;

  {$IFDEF FMX}
  TTeeCanvas3D(ACanvas).PrepareBrush;

  if ACanvas.Brush.Kind<>TBrushKind.{$IFDEF D20}None{$ELSE}bkNone{$ENDIF} then
  begin
     ACanvas.FCanvas.FillPath(IPath, ACanvas.Opacity);
     ACanvas.FCanvas.Fill.Kind:=TBrushKind.{$IFDEF D20}Solid{$ELSE}bkSolid{$ENDIF};
  end;

  if tmpPen and (ACanvas.FPen.Style<>psClear) then
     ACanvas.FCanvas.DrawPath(IPath, ACanvas.Opacity);

  {$ELSE}
  {$IFDEF MSWINDOWS}
  EndPath(ICanvas.Handle);

  tmpBrush:=ACanvas.Brush.Style<>bsClear;

  if ACanvas.Brush.HasGraphic then
  begin
    tmp:=SaveDC(ICanvas.Handle);
    SelectClipPath(ICanvas.Handle,RGN_COPY);
    GetClipBox(ICanvas.Handle,R);
    ACanvas.StretchDraw(R,ACanvas.Brush.Image.Filtered);
    RestoreDC(ICanvas.Handle,tmp);
    tmpBrush:=False;
  end;

  if ACanvas.IsGradientVisible then
  begin
    tmp:=SaveDC(ICanvas.Handle);
    SelectClipPath(ICanvas.Handle,RGN_COPY);
    GetClipBox(ICanvas.Handle,R);
    ACanvas.Brush.Gradient.Draw(ACanvas,R);
    RestoreDC(ICanvas.Handle,tmp);
    tmpBrush:=False;
  end;

  if tmpPen and tmpBrush then
     StrokeAndFillPath(ICanvas.Handle)
  else
  if tmpPen then
     StrokePath(ICanvas.Handle)
  else
  if tmpBrush then
     FillPath(ICanvas.Handle);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeePath.GetPoints(out Types:TPathPointTypes; out Points:TPointFloatArray; out Bounds:TRectF);
begin
  {$IFDEF FMX}
  // IPath...
  {$ELSE}
  // old GDI not capable?
  {$ENDIF}
end;

procedure TTeePath.LineTo(const X,Y:Single);
begin
  {$IFDEF FMX}
  IPath.LineTo(PointF(X,Y));
  {$ELSE}
  {$IFDEF MSWINDOWS}
  Windows.LineTo(ICanvas.Handle, Round(X),Round(Y));
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeePath.MoveTo(const X,Y:Single);
begin
  {$IFDEF FMX}
  IPath.MoveTo(PointF(X,Y));
  {$ELSE}
  {$IFDEF MSWINDOWS}
  MoveToEx(ICanvas.Handle,Round(X),Round(Y),nil);
  {$ENDIF}
  {$ENDIF}
end;

procedure TTeePath.TextOut(const X,Y:Single; const Text:String);
begin
  {$IFDEF FMX}
//  IPath.DrawText(PointF(X,Y));
  {$ELSE}
  {$IFDEF MSWINDOWS}
  ICanvas.TextOut(Round(X),Round(Y),Text);
  {$ENDIF}
  {$ENDIF}
end;

{ draw a simulated checkbox on Canvas }
Procedure TeeDrawCheckBox( x,y:Integer;
                           const Canvas:{$IFDEF FMX}TTeeCanvas{$ELSE}TCanvas{$ENDIF};
                           Checked:Boolean;
                           ABackColor:TColor; CheckBox:Boolean=True);

{$IFDEF FMX}
{$DEFINE TEECHECKBOX}
{$ENDIF}

{$IFNDEF TEECHECKBOX}
var t : Integer;
{$IFDEF MSWINDOWS}
var tmpDC : HDC;
{$ENDIF}
{$ENDIF}
begin
  {$IFDEF TEECHECKBOX}
  Canvas.InternalDrawCheck(x,y,Checked,ABackColor,CheckBox);
  {$ELSE}
  if CheckBox then t:=DFCS_BUTTONCHECK
              else t:=DFCS_BUTTONRADIO;

  if Checked then
     t:=t or DFCS_CHECKED;

//  DrawFrameControl(Canvas.Handle,Bounds(x,y,GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK)),DFC_BUTTON,t);

  {$IFDEF MSWINDOWS}
  tmpDC:=Canvas.Handle;

  // RS-79348:  DrawFrameControl is using text alignment to calc XY position !!!
  SetTextAlign(tmpDC,TA_LEFT);

  DrawFrameControl(tmpDC,Bounds(x,y,13,13),DFC_BUTTON,t);
  {$ENDIF}

  {$ENDIF}
end;

initialization
  {$IFNDEF TEEVCL}
  InitPlatformId;
  {$ENDIF}

  {$IFNDEF FMX}
  {$IFDEF MSWINDOWS}
  IsWindowsNT:=Win32Platform=VER_PLATFORM_WIN32_NT;  // Alphablend fails in Win98
  {$ELSE}
  IsWindowsNT:=False;
  {$ENDIF}
  {$ENDIF}

  TeeDefaultFont;

  {$IFNDEF LCL}
  AddCustomFonts;
  {$ENDIF}

  {$IFNDEF FMX}
  TryLoadSetDC;
  {$ENDIF}

  TeeRenderClasses:=TList{$IFDEF TEELISTMETA}<TCanvas3DClass>{$ENDIF}.Create; // QC115377
  TeeRenderClasses.Add(TTeeCanvas3D);

finalization
  {$IFDEF FMX}
  FBlur.Free;
  {$ENDIF}

  {$IFNDEF LCL}
  RemoveCustomFonts;
  {$ENDIF}

  TeeRenderClasses.Remove(TTeeCanvas3D);
  FreeAndNil(TeeRenderClasses);

  TeeCustomEditColors.Free;

  {$IFNDEF FMX}
  {$IFNDEF LCL}
  if TeeGDI32<>0 then
     TeeFreeLibrary(TeeGDI32);

  if TeeMSIMG32<>0 then
     TeeFreeLibrary(TeeMSIMG32);
  {$ENDIF}

  DeleteClipHistory;
  {$ENDIF}
end.
