{ +-------------------------------------------------------------+ }
{ |                                                             | }
{ |   GM-Software                                               | }
{ |   ===========                                               | }
{ |                                                             | }
{ |   Project: All Projects                                     | }
{ |                                                             | }
{ |   Description: Windows GDI classes                          | }
{ |                                                             | }
{ |                                                             | }
{ |   Copyright (C) - 1996 - Gerrit Moeller.                    | }
{ |                                                             | }
{ |   Source code dstributed under MIT license.                 | }
{ |                                                             | }
{ |   See: https://www.gm-software.de                           | }
{ |                                                             | }
{ +-------------------------------------------------------------+ }

{$INCLUDE GMCompilerSettings.inc}

unit GMGdi;

interface

uses {$IFDEF JEDIAPI}jwaWinType, jwaWinGdi, jwaWinUser,{$ELSE}Windows,{$ENDIF}
     GMStrDef, GMIntf, GMCollections, GMCommon;

const

  {$IFDEF FPC}
  {$IFNDEF JEDIAPI}
  {$EXTERNALSYM RGN_ERROR}
  RGN_ERROR = ERROR;
  {$ENDIF}
  {$ENDIF}

  cSysColFlag = $80000000; // DWORD($80000000);
  clrTransparent = CLR_INVALID;
  clrAutoTransparent = $80FFFFFF;

  // System colors
  clrScrollBar = COLOR_SCROLLBAR or cSysColFlag;
  clrBackground = COLOR_BACKGROUND or cSysColFlag;
  clrActiveCaption = COLOR_ACTIVECAPTION or cSysColFlag;
  clrGradientActiveCaption = COLOR_GRADIENTACTIVECAPTION or cSysColFlag;
  clrGradientInactiveCaption = COLOR_GRADIENTINACTIVECAPTION or cSysColFlag;
  clrInactiveCaption = COLOR_INACTIVECAPTION or cSysColFlag;
  clrMenu = COLOR_MENU or cSysColFlag;
  clrWindow = COLOR_WINDOW or cSysColFlag;
  clrWindowFrame = COLOR_WINDOWFRAME or cSysColFlag;
  clrMenuText = COLOR_MENUTEXT or cSysColFlag;
  clrWindowText = COLOR_WINDOWTEXT or cSysColFlag;
  clrCaptionText = COLOR_CAPTIONTEXT or cSysColFlag;
  clrActiveBorder = COLOR_ACTIVEBORDER or cSysColFlag;
  clrInactiveBorder = COLOR_INACTIVEBORDER or cSysColFlag;
  clrAppWorkSpace = COLOR_APPWORKSPACE or cSysColFlag;
  clrHighlight = COLOR_HIGHLIGHT or cSysColFlag;
  clrHighlightText = COLOR_HIGHLIGHTTEXT or cSysColFlag;
  clrBtnFace = COLOR_BTNFACE or cSysColFlag;
  clrBtnShadow = COLOR_BTNSHADOW or cSysColFlag;
  clrGrayText = COLOR_GRAYTEXT or cSysColFlag;
  clrBtnText = COLOR_BTNTEXT or cSysColFlag;
  clrInactiveCaptionText = COLOR_INACTIVECAPTIONTEXT or cSysColFlag;
  clrBtnHighlight = COLOR_BTNHIGHLIGHT or cSysColFlag;
  clr3DDkShadow = COLOR_3DDKSHADOW or cSysColFlag;
  clr3DLight = COLOR_3DLIGHT or cSysColFlag;
  clrInfoText = COLOR_INFOTEXT or cSysColFlag;
  clrInfoBkgnd = COLOR_INFOBK or cSysColFlag;
  clrHotLight = COLOR_HOTLIGHT or cSysColFlag;

  // Standard colors
  clBlack = $000000;
  clMaroon = $000080;
  clGreen = $008000;
  clOlive = $008080;
  clNavy = $800000;
  clPurple = $800080;
  clTeal = $808000;
  clGray = $808080;
  clSilver = $C0C0C0;
  clRed = $0000FF;
  clLime = $00FF00;
  clYellow = $00FFFF;
  clBlue = $FF0000;
  clPink = $FF00FF;
  clAqua = $FFFF00;
  clLtGray = clSilver; // $C0C0C0;
  clDkGray = $808080;
  clWhite = $FFFFFF;

  // Other useful colors
  clrLightBlue = $FFF0E6; // 255-240-230     //clrLightBlue = $FFEEE7;
  clrColdBlue = $f8b08c;
  clrWarmBlue = $eca800; //  $efad00
  clrGlassBlue = $fbdabe; //     $FDDCCB
  clrGlassGreen = $bfdea9;   // $a2cf81;
  clrOrange = $0080FF;
  clrDkOrange = $4080ff;
  clrCreme = $F0FBFF;

  CLR_DEFAULT = $FF000000;

  cInvalidHIcon = $ffffffff;

  cFrameDarknessDelta = -400;

type

  {$IFDEF FPC}
  {$IFNDEF JEDIAPI}
  PBitmapV5Header = ^TBitmapV5Header;
  BITMAPV5HEADER = record
    bV5Size: DWORD;
    bV5Width: LONG;
    bV5Height: LONG;
    bV5Planes: WORD;
    bV5BitCount: WORD;
    bV5Compression: DWORD;
    bV5SizeImage: DWORD;
    bV5XPelsPerMeter: LONG;
    bV5YPelsPerMeter: LONG;
    bV5ClrUsed: DWORD;
    bV5ClrImportant: DWORD;
    bV5RedMask: DWORD;
    bV5GreenMask: DWORD;
    bV5BlueMask: DWORD;
    bV5AlphaMask: DWORD;
    bV5CSType: DWORD;
    bV5Endpoints: CIEXYZTRIPLE;
    bV5GammaRed: DWORD;
    bV5GammaGreen: DWORD;
    bV5GammaBlue: DWORD;
    bV5Intent: DWORD;
    bV5ProfileData: DWORD;
    bV5ProfileSize: DWORD;
    bV5Reserved: DWORD;
  end;
  {$EXTERNALSYM BITMAPV5HEADER}
  LPBITMAPV5HEADER = ^BITMAPV5HEADER;
  {$EXTERNALSYM LPBITMAPV5HEADER}
  TBitmapV5Header = BITMAPV5HEADER;
  {$ENDIF}
  {$ENDIF}

  TGMGdiObjSelector = class(TGMRefCountedObj, IGMGetHandle, IGMHandleAllocated)
   protected
    FDC: HDC;
    FPrevObject: THandle;
    FGdiObject: THandle;

   public
    //constructor Create(const ADC: HDC; const GdiObj: IGMGetHandle; const ARefLifeTime: Boolean = True); reintroduce; overload;
    constructor Create(const ADC: HDC; const AGdiObject: THandle = 0; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
    procedure ReleaseGdiObject; virtual;
    function GetHandleAllocated: Boolean; stdcall;
    function GetHandle: THandle; virtual; stdcall;
    property Handle: THandle read GetHandle;
    property HandleAllocated: Boolean read GetHandleAllocated;
    property DC: HDC read FDC;
  end;


  TGMGdiObject = class(TGMGdiObjSelector)
   //protected
    //FDC: HDC;
    //FPrevObject: THandle;
    //FGdiObject: THandle;

   public
    //constructor Create(const ADC: HDC; const AGdiObject: THandle = 0; const ARefLifeTime: Boolean = True); reintroduce;
    //destructor Destroy; override;
    procedure ReleaseGdiObject; override;
  end;


  TGMGdiPen = class(TGMGdiObject)
   public
    constructor Create(const ADC: HDC; const AColor: COLORREF; const AStyle: LongInt = PS_SOLID; const AWidth: LongInt = 1; const ARefLifeTime: Boolean = True);
  end;


  TGMGdiBrush = class(TGMGdiObject, IGMHashCode)
   public
    constructor Create(const ADC: HDC; const AColor: COLORREF; const AStyle: LongInt = BS_SOLID; const AHatch: LongInt = 0; const ARefLifeTime: Boolean = True);
    constructor CreateFromBmp(const ADC: HDC; const AHBmp: HBitmap; const ARefLifeTime: Boolean = True);
    function HashCode: TGMHashCode;
  end;


  TGMGdiCachedBrush = class(TGMGdiBrush)
   public
    destructor Destroy; override;
  end;


  IGMGdiFont = interface(IGMGetHandle)
    ['{A11854F1-B57E-4c5e-B1A8-F96D64D07215}']
    function GetFontData: TLogFont; stdcall;
    procedure SetFontData(const Value: TLogFont); stdcall;
    property FontData: TLogFont read GetFontData write SetFontData;
  end;

  TFontStyle = (fsItalic, fsUnderline, fsStrikeOut);
  TFontStyles = set of TFontStyle;
  TDefaultFont = (dfAnsiFixedFont, dfOEMFixedFont, dfAnsiVarFont, dfDeviceFont, dfUIFont, dfSysFont, dfSysFixedFont);

  TGMGdiFont = class(TGMGdiObject, IGMGdiFont)
   protected
    function GetFontData: TLogFont; stdcall;
    procedure SetFontData(const Value: TLogFont); stdcall;

   public
    constructor Create(const ADC: HDC; const AFont: TDefaultFont; const ARefLifeTime: Boolean = True); overload;
    constructor Create(const ADC: HDC; const AFontData: TLogFont; const ARefLifeTime: Boolean = True); overload;
    constructor Create(const ADC: HDC;
                       const AName: TGMString;
                       const AHeight: LongInt = 0;
                       const AWeight: LongInt = FW_NORMAL;
                       const AStyle: TFontStyles = [];
                       const ARefLifeTime: Boolean = True); overload;

    property FontData: TLogFont read GetFontData write SetFontData;
  end;


  TGMGdiBitmap = class(TGMGdiObject)
   public
    constructor CreateFromRes(const ADC: HDC; const AResName: PGMChar; const AInstance: THandle; const ACheckLoad: Boolean = True; const ARefLifeTime: Boolean = True);
    constructor CreateFromBmpData(const ADC: HDC; const ABmpData: TBitmap; const ARefLifeTime: Boolean = True);
    constructor CreateCompatibleBmp(const ADC, ADcCompatible: HDC; const ASize: TPoint; const ARefLifeTime: Boolean = True);
    constructor CreateFromData(const ADC: HDC; const AWidth, AHeight, APlanes, ABitCount: LongInt; const ABits: Pointer = nil; const ARefLifeTime: Boolean = True);
    //constructor CreateCompatibleBmp(const ADC: HDC; const ASize: TPoint; const ARefLifeTime: Boolean = True); overload;
  end;


  TGMGdiDIBitmap = class(TGMGdiObject)
   protected
    FBmpInfo: TBitmapInfoHeader;
    FBits: Pointer;

   public
    constructor Create(const ADC: HDC;
                       const ASize: TPoint;
                       const ABitsPerPixel: LongInt;
                       const ARefLifeTime: Boolean = True);
  end;


  {TGMGdiDCBitmap = class;

  IGMGdiDCBitmap = interface(IGMGetHandle)
    ['F32674B8-013F-4406-B151-E79EC71338A1']
    function Obj: TGMGdiDCBitmap;
  end;

  TGMGdiDCBitmap = class(TGMGdiBitmap, IGMGdiDCBitmap)
   public
    constructor CreateCompatible(const ASize: TPoint; const ARefLifeTime: Boolean = True);
    function Obj: TGMGdiDCBitmap;
  end;}


  TGMGdiRegion = class(TGMGdiObject)
   public
    constructor CreateRect(const ADC: HDC; const ARect: TRect; const ARefLifeTime: Boolean = True);
    constructor CreateRoundRect(const ADC: HDC; const ARect: TRect; const ARounding: TPoint; const ARefLifeTime: Boolean = True);
    constructor CreatePolygon(const ADC: HDC; const APoints: array of TPoint; const AFilleMode: LongInt = WINDING; const ARefLifeTime: Boolean = True);
    constructor CreateElipse(const ADC: HDC; const ABounds: TRect; const ARefLifeTime: Boolean = True);
    constructor CreatefromWindow(const ADC: HDC; const AWnd: HWnd; const ARefLifeTime: Boolean = True);
  end;


  TGMGdiPalette = class(TGMGdiObject)
   public
    constructor Create(const ADC: HDC; const ALogPalette: TLogPalette; const ARefLifeTime: Boolean = True);
  end;


  TGMWindowDC = class(TGMRefCountedObj, IGMGetHandle)
   protected
    FWnd: HWnd;
    FDC: HDC;
   public
    constructor Create(const AWnd: HWnd; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
    function GetHandle: THandle; virtual; stdcall;
    property Handle: THandle read GetHandle;
  end;


  TGMGdiCompatibleDC = class(TGMRefCountedObj, IGMGetHandle, IGMCriticalSection)
   protected
    FDC: HDC;
    FCriticalSection: IGMCriticalSection;
    //FGdiObject: THandle;
    //FPrevObject: THandle;

   public
//  constructor Create(const ARefLifeTime: Boolean = True); override; overload;
    constructor Create(const AGdiObj: THandle = 0; const ADcCompatible: HDC = 0; const ARefLifeTime: Boolean = True); reintroduce; // overload;
    destructor Destroy; override;
    function GetHandle: THandle; virtual; stdcall;
    property Handle: THandle read GetHandle;
    property CriticalSection: IGMCriticalSection read FCriticalSection implements IGMCriticalSection;
  end;


  TGMGdiBitmapedDC = class;

  IGMGdiBitmapedDC = interface(IUnknown)
    ['{A8AEE541-DC9E-438C-B044-C792D9829622}']
    function Obj: TGMGdiBitmapedDC;
  end;

  TGMGdiBitmapedDC = class(TGMRefCountedObj, IGMGetHandle, IGMCriticalSection, IGMGdiBitmapedDC)
   protected
    FCriticalSection: IGMCriticalSection;
    FBitmap, FDC: IGMGetHandle; // <- FBitmap released first

   public
    constructor Create(const ARefLifeTime: Boolean = True); override;
    constructor CreateFromRes(const ADcCompatible: HDC; const AResName: PGMChar; const AInstance: THandle; const ACheckLoad: Boolean = True; const ARefLifeTime: Boolean = True);
    constructor CreateFromBmpData(const ADcCompatible: HDC; const ABmpData: TBitmap; const ARefLifeTime: Boolean = True);
    constructor CreateCompatible(const ADcCompatible: HDC; const ASize: TPoint; const ARefLifeTime: Boolean = True);
    constructor CreateFromData(const ADcCompatible: HDC; const AWidth, AHeight, APlanes, ABitCount: LongInt; const ABits: Pointer = nil; const ARefLifeTime: Boolean = True);
//  destructor Destroy; override;
    function Obj: TGMGdiBitmapedDC;
    function GetHandle: THandle; virtual; stdcall;
    property Handle: THandle read GetHandle;
    property CriticalSection: IGMCriticalSection read FCriticalSection implements IGMCriticalSection;
  end;


  TGMGdiDIBitmapedDC = class(TGMRefCountedObj, IGMGetHandle)
   protected
    FBitmap, FDC: IGMGetHandle; // <- FBitmap released first
   public
    constructor Create(const ADcCompatible: HDC; const ASize: TPoint; const ABitsPerPixel: Integer; const ARefLifeTime: Boolean = True); reintroduce;
    function GetHandle: THandle; virtual; stdcall;
//  procedure DetachBitmap;
  end;


  TGMGdiClipRgn = class(TGMRefCountedObj)
   protected
    FDC: HDC;
    FOrgRgn: IGMGetHandle;
   public
    constructor Create(const ADC: HDC; const ARgn: HRGN; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
  end;


  TGMGdiTextColorSelector = class(TGMRefCountedObj)
   protected
    FDC: HDC;
    FTextColor: COLORREF;
    FTextBkColor: COLORREF;
    FPrevTextColor: COLORREF;
    FPrevBkColor: COLORREF;
    FPrevTextBkMode: LongInt;
   public
    constructor Create(const ADC: HDC; const ATextColor, ATextBkColor: COLORREF; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
  end;


  TGMGdiDCKeeperBase = class(TGMRefCountedObj, IGMGetHandle)
   protected
    FDC: HDC;

   public
    constructor Create(const ADC: HDC; const ARefLifeTime: Boolean = True); reintroduce;
    function GetHandle: THandle; stdcall;
  end;


  TGMGdiDCStateKeeper = class(TGMGdiDCKeeperBase)
   protected
    FDCState: LongInt;

   public
    constructor Create(const ADC: HDC; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
  end;


  TGMGdiROPKeeper = class(TGMGdiDCKeeperBase)
   protected
    FROP: LongInt;

   public
    constructor Create(const ADC: HDC; const AROP: LongInt; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
  end;


  TGMGdiClipRgnKeeper = class(TGMGdiDCKeeperBase)
   protected
    FOldClipRgn: IGMGetHandle;

   public
    constructor Create(const ADC: HDC; const AAdditionalRgn: HRGN; const AMode: LongInt = RGN_AND; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
  end;


  TGMCursorWithPlus = class(TGMRefCountedObj, IGMGetHandle)
   protected
    FDoDestroyCursor: Boolean;
    FHandle: LongWord;
   public
    constructor Create(const ACursor: MakeIntResource; const ARefLifeTime: Boolean = True); reintroduce;
    destructor Destroy; override;
    function GetHandle: THandle; stdcall;
  end;


  PGMImgCollectionDesc = ^TGMImgCollectionDesc;
  TGMImgCollectionDesc = record
   XOffset: LongInt;
   Size: TPoint;
  end;

  TGMImgCollectionDescs = array of TGMImgCollectionDesc;

  TGMImageCollection = class;

  IGMImageCollection = interface(IUnknown)
    ['{8A775175-F8A4-458A-B473-E9E2E852C719}']
    function Obj: TGMImageCollection;
  end;

  TGMImageCollection = class(TGMRefCountedObj, IGMImageCollection)
   protected
    FBmpSize: TPoint;
    FDcCompatible: HDC;
    FImageDescs: TGMImgCollectionDescs;
    FImagesDC, FDisabledImgDC, FBkgndMaskDC: IGMGetHandle;

    procedure EnlargeBitmaps(const ADelta: TPoint);
    procedure BuildDisabledImg(const AXOffset: Integer; const ASize: TPoint);

   public
    constructor Create(const ADcCompatible: HDC; const ARefLifeTime: Boolean = True); reintroduce; overload;
    constructor Create(const AResBmpName: PGMChar; const AInstance: THandle; const ASplitWidth: LongInt = 0;
                       ATransparentColor: COLORREF = clrAutoTransparent; const ADcCompatible: HDC = 0;
                       const ALightnessDelta: LongInt = 0; const ADisabledLightnessDelta: LongInt = 0;
                       const ARefLifeTime: Boolean = True); reintroduce; overload;
    function Obj: TGMImageCollection;
    procedure AddIcon(const AIcon: HIcon; const ALightnessDelta: LongInt = 0);
    procedure AddBitmap(const ABitmap: HBitmap; ASplitWidth: LongInt = 0; ATransparentColor: COLORREF = clrAutoTransparent; const ALightnessDelta: LongInt = 0; const ADisabledLightnessDelta: LongInt = 0);
    procedure DrawImage(const AImageIndex: LongInt; const ADestDC: HDC; const ADstRect: TRect; const ADisabled: Boolean = False);

    property ImageDescs: TGMImgCollectionDescs read FImageDescs;
  end;



function GMCachedBrushes: IGMObjCollection;
function GMGetCachedBrush(const AColor: COLORREF; const AStyle: LongInt = BS_SOLID; const AHatch: LongInt = 0): IGMGetHandle;
function GMBoldUIFont: HFont;
function GMDitheredBrush: HBrush;

//function GMDitheredBrush: HBrush;
//function UICalcMemDC: IGMGetHandle;
//function CsUICalcMemDC: IGMCriticalSection;

function GMFontProperties(const AFont: HFONT): TLogFont;
function GMBrushProperties(const ABrush: HBRUSH): TLogBrush;

function GMRGBColor(const AColor: COLORREF): COLORREF;
function GMSysColor(const AColor: COLORREF): COLORREF;
function GMIsSysColor(const AColor: COLORREF): Boolean;
function GMCheckBoxSize: TPoint;
function GMBitmapSize(const ABitmap: IGMGetHandle): TPoint; overload;
function GMBitmapSize(const ABitmap: HBitmap): TPoint; overload;
function GMIconSize(const AIcon: HIcon): TPoint;
function GMCalcTextAreaSize(const Text: TGMString; const NewSize, FrameSize, PaddSpace: TPoint; const Font: HFont; const DrawFlags: DWORD): TPoint;
function GMTextExtent(const AText: TGMString; const AFont: HFont): TPoint;
procedure GMDrawIcon(const ADC: HDC; const AIcon: HIcon; const ARect: TRect; const ADisabled: Boolean = False);

function GMChangeColorLightness(AColor: COLORREF; ALightnessDelta: LongInt): COLORREF;
function GMShadeColor(const Value: COLORREF; const Scale: Single): COLORREF;
function GMFrameColorFromBkgndColor(const AColor: COLORREF): COLORREF;

procedure GMGrayScaleDCArea(const ADC: HDC; const AAreaRect: TRect);
procedure GMChangeDCAreaLightness(const ADC: HDC; const AAreaRect: TRect; const ALightnessDelta: LongInt);

procedure GMDrawDropArrow(const ADC: HDC; const AEnabled, ADown: Boolean; AR: TRect; const AColors: array of COLORREF);
procedure GMDrawRoundFrame(const ADC: HDC; const ABounds: TRect; const ColorShade: COLORREF; const ColorLight: COLORREF = CLR_INVALID);
function GMPaintText(const ADC: HDC; const Text: TGMString; RText: TRect; const Enabled: Boolean; const HAlignment: TGMHorizontalAlignment; const VAlignment: TGMVerticalAlignment; const DrawFlags: LongWord = cDfltTextDrawFlags): TRect;

function GMSetBmpStretchMode(const ADC: HDC; const AStretchMode: LongInt; const Caller: TObject): LongInt;
function GMFindMajorityColor(const ADC: HDC; const ARect: TRect): COLORREF;

function GMMultiLineTextSize(const ADC: HDC; const AText: TGMString; ARText: TRect; const ADrawFlags: LongWord = cDfltTextDrawFlags): TPoint;
function GMDrawText(const ADC: HDC; const AText: TGMString; const ARText: TRect; const AHAlignment: TGMHorizontalAlignment; const AVAlignment: TGMVerticalAlignment; const ADrawFlags: LongWord = cDfltTextDrawFlags): TRect;
procedure GMDrawCenteredText(const ADC: HDC; const AText: TGMString; const ARText: TRect; const ADrawFlags: LongWord = cDfltTextDrawFlags);


const

  cDefaultFont: array [TDefaultFont] of LongInt = (ANSI_FIXED_FONT, OEM_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT,
                                                   DEFAULT_GUI_FONT, SYSTEM_FONT, SYSTEM_FIXED_FONT);
        
  cGMSeverityColor: array [TGMSeverityLevel] of COLORREF = (clrWindowText, clNavy, clBlue, clrOrange, clRed);


implementation

uses SysUtils {$IFDEF JEDIAPI},jwaWinBase{$ENDIF};

const

  cImgCollYOffs = 0;


var

  vCheckBoxSize: TPoint = (X: 0; Y: 0);
  //vGMDefaultFont: IGMGdiFont = nil;
  vGMBoldUIFont: IGMGdiFont = nil;
  vGMDitheredBrushBmp: IGMGetHandle = nil;
  vGMDitheredBrush: IGMGetHandle = nil;
  vGMCachedBrushes: IGMObjCollection = nil;
  //vUICalcMemDC: IGMGetHandle = nil;
  //vCsUICalcMemDC: IGMCriticalSection = nil;


{ ------------------------- }
{ ---- Global Routines ---- }
{ ------------------------- }


{function UICalcMemDC: IGMGetHandle;
begin
  // Creation is done in unit initialisation part because it should be done by the main thread!
  Result := vUICalcMemDC;
end;}

{function CsUICalcMemDC: IGMCriticalSection;
begin
  // Creation is done in unit initialisation part
  Result := vCsUICalcMemDC;
end;}

function GMRGBColor(const AColor: COLORREF): COLORREF;
begin
  if AColor and cSysColFlag = 0 then Result := AColor else Result := GetSysColor(AColor and not cSysColFlag);
end;

function GMIsSysColor(const AColor: COLORREF): Boolean;
begin
  Result := AColor and cSysColFlag <> 0;
end;

function GMSysColor(const AColor: COLORREF): COLORREF;
begin
  if AColor and cSysColFlag = 0 then Result := CLR_INVALID else Result := AColor and not cSysColFlag;
end;

//function GMChangeColorLightness(AColor: COLORREF; Amount: LongInt): COLORREF;
//var Table: array [0..255] of Byte; i: Byte;
//begin
//AColor := GMRGBColor(AColor);
//
//if Amount < 0 then
// begin
//  Amount := -Amount;
//  for i:=0 to 255 do Table[i] := Byte(i-((Amount*i) shr 8));
// end else
//  for i:=0 to 255 do Table[i] := Byte(i+((Amount*(i xor 255)) shr 8));
//
//Result := RGB(Table[GetRValue(AColor)], Table[GetGValue(AColor)], Table[GetBValue(AColor)]);
//end;

function GMChangeColorLightness(AColor: COLORREF; ALightnessDelta: LongInt): COLORREF;
  function LimitByte(AValue: LongInt): LongInt;
  begin
    Result := AValue;
    if Result > 255 then Result := 255 else if Result < 0 then Result := 0;
  end;
begin
  ALightnessDelta := ALightnessDelta div 12;
  AColor := GMRGBColor(AColor);
  Result := RGB(LimitByte(GetRValue(AColor) + ALightnessDelta), LimitByte(GetGValue(AColor) + ALightnessDelta), LimitByte(GetBValue(AColor) + ALightnessDelta));
end;

function GMShadeColor(const Value: COLORREF; const Scale: Single): COLORREF;
  function ShadeByte(ValByte: Byte): Byte;
  begin
    Result := GMBoundedInt(Round(ValByte * Scale), 0, $FF);
  end;
begin
  Result := RGB(ShadeByte(GetRValue(Value)), ShadeByte(GetGValue(Value)), ShadeByte(GetBValue(Value)));
end;

function GMFrameColorFromBkgndColor(const AColor: COLORREF): COLORREF;
begin
  Result := GMChangeColorLightness(AColor, cFrameDarknessDelta);
end;

function GMFontProperties(const AFont: HFONT): TLogFont;
begin
  //FillChar(Result, SizeOf(Result), 0);
  Result := Default(TLogFont);
  if (AFont = 0) or (GetObject(AFont, 0, nil) <> SizeOf(Result)) then Exit;
  GMApiCheckObj('GMFontProperties', '', GetLastError, GetObject(AFont, SizeOf(Result), @Result) <> 0);
end;

function GMBrushProperties(const ABrush: HBRUSH): TLogBrush;
begin
  //FillChar(Result, SizeOf(Result), 0);
  Result := Default(TLogBrush);
  if (ABrush = 0) or (GetObject(ABrush, 0, nil) <> SizeOf(Result)) then Exit;
  GMApiCheckObj('GMBrushProperties', '', GetLastError, GetObject(ABrush, SizeOf(Result), @Result) <> 0);
end;

function GMBitmapSize(const ABitmap: HBitmap): TPoint;
var bmpData: TBitmap;
begin
  if (ABitmap = 0) or (GetObject(ABitmap, 0, nil) <> SizeOf(bmpData)) or
     (GetObject(ABitmap, SizeOf(bmpData), @bmpData) = 0) then
   Result := cNullPoint
  else
   Result := GMPoint(bmpData.bmWidth, bmpData.bmHeight);
end;

function GMBitmapSize(const ABitmap: IGMGetHandle): TPoint;
begin
  if ABitmap = nil then Result := cNullPoint else Result :=  GMBitmapSize(ABitmap.Handle);
end;

function GMIconSize(const AIcon: HIcon): TPoint;
var iconInfo: TIconInfo;
begin
  Result := cNullPoint;
  if AIcon = 0 then Exit;
  //FillChar(iconInfo, SizeOf(iconInfo), 0);
  iconInfo := Default(TIconInfo);
  //GMApiCheck(GetIconInfo(AIcon, iconInfo), 'GetIconInfo');
  if GetIconInfo(AIcon, iconInfo) then
   begin
    if iconInfo.hbmColor <> 0 then
     begin
      Result := GMBitmapSize(iconInfo.hbmColor);
      DeleteObject(iconInfo.hbmColor);
     end;
    if iconInfo.hbmMask <> 0 then DeleteObject(iconInfo.hbmMask);
   end;
end;

function GMCheckBoxSize: TPoint;
var ChkBmp: IGMGetHandle;
begin
  if (vCheckBoxSize.x = 0) {or (vCheckBoxSize.y = 0)} then
   begin
    ChkBmp := TGMGdiBitmap.CreateFromRes(0, MakeIntResource(OBM_CHECK), 0);
    vCheckBoxSize := GMBitmapSize(ChkBmp.Handle);
   end;
  Result := vCheckBoxSize;
end;

procedure GMDrawRoundFrame(const ADC: HDC; const ABounds: TRect; const ColorShade, ColorLight: COLORREF);
var Points: array of TPoint; Pen: IUnknown;
begin
  Pen := TGMGdiPen.Create(ADC, GMRGBColor(ColorShade));
  SetLength(Points, 6);

  with ABounds do
   begin
    Points[0] := GMPoint(Left+2, Bottom-1);
    Points[1] := GMPoint(Left, Bottom-3);
    Points[2] := GMPoint(Left, top+2);
    Points[3] := GMPoint(Left+2, top);
    Points[4] := GMPoint(Right-3, top);
    Points[5] := GMPoint(Right-1, top+2);
   end;
  PolyLine(ADC, {$IFDEF JEDIAPI}@{$ENDIF}Points[0], Length(Points));

  if (ColorShade <> ColorLight) and (ColorLight <> CLR_INVALID) then
   begin Pen := nil; Pen := TGMGdiPen.Create(ADC, GMRGBColor(ColorLight)); end;

  //SetLength(Points, 4);
  with ABounds do
   begin
    Points[0] := GMPoint(Right-1, Top+2);
    Points[1] := GMPoint(Right-1, Bottom-3);
    Points[2] := GMPoint(Right-3, Bottom-1);
    Points[3] := GMPoint(Left+2, Bottom-1);
   end;
  PolyLine(ADC, {$IFDEF JEDIAPI}@{$ENDIF}Points[0], Length(Points)-2);
end;

{procedure GMDrawRoundFrame(const ADC: HDC; const ABounds: TRect; const ColorShade, ColorLight: COLORREF);
var FrameColor: COLORREF; Pen: IUnknown;
begin
  with ABounds do
   begin
    FrameColor := GMRGBColor(ColorShade);
    Pen := TGMGdiPen.Create(ADC, FrameColor);

    MoveToEx(ADC, Left, Bottom-3, nil); LineTo(ADC, Left, Top+1);
    SetPixel(ADC, Left+1, Top+1, FrameColor);
    MoveToEx(ADC, Left+2, Top, nil); LineTo(ADC, Right-2, Top);

    if (ColorShade <> ColorLight) and (ColorLight <> CLR_INVALID) then
     begin
      Pen := nil;
      FrameColor := GMRGBColor(ColorLight);
      Pen := TGMGdiPen.Create(ADC, FrameColor);
     end;

    MoveToEx(ADC, Right-1, Top+2, nil); LineTo(ADC, Right-1, Bottom-2);
    MoveToEx(ADC, Right-3, Bottom-1, nil); LineTo(ADC, Left+1, Bottom-1);

    SetPixel(ADC, Right-2, Top+1, FrameColor);
    SetPixel(ADC, Right-2, Bottom-2, FrameColor);
    SetPixel(ADC, Left+1, Bottom-2, FrameColor);
   end;
end;}

procedure GMDrawDropArrow(const ADC: HDC; const AEnabled, ADown: Boolean; AR: TRect; const AColors: array of COLORREF);
//const cColor: array [Boolean] of COLORREF = (clrBtnShadow, clrHighLight);
var ArrowPoints: array of TPoint; Pen, Brush: IUnknown;
begin
  if ADC = 0 then Exit;
  Pen := TGMGdiPen.Create(ADC, AColors[Ord(AEnabled)], PS_NULL);
  Brush := TGMGdiBrush.Create(ADC, AColors[Ord(AEnabled)]);
  SetLength(ArrowPoints, 3);
  if ADown then
   begin
    ArrowPoints[0] := GMPoint(AR.Left, AR.Top);
    ArrowPoints[1] := GMPoint(AR.Right, AR.Top);
    ArrowPoints[2] := GMPoint((AR.Right + AR.Left) div 2, AR.Bottom);
   end
  else
   begin
    Dec(AR.Top); Dec(AR.Left);
    ArrowPoints[2] := GMPoint((AR.Right + AR.Left) div 2, AR.Top);
    ArrowPoints[0] := GMPoint(AR.Left, AR.Bottom);
    ArrowPoints[1] := GMPoint(AR.Right, AR.Bottom);
   end;
  Polygon(ADC, {$IFDEF JEDIAPI}@{$ENDIF}ArrowPoints[0], Length(ArrowPoints));
end;

procedure GMGrayScaleDCArea(const ADC: HDC; const AAreaRect: TRect);
var x, y: LongInt; lum: Byte; color: COLORREF; // Div3: array [0..767] of Byte;
begin
//   x:=0;
//   for i:=0 to 255 do
//    begin
//     Div3[x]:=i; Inc(x);
//     Div3[x]:=i; Inc(x);
//     Div3[x]:=i; Inc(x);
//    end;

  // Graysacle the image
  for y:=AAreaRect.Top to AAreaRect.Bottom-1 do
   for x:=AAreaRect.Left to AAreaRect.Right-1 do
    begin
     color := GetPixel(ADC, x, y);
//      lum := Div3[GetRValue(color) + GetGValue(color) + GetBValue(color)];
     lum := Round(GetRValue(color)*0.3 + GetGValue(color)*0.59 + GetBValue(color)*0.11);
     SetPixel(ADC, x, y, RGB(lum, lum, lum));
    end;
end;

procedure GMChangeDCAreaLightness(const ADC: HDC; const AAreaRect: TRect; const ALightnessDelta: LongInt);
var x, y: LongInt;
begin
  for y:=AAreaRect.Top to AAreaRect.Bottom-1 do
   for x:=AAreaRect.Left to AAreaRect.Right-1 do
    SetPixel(ADC, x, y, GMChangeColorLightness(GetPixel(ADC, x, y), ALightnessDelta));
end;

{function UICalcMemDC: HDC;
//
// Must always be created by main thread, so better do it in the unit initialisation part
//
begin
  if vUICalcMemDC = nil then vUICalcMemDC := TGMGdiCompatibleDC.Create(True);
  Result := vUICalcMemDC.Handle;
end;}

function GMMultiLineTextSize(const ADC: HDC; const AText: TGMString; ARText: TRect; const ADrawFlags: LongWord = cDfltTextDrawFlags): TPoint;
begin
  Result := CNullPoint;
  if (ADC <> 0) and (Length(AText) > 0) then
   begin
    if ARText.Left <> 0 then OffsetRect(ARText, -ARText.Left, 0);
    if ARText.Top <> 0 then OffsetRect(ARText, 0, -ARText.Top);
    if (ARText.Right <= ARText.Left) and (ADrawFlags and DT_WORDBREAK <> 0) then ARText.Right := ARText.Left + 1;
  //if ARText.Bottom < 0 then ARText.Bottom := 0;
    Result.y := DrawText(ADC, PGMChar(AText), Length(AText), ARText, ADrawFlags or DT_CALCRECT);
    if Result.y = 0 then Result.y := ARText.Bottom - ARText.Top;
    Result.x := ARText.Right - ARText.Left;
   end;
end;

function GMDrawText(const ADC: HDC; const AText: TGMString; const ARText: TRect; const AHAlignment: TGMHorizontalAlignment; const AVAlignment: TGMVerticalAlignment; const ADrawFlags: LongWord = cDfltTextDrawFlags): TRect;
//var RDraw: TRect;
begin
  if (ADC <> 0) then
   begin
    Result := GMLayoutRect(ARText, GMMultiLineTextSize(ADC, AText, ARText, ADrawFlags), AHAlignment, AVAlignment);
    DrawText(ADC, PGMChar(AText), Length(AText), Result, ADrawFlags);
   end
  else Result := Default(TRect);
end;

procedure GMDrawCenteredText(const ADC: HDC; const AText: TGMString; const ARText: TRect; const ADrawFlags: LongWord = cDfltTextDrawFlags);
begin
  if ADC <> 0 then GMDrawText(ADC, AText, ARText, haCenter, vaCenter, ADrawFlags);
end;

function GMCalcTextAreaSize(const Text: TGMString; const NewSize, FrameSize, PaddSpace: TPoint; const Font: HFont; const DrawFlags: DWORD): TPoint;
var PIFont: IUnknown; MemDC: IGMGetHandle;
begin
  Result := cNullPoint;
  //SyncLock := TGMCriticalSectionLock.Create(UICalcMemDC);
  MemDC := TGMGdiCompatibleDC.Create;
  PIFont := TGMGdiObjSelector.Create(MemDC.Handle, Font);

  if DrawFlags and DT_SINGLELINE <> 0 then
   begin
    if not GetTextExtentPoint32(MemDC.Handle, PGMChar(Text), Length(Text), TSize(Result)) then Result := cNullPoint;
    Result := GMPoint(Result.x + FrameSize.x + PaddSpace.x, Result.y + FrameSize.y + PaddSpace.y);
   end
  else
   begin
    Result := GMMultiLineTextSize(MemDC.Handle, Text, GMRect(0, 0, NewSize.x - FrameSize.x - PaddSpace.x, NewSize.y - FrameSize.y - PaddSpace.y), DrawFlags);
    Inc(Result.x, FrameSize.x + PaddSpace.x);
    Inc(Result.y, FrameSize.y + PaddSpace.y);
   end;
end;

function GMTextExtent(const AText: TGMString; const AFont: HFont): TPoint;
var Font: IUnknown; MemDC: IGMGetHandle; // Size: TSize;
begin
  //SyncLock := TGMCriticalSectionLock.Create(UICalcMemDC);
  Result := cNullPoint;
  MemDC := TGMGdiCompatibleDC.Create;
  Font := TGMGdiObjSelector.Create(MemDC.Handle, AFont);
  if not GetTextExtentPoint32(MemDC.Handle, PGMChar(AText), Length(AText), TSize(Result)) then Result := cNullPoint;
  //Result.x := Size.cx;
  //Result.y := Size.cy;
end;

function GMPaintText(const ADC: HDC; const Text: TGMString; RText: TRect; const Enabled: Boolean; const HAlignment: TGMHorizontalAlignment; const VAlignment: TGMVerticalAlignment; const DrawFlags: LongWord = cDfltTextDrawFlags): TRect;
var PITextColor: IUnknown;
begin
  if Enabled then
   Result := GMDrawText(ADC, Text, RText, HAlignment, VAlignment, DrawFlags)
  else
   begin
    RText := GMMoveRect(RText, 1, 1);
    PITextColor := TGMGdiTextColorSelector.Create(ADC, clWhite, clrTransparent);
    GMDrawText(ADC, Text, RText, HAlignment, VAlignment, DrawFlags);
    RText := GMMoveRect(RText, -1, -1);
    PITextColor := nil;
    PITextColor := TGMGdiTextColorSelector.Create(ADC, clDkGray, clrTransparent);
    Result := GMDrawText(ADC, Text, RText, HAlignment, VAlignment, DrawFlags);
    Inc(Result.Right); Inc(Result.Bottom);
   end;
end;

function GMSetBmpStretchMode(const ADC: HDC; const AStretchMode: LongInt; const Caller: TObject): LongInt;
begin
  if ADC = 0 then begin Result := 0; Exit; end;
  Result := SetStretchBltMode(ADC, AStretchMode);
  //GMApiCheckObj(Result <> 0, Caller, 'GMSetBmpStretchMode: SetStretchBltMode');
  if AStretchMode = HALFTONE then SetBrushOrgEx(ADC, 0, 0, nil);
    //GMApiCheckObj(SetBrushOrgEx(ADC, 0, 0, nil), Caller, 'GMSetBmpStretchMode: SetBrushOrgEx');
end;

procedure GMDrawIcon(const ADC: HDC; const AIcon: HIcon; const ARect: TRect; const ADisabled: Boolean);
var dcImage, dcBkgnd, dcBkgndMask: IGMGetHandle; imgSize: TPoint; // dcImgMask // Div3: array [0..767] of Byte;
// x, y: LongInt; lum: Byte; color: COLORREF;
begin
  if AIcon = 0 then Exit;
  with ARect do
   if not ADisabled then
    DrawIconEx(ADC, Left, Top, AIcon, Right-Left, Bottom-Top, 0, 0, DI_NORMAL)
   else
    begin
//   DrawState(ADC, 0, nil, LongInt(AIcon), 0, Left, Top, Right-Left, Bottom-Top, DST_ICON or DSS_DISABLED);

     imgSize := GMRectSize(ARect);

     // Draw the image
     dcImage := TGMGdiBitmapedDC.CreateCompatible(ADC, imgSize, True);
     DrawIconEx(dcImage.Handle, 0, 0, AIcon, imgSize.x, imgSize.y, 0, 0, DI_IMAGE);

     GMGrayScaleDCArea(dcImage.Handle, GMRect(cNullPoint, imgSize));

     // Build monochrome background mask
     dcBkgndMask := TGMGdiBitmapedDC.CreateFromData(ADC, imgSize.x, imgSize.y, 1, 1, nil, True);
//   FillRect(dcBkgndMask.Handle, GMRect(cNullPoint, imgSize), GetStockObject(WHITE_BRUSH));
     DrawIconEx(dcBkgndMask.Handle, 0, 0, AIcon, imgSize.x, imgSize.y, 0, 0, DI_MASK);

     // Build monochrome image mask as the inverse of the background mask
//   dcImgMask := TGMGdiBitmapedDC.CreateFromData(ADC, imgSize.x, imgSize.y, 1, 1, nil, True);
//   BitBlt(dcImgMask.Handle, 0, 0, imgSize.x, imgSize.y, dcBkgndMask.Handle, 0, 0, NOTSRCCOPY);
     // Mask the image
//   BitBlt(dcImage.Handle, 0, 0, imgSize.x, imgSize.y, dcImgMask.Handle, 0, 0, SRCAND);

     // Get original background
     dcBkgnd := TGMGdiBitmapedDC.CreateCompatible(ADC, imgSize, True);
     Bitblt(dcBkgnd.Handle, 0, 0, imgSize.x, imgSize.y, ADC, left, Top, SRCCOPY);
     // Mask the background
     BitBlt(dcBkgnd.Handle, 0, 0, imgSize.x, imgSize.y, dcBkgndMask.Handle, 0, 0, SRCAND);
     // Combine masked background with masked image
     BitBlt(dcBkgnd.Handle, 0, 0, imgSize.x, imgSize.y, dcImage.Handle, 0, 0, SRCPAINT);
     // Draw the Result back to original device context
     Bitblt(ADC, left, Top, imgSize.x, imgSize.y, dcBkgnd.Handle, 0, 0, SRCCOPY);
    end;
end;

function GMFindMajorityColor(const ADC: HDC; const ARect: TRect): COLORREF;
type TColorCount = record Color: COLORREF; Count: LongInt; end;
var colorCounts: array of TColorCount; i: LongInt;
  procedure AddColor(const AColor: COLORREF);
  var i: LongInt;
  begin
    for i:=Low(colorCounts) to High(colorCounts) do
      if colorCounts[i].Color = AColor then begin Inc(colorCounts[i].Count); Exit; end;
    SetLength(colorCounts, Length(colorCounts)+1);
    colorCounts[High(colorCounts)].Color := AColor;
    colorCounts[High(colorCounts)].Count := 1;
  end;
begin
  AddColor(GetPixel(ADC, ARect.Left, ARect.Top));
  AddColor(GetPixel(ADC, ARect.Right-1, ARect.Top));
  AddColor(GetPixel(ADC, ARect.Right-1, ARect.Bottom-1));
  AddColor(GetPixel(ADC, ARect.Left, ARect.Bottom-1));

  for i:=Low(colorCounts) to High(colorCounts) do
   if colorCounts[i].Count >= 3 then begin Result := colorCounts[i].Color; Exit; end;

  for i:=Low(colorCounts) to High(colorCounts) do
   if colorCounts[i].Count >= 2 then begin Result := colorCounts[i].Color; Exit; end;

  Result := colorCounts[Low(colorCounts)].Color;
end;


{ ---------------------------- }
{ ---- Global GDI Objects ---- }
{ ---------------------------- }

function GMCompareBrush(const ItemA, ItemB: IUnknown): TGMCompareResult;
const cStrRoutineName = 'GMCompareBrush';
var BrushA, BrushB: IGMGetHandle; LogBrushA, LogBrushB: TLogBrush;
begin
  GMCheckQueryInterface(ItemA, IGMGetHandle, BrushA, cStrRoutineName);
  GMCheckQueryInterface(ItemB, IGMGetHandle, BrushB, cStrRoutineName);

  LogBrushA := GMBrushProperties(BrushA.Handle);
  LogBrushB := GMBrushProperties(BrushB.Handle);

  if LogBrushA.lbColor > LogBrushB.lbColor then Result := crAGreaterThanB else
  if LogBrushA.lbColor < LogBrushB.lbColor then Result := crALessThanB else
   if LogBrushA.lbStyle > LogBrushB.lbStyle then Result := crAGreaterThanB else
   if LogBrushA.lbStyle < LogBrushB.lbStyle then Result := crALessThanB else
    if LogBrushA.lbHatch > LogBrushB.lbHatch then Result := crAGreaterThanB else
    if LogBrushA.lbHatch < LogBrushB.lbHatch then Result := crALessThanB else
     Result := crAEqualToB;
end;

function GMCachedBrushes: IGMObjCollection;
begin
  //if vGMCachedBrushes = nil then vGMCachedBrushes := TGMObjArrayCollection.Create(False, False, True, GMCompareBrush, True);
  if vGMCachedBrushes = nil then vGMCachedBrushes := TGMObjHashTable.Create(False, False, GMCompareBrush, True);
  Result := vGMCachedBrushes;
end;

function GMGetCachedBrush(const AColor: COLORREF; const AStyle: LongInt; const AHatch: LongInt): IGMGetHandle;
var Brush: TObject;
begin
  Result := TGMGdiCachedBrush.Create(0, AColor, AStyle, AHatch, True);
  if GMCachedBrushes.Find(Result, Brush) then
   GMCheckGetInterface(Brush, IGMGetHandle, Result) // <- re-assign Result to the brush found in the list, will free original brush
  else
   GMCachedBrushes.Add(GMObjFromIntf(Result)); // GMTrace('Brushes: ' + IntToStr(GMCachedBrushes.Count)); end;
end;

function GMBoldUIFont: HFont;
var fontData: TLogFont;
begin
  if vGMBoldUIFont = nil then
   begin
    vGMBoldUIFont := TGMGdiFont.Create(0, dfUIFont, True);
    fontData := vGMBoldUIFont.fontData;
    fontData.lfWeight := FW_BOLD;
    vGMBoldUIFont.fontData := fontData;
   end;
  Result := vGMBoldUIFont.Handle;
end;

function GMDitheredBrush: HBrush;
const HatchBits: array [0..7] of word = ($aa, $55, $aa, $55, $aa, $55, $aa, $55);
begin
  //
  // This Brush will be painted 0 => BkgndColor,  1 => FontColor.
  // So better overwrite FontColor and BkgndColor to set these colors.
  //
  if vGMDitheredBrush = nil then
   begin
    if vGMDitheredBrushBmp = nil then //vGMDitheredBrushBmp := TGMGdiBitmap.CreateFromRes(0, cResGMDitheredBrushBmp, HInstance);
      vGMDitheredBrushBmp := TGMGdiBitmap.CreateFromData(0, 8, 8, 1, 1, @HatchBits);

    vGMDitheredBrush := TGMGdiBrush.CreateFromBmp(0, vGMDitheredBrushBmp.Handle);
   end;
  Result := vGMDitheredBrush.Handle;
end;


{ --------------------------- }
{ ---- TGMGdiObjSelector ---- }
{ --------------------------- }

constructor TGMGdiObjSelector.Create(const ADC: HDC; const AGdiObject: THandle; const ARefLifeTime: Boolean);
begin
  inherited Create(ARefLifeTime);
  FDC := ADC;
  FGdiObject := AGdiObject;
  if (FDC <> 0) and (FGdiObject <> 0) then FPrevObject := SelectObject(FDC, FGdiObject);
end;

destructor TGMGdiObjSelector.Destroy;
begin
  ReleaseGdiObject;
  inherited Destroy;
end;

function TGMGdiObjSelector.GetHandle: THandle;
begin
  Result := FGdiObject;
end;

function TGMGdiObjSelector.GetHandleAllocated: Boolean;
begin
  Result := FGdiObject <> 0;
end;

procedure TGMGdiObjSelector.ReleaseGdiObject;
begin
  // Only restore if ours is still the current ?!?
  //if (FDC <> 0) and (FPrevObject <> 0) and (FGdiObject <> 0) and
     //(GetCurrentObject(FDC, GetObjectType(FGdiObject)) = FGdiObject) then SelectObject(FDC, FPrevObject);

  if FPrevObject <> 0 then
   begin
    if (FGdiObject <> 0) and (FDC <> 0) and (GetCurrentObject(FDC, GetObjectType(FGdiObject)) = FGdiObject) then
     SelectObject(FDC, FPrevObject) else DeleteObject(FPrevObject)
   end;


    //if FDC <> 0 then SelectObject(FDC, FPrevObject) else DeleteObject(FPrevObject);

   //else
    //begin DeleteObject(FPrevObject); Result := False; end;
  
  FPrevObject := 0;
end;


//destructor TGMGdiObjSelector.Destroy;
//begin
//// Only restore if ours is still the current ?!?
//if (FDC <> 0) and (FPrevObject <> 0) and (FGdiObject <> 0) and
//   (GetCurrentObject(FDC, GetObjectType(FGdiObject)) = FGdiObject) then SelectObject(FDC, FPrevObject);
//inherited Destroy;
//end;
//
//procedure TGMGdiObject.ReleaseHandle;
//begin
//// Only restore if ours is still the current ?!?
//if (FDC <> 0) and (FPrevObject <> 0) and (FGdiObject <> 0) and
//   (GetCurrentObject(FDC, GetObjectType(FGdiObject)) = FGdiObject) then SelectObject(FDC, FPrevObject);
//FPrevObject := 0;
//if FGdiObject <> 0 then begin DeleteObject(FGdiObject); FGdiObject := 0; end;
//end;



{ ---------------------- }
{ ---- TGMGdiObject ---- }
{ ---------------------- }

procedure TGMGdiObject.ReleaseGdiObject;
begin
  inherited;
  if FGdiObject <> 0 then begin DeleteObject(FGdiObject); FGdiObject := 0; end;
end;


{ ------------------- }
{ ---- TGMGdiPen ---- }
{ ------------------- }

constructor TGMGdiPen.Create(const ADC: HDC; const AColor: COLORREF; const AStyle, AWidth: LongInt; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, CreatePen(AStyle, AWidth, GMRGBColor(AColor)), ARefLifeTime);
  GMAPICheckObj('CreatePen', '', GetLastError, FGdiObject <> 0, Self);
end;


{ --------------------- }
{ ---- TGMGdiBrush ---- }
{ --------------------- }

constructor TGMGdiBrush.Create(const ADC: HDC; const AColor: COLORREF; const AStyle: LongInt; const AHatch: LongInt; const ARefLifeTime: Boolean);
var brushData: TLogBrush;
begin
  //FillChar(brushData, SizeOf(brushData), 0);
  brushData := Default(TLogBrush);
  brushData.lbStyle := AStyle;
  brushData.lbColor := GMRGBColor(AColor);
  brushData.lbHatch := AHatch;
  inherited Create(ADC, CreateBrushIndirect(brushData), ARefLifeTime);
  //FGdiObject := CreateBrushIndirect(brushData);
  GMAPICheckObj('CreateBrushIndirect', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiBrush.CreateFromBmp(const ADC: HDC; const AHBmp: HBitmap; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, CreatePatternBrush(AHBmp), ARefLifeTime);
  //FGdiObject := CreatePatternBrush(AHBmp);
  GMAPICheckObj('CreateBrushIndirect', '', GetLastError, FGdiObject <> 0, Self);
end;

function TGMGdiBrush.HashCode: TGMHashCode;
var LogBrush: TLogBrush;
begin
  LogBrush := GMBrushProperties(FGdiObject);
  Result := LogBrush.lbColor xor LogBrush.lbStyle xor UINT(LogBrush.lbHatch);
end;

{constructor TGMGdiBrush.CreateFromBmp(const ADC: HDC; const AHBmp: HBitmap; const ARefLifeTime: Boolean);
var BrushData: TLogBrush;
begin
  inherited Create(ADC, 0, ARefLifeTime);
  FillChar(BrushData, SizeOf(BrushData), 0);
  BrushData.lbStyle := BS_PATTERN;
  //BrushData.lbColor := GMRGBColor(AColor);
  BrushData.lbHatch := LongInt(AHBmp);
  FGdiObject := CreateBrushIndirect(BrushData);
  GMAPICheckObj(FGdiObject <> 0, Self, 'CreateBrushIndirect');
end;}


{ --------------------------- }
{ ---- TGMGdiCachedBrush ---- }
{ --------------------------- }

destructor TGMGdiCachedBrush.Destroy;
begin
  GMCachedBrushes.RemoveByKey(Self);
  inherited;
end;


{ -------------------- }
{ ---- TGMGdiFont ---- }
{ -------------------- }

constructor TGMGdiFont.Create(const ADC: HDC; const AFont: TDefaultFont; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, GetStockObject(cDefaultFont[AFont]), ARefLifeTime);
  GMAPICheckObj('GetStockObject', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiFont.Create(const ADC: HDC; const AFontData: TLogFont; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, 0, ARefLifeTime);
  FontData := AFontData;
end;

constructor TGMGdiFont.Create(const ADC: HDC;
                              const AName: TGMString;
                              const AHeight: LongInt;
                              const AWeight: LongInt;
                              const AStyle: TFontStyles;
                              const ARefLifeTime: Boolean);
const cBoolInt: array [Boolean] of BYte = (0, 1);
var fontData: TLogFont;
begin
  //inherited Create(ADC, ARefLifeTime);
  //FillChar(fontData, SizeOf(fontData), 0);
  fontData := Default(TLogFont);
  fontData.lfHeight := AHeight;
  fontData.lfWeight := AWeight;
  fontData.lfItalic := cBoolInt[fsItalic in AStyle];
  fontData.lfUnderline := cBoolInt[fsUnderline in AStyle];
  fontData.lfStrikeOut := cBoolInt[fsStrikeOut in AStyle];
  fontData.lfCharSet := DEFAULT_CHARSET;
  fontData.lfQuality := DEFAULT_QUALITY;
  fontData.lfOutPrecision := OUT_DEFAULT_PRECIS;
  fontData.lfClipPrecision := CLIP_DEFAULT_PRECIS;
  fontData.lfPitchAndFamily := DEFAULT_PITCH;
  lstrcpyn(fontData.lfFaceName, PGMChar(AName), Min(Length(AName)+1, SizeOf(fontData.lfFaceName) div SizeOf(TGMChar)));
  Create(ADC, fontData, ARefLifeTime);
end;

function TGMGdiFont.GetFontData: TLogFont;
begin
  //FillChar(Result, SizeOf(Result), 0);
  Result := Default(TLogFont);
  if not HandleAllocated or (GetObject(Handle, 0, nil) <> SizeOf(Result)) then Exit;
  GMApiCheckObj('GetFontData', '', GetLastError, GetObject(Handle, SizeOf(Result), @Result) <> 0, Self)
end;

procedure TGMGdiFont.SetFontData(const Value: TLogFont);
begin
  ReleaseGdiObject;
  FGdiObject := CreateFontIndirect({$IFDEF FPC}{$IFNDEF JEDIAPI}@{$ENDIF}{$ENDIF}Value);
  //{$IFDEF FPC}
  //FGdiObject := CreateFontIndirect((PLogFont(@Value)^);
  //{$ELSE}
  //FGdiObject := CreateFontIndirect(Value);
  //{$ENDIF}
  GMAPICheckObj('CreateFontIndirect', '', GetLastError, FGdiObject <> 0, Self);
  if FDC <> 0 then FPrevObject := SelectObject(FDC, FGdiObject);
end;


{ ---------------------- }
{ ---- TGMGdiBitmap ---- }
{ ---------------------- }

constructor TGMGdiBitmap.CreateFromRes(const ADC: HDC; const AResName: PGMChar; const AInstance: THandle;
  const ACheckLoad: Boolean; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, LoadBitmap(AInstance, AResName), ARefLifeTime);
  //if AInstance = 0 then AInstance := HInstance; No, otherwise OEM bitmaps can not be loaded!
  if ACheckLoad then GMAPICheckObj('LoadBitmap', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiBitmap.CreateFromBmpData(const ADC: HDC; const ABmpData: TBitmap; const ARefLifeTime: Boolean);
begin
  {$IFDEF FPC}
  inherited Create(ADC, CreateBitmapIndirect(PBitmap(@ABmpData)^), ARefLifeTime);
  {$ELSE}
  inherited Create(ADC, CreateBitmapIndirect(ABmpData), ARefLifeTime);
  {$ENDIF}
  GMAPICheckObj('CreateBitmapIndirect', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiBitmap.CreateCompatibleBmp(const ADC, ADcCompatible: HDC; const ASize: TPoint; const ARefLifeTime: Boolean);
var BmpDC: HDC; FreeDC: Boolean;
begin
  if ADcCompatible <> 0 then begin BmpDC := ADcCompatible; FreeDC := False; end else begin BmpDC := GetDC(0); FreeDC := True; end;
  GMAPICheckObj('CreateCompatibleBmp-GetDC', '', GetLastError, BmpDC <> 0, Self);
  try
   inherited Create(ADC, {$IFDEF JEDIAPI}jwaWinGdi{$ELSE}Windows{$ENDIF}.CreateCompatibleBitmap(BmpDC, ASize.x, ASize.y), ARefLifeTime);
   GMAPICheckObj('CreateCompatibleBitmap', '', GetLastError, FGdiObject <> 0, Self);
  finally
   if FreeDC then ReleaseDC(0, BmpDC);
  end;
end;

{constructor TGMGdiBitmap.CreateCompatibleBmp(const ADC, ADC: HDC; const ASize: TPoint; const ARefLifeTime: Boolean);
var BmpDC: HDC; FreeDC: Boolean;
begin
  inherited Create(ADC, 0, ARefLifeTime);
  //FreeDC := False;
  //if ADC <> 0 then begin BmpDC := ADC; FreeDC := False; end else begin BmpDC := GetDC(0); FreeDC := True; end;
  BmpDC := GetDC(0); FreeDC := True;
  GMAPICheckObj(BmpDC <> 0, Self, 'CreateCompatibleBmp.GetDC');
  try
   FGdiObject := Windows.CreateCompatibleBitmap(BmpDC, ASize.x, ASize.y);
   GMAPICheckObj(FGdiObject <> 0, Self, 'CreateCompatibleBitmap');
  finally
   if FreeDC then ReleaseDC(0, BmpDC);
  end;
end;}

constructor TGMGdiBitmap.CreateFromData(const ADC: HDC; const AWidth, AHeight, APlanes, ABitCount: LongInt; const ABits: Pointer; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, CreateBitmap(AWidth, AHeight, APlanes, ABitCount, ABits), ARefLifeTime);
  GMAPICheckObj('CreateBitmap', '', GetLastError, FGdiObject <> 0, Self);
end;


{ ------------------------ }
{ ---- TGMGdiDIBitmap ---- }
{ ------------------------ }

constructor TGMGdiDIBitmap.Create(const ADC: HDC; const ASize: TPoint; const ABitsPerPixel: Integer; const ARefLifeTime: Boolean);
begin
  FBmpInfo.biSize := SizeOf(FBmpInfo);
  FBmpInfo.biWidth := ASize.x;
  FBmpInfo.biHeight := ASize.y;
  FBmpInfo.biPlanes := 1;
  FBmpInfo.biBitCount := ABitsPerPixel;
  FBmpInfo.biCompression := BI_RGB;
  inherited Create(ADC, CreateDIBSection(0, PBitmapInfo(@FBmpInfo){$IFNDEF JEDIAPI}^{$ENDIF}, DIB_RGB_COLORS, FBits, 0, 0), ARefLifeTime);
  GMAPICheckObj('CreateDIBSection', '', GetLastError, FGdiObject <> 0, Self);
end;


{ ------------------------ }
{ ---- TGMGdiDCBitmap ---- }
{ ------------------------ }

{constructor TGMGdiDCBitmap.CreateCompatible(const ASize: TPoint; const ARefLifeTime: Boolean);
var ScreenDC: HDC;
begin
  inherited Create(0, ARefLifeTime);
  FDC := CreateCompatibleDC(0);
  GMAPICheckObj(FDC <> 0, Self, 'CreateCompatibleDC');
  ScreenDC := GetDC(0);
  GMAPICheckObj(ScreenDC <> 0, Self, 'CreateCompatible.GetDC');
  try
   FGdiObject := CreateCompatibleBitmap(ScreenDC, ASize.x, ASize.y);
   GMAPICheckObj(FGdiObject <> 0, Self, 'CreateCompatibleBitmap');
  finally
   ReleaseDC(0, ScreenDC);
  end;
  // if FDC <> 0 then
   FPrevObject := SelectObject(FDC, FGdiObject);
  FillRect(DC, GMRect(0, 0, Asize.X, Asize.y), GetStockObject(WHITE_BRUSH));
end;

function TGMGdiDCBitmap.Obj: TGMGdiDCBitmap;
begin
  Result := Self;
end;}


{ --------------------- }
{ ---- TGMWindowDC ---- }
{ --------------------- }

constructor TGMWindowDC.Create(const AWnd: HWnd; const ARefLifeTime: Boolean);
begin
  inherited Create(ARefLifeTime);
  FWnd := AWnd;
  FDC := GetDC(FWnd);
end;

destructor TGMWindowDC.Destroy;
begin
  if FDC <> 0 then ReleaseDC(FWnd, FDC);
  inherited Destroy;
end;

function TGMWindowDC.GetHandle: THandle;
begin
  Result := FDC;
end;


{ ---------------------------- }
{ ---- TGMGdiCompatibleDC ---- }
{ ---------------------------- }

//constructor TGMGdiCompatibleDC.Create(const ARefLifeTime: Boolean);
//begin
//inherited Create(ARefLifeTime);
//FCriticalSection := TGMCriticalSection.Create;
//end;

constructor TGMGdiCompatibleDC.Create(const AGdiObj: THandle; const ADcCompatible: HDC; const ARefLifeTime: Boolean);
begin
  //Assert(AGdiObj = 0);
  inherited Create(ARefLifeTime);
  FCriticalSection := TGMCriticalSection.Create;
  FDC := CreateCompatibleDC(ADcCompatible);
  GMAPICheckObj('CreateCompatibleDC', '', GetLastError, FDC <> 0, Self);
  //FGdiObject := AGdiObj;
  if AGdiObj <> 0 then {FPrevObject :=} DeleteObject(SelectObject(FDC, AGdiObj));
end;

destructor TGMGdiCompatibleDC.Destroy;
begin
  //if (FDC <> 0) and (FPrevObject <> 0) and (FGdiObject <> 0) and
     //(GetCurrentObject(FDC, GetObjectType(FGdiObject)) = FGdiObject) then SelectObject(FDC, FPrevObject);
  //FPrevObject := 0;
  if FDC <> 0 then DeleteDC(FDC);
  inherited Destroy;
end;

function TGMGdiCompatibleDC.GetHandle: THandle;
begin
  Result := FDC;
end;


{ -------------------------- }
{ ---- TGMGdiBitmapedDC ---- }
{ -------------------------- }

//destructor TGMGdiBitmapedDC.Destroy;
//begin
//inherited;
//end;

constructor TGMGdiBitmapedDC.Create(const ARefLifeTime: Boolean);
begin
  inherited Create(ARefLifeTime);
  FCriticalSection := TGMCriticalSection.Create;
end;

constructor TGMGdiBitmapedDC.CreateCompatible(const ADcCompatible: HDC; const ASize: TPoint; const ARefLifeTime: Boolean);
begin
  //inherited
  Create(ARefLifeTime);
  FDC := TGMGdiCompatibleDC.Create(0, ADcCompatible, True);
  FBitmap := TGMGdiBitmap.CreateCompatibleBmp(FDC.Handle, ADcCompatible, ASize, True);
end;

constructor TGMGdiBitmapedDC.CreateFromBmpData(const ADcCompatible: HDC; const ABmpData: TBitmap; const ARefLifeTime: Boolean);
begin
  //inherited
  Create(ARefLifeTime);
  FDC := TGMGdiCompatibleDC.Create(0, ADcCompatible, True);
  FBitmap := TGMGdiBitmap.CreateFromBmpData(FDC.Handle, ABmpData, True);
end;

constructor TGMGdiBitmapedDC.CreateFromData(const ADcCompatible: HDC; const AWidth, AHeight, APlanes, ABitCount: Integer; const ABits: Pointer; const ARefLifeTime: Boolean);
begin
  //inherited
  Create(ARefLifeTime);
  FDC := TGMGdiCompatibleDC.Create(0, ADcCompatible, True);
  FBitmap := TGMGdiBitmap.CreateFromData(FDC.Handle, AWidth, AHeight, APlanes, ABitCount, ABits, True);
end;

constructor TGMGdiBitmapedDC.CreateFromRes(const ADcCompatible: HDC; const AResName: PGMChar; const AInstance: THandle; const ACheckLoad: Boolean; const ARefLifeTime: Boolean);
begin
  //inherited
  Create(ARefLifeTime);
  FDC := TGMGdiCompatibleDC.Create(0, ADcCompatible, True);
  FBitmap := TGMGdiBitmap.CreateFromRes(FDC.Handle, AResName, AInstance, ACheckLoad, True);
end;

function TGMGdiBitmapedDC.Obj: TGMGdiBitmapedDC;
begin
  Result := Self;
end;

function TGMGdiBitmapedDC.GetHandle: THandle;
begin
  if FDC = nil then Result := 0 else Result := FDC.Handle;
end;


{ ---------------------------- }
{ ---- TGMGdiDIBitmapedDC ---- }
{ ---------------------------- }

constructor TGMGdiDIBitmapedDC.Create(const ADcCompatible: HDC; const ASize: TPoint; const ABitsPerPixel: Integer; const ARefLifeTime: Boolean);
begin
  inherited Create(ARefLifeTime);
  FDC := TGMGdiCompatibleDC.Create(0, ADcCompatible, True);
  FBitmap := TGMGdiDIBitmap.Create(FDC.Handle, ASize, ABitsPerPixel, True);
end;

//procedure TGMGdiDIBitmapedDC.DetachBitmap;
//begin
//
//end;

function TGMGdiDIBitmapedDC.GetHandle: THandle;
begin
  if FDC = nil then Result := 0 else Result := FDC.Handle;
end;


{ ----------------------- }
{ ---- TGMGdiClipRgn ---- }
{ ----------------------- }

constructor TGMGdiClipRgn.Create(const ADC: HDC; const ARgn: HRGN; const ARefLifeTime: Boolean);
//var Clip: LongInt;
begin
  inherited Create(ARefLifeTime);
  if (ADC = 0) {or (Rgn = 0)} then Exit;
  FOrgRgn := TGMGdiRegion.CreateRect(0, cNullRect, True);

  case GetClipRgn(ADC, FOrgRgn.Handle) of
   0: FOrgRgn := nil; // <- Free FOrgRgn, restore default (NULL) Region in destructor
   Low(LongInt) .. -1: GMApiCheckObj('GetClipRgn', '', GetLastError, False, Self);
   // else -> success, restore FOrgRgn in destructor
  end;

  {Clip := GetClipRgn(ADC, FOrgRgn.Handle);
  if Clip = 0 then FOrgRgn := nil
  else
  if Clip < 0 then GMApiCheckObj(False, Self, 'GetClipRgn');}

  GMApiCheckObj('SelectClipRgn', '', GetLastError, SelectClipRgn(ADC, ARgn) <> RGN_ERROR, Self);
  //GMApiCheckObj(ExtSelectClipRgn(ADC, Rgn, RGN_COPY) <> RGN_ERROR, Self, 'ExtSelectClipRgn');
  FDC := ADC; // <- skipped on exception, prevent restore in destructor
end;

destructor TGMGdiClipRgn.Destroy;
begin
  if FDC <> 0 then
   if (FOrgRgn <> nil) {and (FOrgRgn.Handle <> 0)} then SelectClipRgn(FDC, FOrgRgn.Handle) else SelectClipRgn(FDC, 0);
  inherited Destroy;
end;


{ ---------------------- }
{ ---- TGMGdiRegion ---- }
{ ---------------------- }

constructor TGMGdiRegion.CreateRect(const ADC: HDC; const ARect: TRect; const ARefLifeTime: Boolean);
begin
  with ARect do inherited Create(ADC, CreateRectRgn(Left, Top, Right, Bottom), ARefLifeTime);
  GMAPICheckObj('CreateRectRgn', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiRegion.CreateRoundRect(const ADC: HDC; const ARect: TRect; const ARounding: TPoint; const ARefLifeTime: Boolean);
begin
  //
  // CreateRoundRectRgn is one pixel too small ..
  with ARect do inherited Create(ADC, CreateRoundRectRgn(Left, Top, Right+1, Bottom+1, ARounding.x, ARounding.y), ARefLifeTime);
  GMAPICheckObj('CreateRoundRectRgn', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiRegion.CreatePolygon(const ADC: HDC; const APoints: array of TPoint; const AFilleMode: Integer; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, CreatePolygonRgn(
                    {$IFDEF JEDIAPI}@APoints[Low(APoints)]
                    {$ELSE}
                      {$IFDEF FPC}(@APoints[Low(APoints)])^
                      {$ELSE}
                      APoints[Low(APoints)]
                      {$ENDIF}
                    {$ENDIF}
                 , Length(APoints), AFilleMode), ARefLifeTime);
  //{$IFDEF FPC}
  //inherited Create(ADC, CreatePolygonRgn(PPoint(@APoints[Low(APoints)])^, Length(APoints), AFilleMode), ARefLifeTime);
  //{$ELSE}
  //inherited Create(ADC, CreatePolygonRgn({$IFDEF JEDIAPI}@{$ENDIF}APoints[Low(APoints)], Length(APoints), AFilleMode), ARefLifeTime);
  //{$ENDIF}
  GMAPICheckObj('CreatePolygonRgn', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiRegion.CreateElipse(const ADC: HDC; const ABounds: TRect; const ARefLifeTime: Boolean);
begin
  with ABounds do inherited Create(ADC, CreateEllipticRgn(Left, Top, Right, Bottom), ARefLifeTime);
  GMAPICheckObj('CreateEllipticRgn', '', GetLastError, FGdiObject <> 0, Self);
end;

constructor TGMGdiRegion.CreatefromWindow(const ADC: HDC; const AWnd: HWnd; const ARefLifeTime: Boolean);
//var Rgn: HRgn;
begin
  CreateRect(ADC, cNullRect, ARefLifeTime);
  GetWindowRgn(AWnd, Handle);
end;


{ ----------------------- }
{ ---- TGMGdiPalette ---- }
{ ----------------------- }

constructor TGMGdiPalette.Create(const ADC: HDC; const ALogPalette: TLogPalette; const ARefLifeTime: Boolean);
begin
  {$IFDEF FPC}
  inherited Create(ADC, CreatePalette(PLogPalette(@ALogPalette)^), ARefLifeTime);
  {$ELSE}
  inherited Create(ADC, CreatePalette(ALogPalette), ARefLifeTime);
  {$ENDIF}
  //FGdiObject := CreatePalette(ALogPalette);
  GMAPICheckObj('CreatePalette', '', GetLastError, FGdiObject <> 0, Self);
end;


{ --------------------------------- }
{ ---- TGMGdiTextColorSelector ---- }
{ --------------------------------- }

constructor TGMGdiTextColorSelector.Create(const ADC: HDC; const ATextColor, ATextBkColor: COLORREF; const ARefLifeTime: Boolean = True);
begin
  inherited Create(ARefLifeTime);
  FDC := ADC;
  FTextColor := ATextColor;
  FTextBkColor := ATextBkColor;
  if FDC = 0 then Exit;
  if FTextColor <> clrTransparent then FPrevTextColor := SetTextColor(ADC, GMRGBColor(FTextColor));
  if FTextBkColor = clrTransparent then FPrevTextBkMode := SetBkMode(ADC, TRANSPARENT) else
   begin
    FPrevTextBkMode := SetBkMode(FDC, OPAQUE);
    FPrevBkColor := SetBkColor(ADC, GMRGBColor(FTextBkColor));
   end;
end;

destructor TGMGdiTextColorSelector.Destroy;
begin
  // Only restore if our AColor is still the current ?!?
  if FDC <> 0 then
   begin
    if (FPrevTextColor <> CLR_INVALID) and (GetTextColor(FDC) = FTextColor) then SetTextColor(FDC, FPrevTextColor);
    if (FTextBkColor <> clrTransparent) and (FPrevBkColor <> CLR_INVALID) and (GetBkColor(FDC) = FTextBkColor) then SetBkColor(FDC, FPrevBkColor);
    if FPrevTextBkMode > 0 then SetBkMode(FDC, FPrevTextBkMode);
   end;

  inherited Destroy;
end;


{ ---------------------------- }
{ ---- TGMGdiDCKeeperBase ---- }
{ ---------------------------- }

constructor TGMGdiDCKeeperBase.Create(const ADC: HDC; const ARefLifeTime: Boolean = True);
begin
  inherited Create(ARefLifeTime);
  if ADC <> 0 then FDC := ADC;
end;

function TGMGdiDCKeeperBase.GetHandle: THandle;
begin
  Result := FDC;
end;


{ ----------------------------- }
{ ---- TGMGdiDCStateKeeper ---- }
{ ----------------------------- }

constructor TGMGdiDCStateKeeper.Create(const ADC: HDC; const ARefLifeTime: Boolean = True);
begin
  inherited Create(ADC, ARefLifeTime);
  if ADC = 0 then Exit;
  FDCState := SaveDC(ADC);
  GMAPICheckObj('SaveDC', '', GetLastError, FDCState <> 0, Self);
end;

destructor TGMGdiDCStateKeeper.Destroy;
begin
  if (FDC <> 0) and (FDCState <> 0) then RestoreDC(FDC, FDCState);
  inherited Destroy;
end;


{ ------------------------- }
{ ---- TGMGdiROPKeeper ---- }
{ ------------------------- }

constructor TGMGdiROPKeeper.Create(const ADC: HDC; const AROP: Integer; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, ARefLifeTime);
  if ADC = 0 then Exit;
  FROP := SetROP2(ADC, AROP);
  GMApiCheckObj('TGMGdiROPKeeper.Create - SetROP2', '', GetLastError, FROP <> 0, Self);
end;

destructor TGMGdiROPKeeper.Destroy;
begin
  if (FDC <> 0) and (FROP <> 0) then SetROP2(FDC, FROP);
  inherited Destroy;
end;


{ ----------------------------- }
{ ---- TGMGdiClipRgnKeeper ---- }
{ ----------------------------- }

constructor TGMGdiClipRgnKeeper.Create(const ADC: HDC; const AAdditionalRgn: HRGN; const AMode: LongInt; const ARefLifeTime: Boolean);
begin
  inherited Create(ADC, ARefLifeTime);
  if ADC = 0 then Exit;
  FOldClipRgn := TGMGdiRegion.CreateRect(0, cNullRect, True);
  case GetClipRgn(FDC, FOldClipRgn.Handle) of
   0: FOldClipRgn := TGMHandleObj.Create(0, True); // <- will remove the clipping region when restored
   -1: FOldClipRgn := nil; // <- error, dont restore clipping region
   //1: Success
  end;
  //Clip :=  GetDeviceCaps(ADC, SHADEBLENDCAPS); // CLIPCAPS    TECHNOLOGY
  //if WindowFromDC(ADC) <> 0 then ExtSelectClipRgn(ADC, AreaRgn.Handle, RGN_AND) else
  // with RSurface do IntersectClipRect(ADC, Left, top, Right, Bottom);
  //ExtSelectClipRgn(ADC, AreaRgn.Handle, RGN_AND);
  if AAdditionalRgn <> 0 then ExtSelectClipRgn(FDC, AAdditionalRgn, AMode);
end;

destructor TGMGdiClipRgnKeeper.Destroy;
begin
  if (FDC <> 0) and (FOldClipRgn <> nil) then SelectClipRgn(FDC, FOldClipRgn.Handle); // (FOldClipRgn.Handle <> 0)
  inherited Destroy;
end;


{ --------------------------- }
{ ---- TGMCursorWithPlus ---- }
{ --------------------------- }

constructor TGMCursorWithPlus.Create(const ACursor: MakeIntResource; const ARefLifeTime: Boolean);
const cPlusSz = 9; cFrm = 0; // 1
var iconInfo: TIconInfo; bmpMask, bmpColor, dcSrc, dcAlpha, bmpAlpha: IGMGetHandle; imgSz, maskSz: TPoint; pen, brush: IUnknown;
    rPlus: TRect; x, y: LongInt; ppxColor: PDword; bmpAlphaHdr: TBitmapV5Header; aplphaBmpBits: Pointer; //  pxColor: COLORREF;
begin
  inherited Create(ARefLifeTime);
  try
   //FillChar(iconInfo, SizeOf(iconInfo), 0);
   iconInfo := Default(TIconInfo);
   GMApiCheckObj('GetIconInfo', '', GetLastError, GetIconInfo(LoadCursor(0, ACursor), iconInfo), Self);

   bmpMask := TGMGdiObject.Create(0, iconInfo.hbmMask);
   bmpColor := TGMGdiObject.Create(0, iconInfo.hbmColor);

   maskSz := GMBitmapSize(bmpMask);
   imgSz := GMBitmapSize(bmpColor);
   aplphaBmpBits := nil;

   if (imgSz.x = 0) or (imgSz.y = 0) then raise EAbort.Create('Zero cursor image size');

   //
   // Create a cursor with partial tranparency see: http://support.microsoft.com/kb/318876/en-us
   //
   //FillChar(bmpAlphaHdr, SizeOf(bmpAlphaHdr), 0);
   bmpAlphaHdr := Default(TBitmapV5Header);
   bmpAlphaHdr.bV5Size := SizeOf(bmpAlphaHdr);
   bmpAlphaHdr.bV5Width := imgSz.x;
   bmpAlphaHdr.bV5Height := imgSz.y;
   bmpAlphaHdr.bV5Planes := 1;
   bmpAlphaHdr.bV5BitCount := 32;
   bmpAlphaHdr.bV5Compression := BI_BITFIELDS;
   bmpAlphaHdr.bV5RedMask   := $00FF0000;
   bmpAlphaHdr.bV5GreenMask := $0000FF00;
   bmpAlphaHdr.bV5BlueMask  := $000000FF;
   bmpAlphaHdr.bV5AlphaMask := $FF000000;

   bmpAlpha := TGMGdiObject.Create(0, CreateDIBSection(0, PBitmapInfo(@bmpAlphaHdr){$IFNDEF JEDIAPI}^{$ENDIF}, DIB_RGB_COLORS, aplphaBmpBits, 0, 0));
   if aplphaBmpBits = nil then raise EAbort.Create('Alpha bitmap bits pointer is <nil>');

   dcAlpha := TGMGdiCompatibleDC.Create(bmpAlpha.Handle);

   rPlus := GMRect(imgSz.x - cPlusSz - cFrm, imgSz.y - cPlusSz - cFrm, imgSz.x - cFrm, imgSz.y - cFrm);

   dcSrc := TGMGdiCompatibleDC.Create(bmpColor.Handle);
   BitBlt(dcAlpha.Handle, 0, 0, imgSz.x, imgSz.y, dcSrc.Handle, 0, 0, SRCCOPY);
   dcSrc := nil;

   brush := TGMGdiBrush.Create(dcAlpha.Handle, clWhite);
   pen := TGMGdiPen.Create(dcAlpha.Handle, clBlack);
   with rPlus do
    begin
     Rectangle(dcAlpha.Handle, Left, Top, Right, Bottom);
     MoveToEx(dcAlpha.Handle, Left + 2, (Top + Bottom) shr 1, nil);
     LineTo(dcAlpha.Handle, Right - 2, (Top + Bottom) shr 1);
     MoveToEx(dcAlpha.Handle, (Left + Right) shr 1, Top + 2, nil);
     LineTo(dcAlpha.Handle, (Left + Right) shr 1, Bottom - 2);
    end;

   for x:=rPlus.Left to rPlus.Right-1 do
 //  for y:=rPlus.Top to rPlus.Bottom-1 do
    for y:=cFrm to cPlusSz+cFrm-1 do // <- the bitmap is bottom up!
     begin
      // NOTE: SetPixel does not work with alpha values! We need to directly set the bits!
      ppxColor := GMAddPtr(aplphaBmpBits, ((y * imgSz.x) + x) * 4);
      ppxColor^ := ppxColor^ or $ff000000;
     end;

   dcAlpha := nil;

 //iconInfo.hbmMask := bmpMask.Handle;
   iconInfo.hbmColor := bmpAlpha.Handle;

   FHandle := CreateIconIndirect(iconInfo);
   GMApiCheckObj('CreateIconIndirect', '', GetLastError, FHandle <> 0, Self);

   FDoDestroyCursor := True;
  except
// FDoDestroyCursor := False;
   FHandle := LoadCursor(0, ACursor);
  end;
end;

destructor TGMCursorWithPlus.Destroy;
begin
  if (FHandle <> 0) and FDoDestroyCursor then DestroyCursor(FHandle);
  inherited Destroy;
end;

function TGMCursorWithPlus.GetHandle: THandle;
begin
  Result := FHandle;
end;


{ ---------------------------- }
{ ---- TGMImageCollection ---- }
{ ---------------------------- }

constructor TGMImageCollection.Create(const ADcCompatible: HDC; const ARefLifeTime: Boolean);
begin
  //inherited
  Create(ARefLifeTime);
  FDcCompatible := ADcCompatible;
end;

constructor TGMImageCollection.Create(const AResBmpName: PGMChar; const AInstance: THandle; const ASplitWidth: Integer;
  ATransparentColor: COLORREF; const ADcCompatible: HDC; const ALightnessDelta, ADisabledLightnessDelta: LongInt; const ARefLifeTime: Boolean);
var bmp: IGMGetHandle;
begin
  Create(ADcCompatible, ARefLifeTime);
  bmp := TGMGdiBitmap.CreateFromRes(0, AResBmpName, AInstance);
  AddBitmap(bmp.Handle, ASplitWidth, ATransparentColor, ALightnessDelta, ADisabledLightnessDelta);
end;

function TGMImageCollection.Obj: TGMImageCollection;
begin
  Result := Self;
end;

procedure TGMImageCollection.EnlargeBitmaps(const ADelta: TPoint);
var oldSize: TPoint; newDC: IGMGetHandle;
begin
  oldSize := FBmpSize;
//if FBkgndMaskDC = nil then oldSize := cNullPoint else oldSize := GMBitmapSize(FBkgndMaskDC.Obj.FBitmap);
//newSize := GMPoint(oldSize.x + ADelta.x, Max(oldSize.y, ADelta.y));
  FBmpSize := GMPoint(FBmpSize.x + ADelta.x, Max(FBmpSize.y, ADelta.y));

  newDC := TGMGdiBitmapedDC.CreateCompatible(FDcCompatible, FBmpSize);
//newDC := TGMGdiDIBitmapedDC.Create(FDcCompatible, FBmpSize, 32);
  if FImagesDC <> nil then BitBlt(newDC.Handle, 0, 0, oldSize.x, oldSize.y, FImagesDC.Handle, 0, 0, SRCCOPY);
  FImagesDC := newDC;

  newDC := TGMGdiBitmapedDC.CreateFromData(FDcCompatible, FBmpSize.x, FBmpSize.y, 1, 1, nil);
  if FBkgndMaskDC <> nil then BitBlt(newDC.Handle, 0, 0, oldSize.x, oldSize.y, FBkgndMaskDC.Handle, 0, 0, SRCCOPY);
  FBkgndMaskDC := newDC;

  newDC := TGMGdiBitmapedDC.CreateCompatible(FDcCompatible, FBmpSize);
  if FDisabledImgDC <> nil then BitBlt(newDC.Handle, 0, 0, oldSize.x, oldSize.y, FDisabledImgDC.Handle, 0, 0, SRCCOPY);
  FDisabledImgDC := newDC;
end;

procedure TGMImageCollection.BuildDisabledImg(const AXOffset: Integer; const ASize: TPoint);
begin
  if (FImagesDC = nil) or (FDisabledImgDC = nil) then Exit;
  BitBlt(FDisabledImgDC.Handle, AXOffset, cImgCollYOffs, ASize.x, ASize.y, FImagesDC.Handle, AXOffset, cImgCollYOffs, SRCCOPY);
  // No need to mask the disabled image again after grayscaling the image. The grayscaling will leave masked pixels (black pixels) intact!
  GMGrayScaleDCArea(FDisabledImgDC.Handle, GMRect(AXOffset, cImgCollYOffs, AXOffset + ASize.x, cImgCollYOffs + ASize.y));
end;

procedure TGMImageCollection.AddIcon(const AIcon: HIcon; const ALightnessDelta: LongInt);
var xOffs: LongInt; iconSz: TPoint; // iconInfo: TIconInfo;
begin
  if AIcon = 0 then Exit;

  iconSz := GMIconSize(AIcon);
  if (iconSz.x <= 0) or (iconSz.y <= 0) then Exit;

  EnlargeBitmaps(iconSz);

  if Length(FImageDescs) <= 0 then xOffs := 0 else xOffs := FImageDescs[High(FImageDescs)].XOffset + FImageDescs[High(FImageDescs)].Size.x;

  if FImagesDC <> nil then
   begin
    DrawIconEx(FImagesDC.Handle, xOffs, cImgCollYOffs, AIcon, iconSz.x, iconSz.y, 0, 0, DI_IMAGE);
    if ALightnessDelta <> 0 then
     GMChangeDCAreaLightness(FImagesDC.Handle, GMRect(xOffs, cImgCollYOffs, xOffs + iconSz.x, cImgCollYOffs + iconSz.y), ALightnessDelta);
   end;

  if FBkgndMaskDC <> nil then DrawIconEx(FBkgndMaskDC.Handle, xOffs, cImgCollYOffs, AIcon, iconSz.x, iconSz.y, 0, 0, DI_MASK);

  BuildDisabledImg(xOffs, iconSz);

  SetLength(FImageDescs, Length(FImageDescs)+1);
  FImageDescs[High(FImageDescs)].XOffset := xOffs;
  FImageDescs[High(FImageDescs)].Size := iconSz;
end;

procedure TGMImageCollection.AddBitmap(const ABitmap: HBitmap; ASplitWidth: LongInt; ATransparentColor: COLORREF;
    const ALightnessDelta, ADisabledLightnessDelta: LongInt);
var bmpSz: TPoint; xPos, xOffs: LongInt; bmpDC: IGMGetHandle;

  procedure AddBmpPart(const AbmpDC: HDC; const x, y, w, h: LongInt; const ATransparentColor: COLORREF);
  var imgMask: IGMGetHandle; oldBmpBkgndColor: COLORREF;
  begin
    // Build the background mask
    imgMask := TGMGdiBitmapedDC.CreateFromData(FDcCompatible, w, h, 1, 1, nil);
    oldBmpBkgndColor := SetBkColor(AbmpDC, ATransparentColor);
    BitBlt(FBkgndMaskDC.Handle, xOffs + x, cImgCollYOffs, w, h, AbmpDC, x, y, SRCCOPY);
    SetBkColor(AbmpDC, oldBmpBkgndColor);

    // Build the image mask as the inverse of the background mask
    BitBlt(imgMask.Handle, 0, 0, w, h, FBkgndMaskDC.Handle, xOffs + x, cImgCollYOffs, NOTSRCCOPY);

    // Copy the image
    BitBlt(FImagesDC.Handle, xOffs + x, cImgCollYOffs, w, h, AbmpDC, x, y, SRCCOPY);

    // Mask out the transparent pixels in the image using the image mask
    BitBlt(FImagesDC.Handle, xOffs + x, cImgCollYOffs, w, h, imgMask.Handle, 0, 0, SRCAND);

    BuildDisabledImg(xOffs + x, GMPoint(w, h));
    if (ADisabledLightnessDelta <> 0) and (FDisabledImgDC <> nil) then
       GMChangeDCAreaLightness(FDisabledImgDC.Handle, GMRect(xOffs + x, 0, w + xOffs + x, h), ADisabledLightnessDelta);
  end;

begin
  if ABitmap = 0 then Exit;
  bmpSz := GMBitmapSize(ABitmap);
  if (bmpSz.x <= 0) or (bmpSz.y <= 0) then Exit;

  EnlargeBitmaps(bmpSz);

  if ASplitWidth = 0 then ASplitWidth := bmpSz.y else if ASplitWidth < 0 then ASplitWidth := bmpSz.x;

  if Length(FImageDescs) <= 0 then xOffs := 0 else xOffs := FImageDescs[High(FImageDescs)].XOffset + FImageDescs[High(FImageDescs)].Size.x;

  bmpDC := TGMGdiCompatibleDC.Create(ABitmap, FDcCompatible, True);
  if ALightnessDelta <> 0 then GMChangeDCAreaLightness(bmpDC.Handle, GMRect(cNullPoint, bmpSz), ALightnessDelta);

  if ATransparentColor = clrAutoTransparent then
    ATransparentColor := GMFindMajorityColor(bmpDC.Handle, GMRect(cNullPoint, bmpSz));

  xPos := 0;
  while xPos + ASplitWidth <= bmpSz.x do
   begin
    AddBmpPart(bmpDC.Handle, xPos, 0, ASplitWidth, bmpSz.y, ATransparentColor);

    SetLength(FImageDescs, Length(FImageDescs)+1);
    FImageDescs[High(FImageDescs)].XOffset := xOffs + xPos;
    FImageDescs[High(FImageDescs)].Size := GMPoint(ASplitWidth, bmpSz.y);

    Inc(xPos, ASplitWidth);
   end;
end;

procedure TGMImageCollection.DrawImage(const AImageIndex: LongInt; const ADestDC: HDC; const ADstRect: TRect; const ADisabled: Boolean);
var imgDesc: PGMImgCollectionDesc; dstSize: TPoint; dcBkgnd, srcDC: IGMGetHandle;
begin
  if (FImagesDC = nil) or not GMIsInRange(AImageIndex, Low(FImageDescs), High(FImageDescs)) then Exit;

  dstSize := GMRectSize(ADstRect);
  imgDesc := @FImageDescs[AImageIndex];

  // Get original background
  dcBkgnd := TGMGdiBitmapedDC.CreateCompatible(ADestDC, imgDesc.Size, True);
//dcBkgnd := TGMGdiDIBitmapedDC.Create(ADestDC, imgDesc.Size, 24, True);
  SetStretchBltMode(dcBkgnd.Handle, HALFTONE);
  StretchBlt(dcBkgnd.Handle, 0, 0, imgDesc.Size.x, imgDesc.Size.y, ADestDC, ADstRect.left, ADstRect.Top, dstSize.x, dstSize.y, SRCCOPY);

  // Mask the background
  BitBlt(dcBkgnd.Handle, 0, 0, imgDesc.Size.x, imgDesc.Size.y, FBkgndMaskDC.Handle, imgDesc.XOffset, cImgCollYOffs, SRCAND);

  // Combine masked background with masked image
  if ADisabled then srcDC := FDisabledImgDC else srcDC := FImagesDC;
  if srcDC <> nil then BitBlt(dcBkgnd.Handle, 0, 0, imgDesc.Size.x, imgDesc.Size.y, srcDC.Handle, imgDesc.XOffset, cImgCollYOffs, SRCPAINT);  

  // Draw the Result back to original device context
  StretchBlt(ADestDC, ADstRect.left, ADstRect.Top, dstSize.x, dstSize.y, dcBkgnd.Handle, 0, 0, imgDesc.Size.x, imgDesc.Size.y, SRCCOPY);
end;


//initialization

  //vUICalcMemDC := TGMGdiCompatibleDC.Create(0, True);
  //vCsUICalcMemDC := TGMCriticalSection.Create;

end.