Skip to content

Instantly share code, notes, and snippets.

@CynicRus
Last active January 10, 2026 01:16
Show Gist options
  • Select an option

  • Save CynicRus/8b31691e88b6354710419a58a80527db to your computer and use it in GitHub Desktop.

Select an option

Save CynicRus/8b31691e88b6354710419a58a80527db to your computer and use it in GitHub Desktop.
GDI+ Canvas for Lazarus
unit gdiplus_canvas;
{$mode ObjFPC}{$H+}
interface
uses
{$IFDEF MSWINDOWS}Windows, gdipapi,{$ENDIF}lclintf, lcltype, Classes, SysUtils, Controls,
StdCtrls, GraphType, Graphics, FPImage, FPCanvas, Math;
type
{$IFDEF MSWINDOWS}
{ TGDIPlusCanvas }
TGDIPlusCanvas = class(TFPCustomCanvas)
private
FAutoRedraw: boolean;
FCopyMode: TCopyMode;
FGpGraphics: GpGraphics;
FGpPen: GpPen;
FGpBrush: GpBrush;
FGpFont: GpFont;
FFontFamily: GpFontFamily;
FImage: GpBitmap;
FGpClipRegion: GpRegion;
FLazBrush: TBrush;
FLazFont: TFont;
FLazPen: TPen;
FOnChange: TNotifyEvent;
FOnChanging: TNotifyEvent;
FPath: GpPath;
FRegion: TRegion;
FTextStyle: TTextStyle;
FPenChanged: boolean;
FBrushChanged: boolean;
FFontChanged: boolean;
// API compatibility
FCurrentPos: TPoint;
FMemDC: HDC;
FBitmap: HBITMAP;
FOwnsBitmap: boolean;
// Для кэширования измерений текста
FLastMeasuredText: string;
FLastTextSize: TSize;
// для кэширования шрифтов
FLastFontName: UnicodeString;
FLastFontSize: Integer;
FLastFontStyle: Integer; // GDI+ FontStyle flags
// Для Save/RestoreHandleState
FStateToken: ULONG;
// AutoRedraw
FPresentDC: HDC;
FPresentX: Integer;
FPresentY: Integer;
// Для Lock/Unlock
FLock: TRTLCriticalSection;
procedure InitFromDC(DC: HDC);
procedure InitFromBitmap(ABitmap: HBITMAP; AOwns: boolean);
function FPColorToARGB(const Color: TFPColor): ARGB;
function GetHandle: HDC;
function GetPixel(X, Y: integer): TColor;
procedure SetAutoRedraw(AValue: boolean);
procedure SetHandle(AValue: HDC);
procedure SetLazBrush(AValue: TBrush);
procedure SetLazFont(AValue: TFont);
procedure SetLazPen(AValue: TPen);
procedure SetPixel(X, Y: integer; AValue: TColor);
procedure SetRegion(AValue: TRegion);
procedure UpdateBrush;
procedure UpdatePen;
procedure UpdateFont;
procedure CheckStatus(Status: TStatus);
procedure ApplyClipping;
procedure ApplyDrawingMode;
procedure ApplyInterpolation;
function CreateGDIPBitmap(Image: TFPCustomImage): GpBitmap;
procedure PenChanging(APen: TObject); virtual;
procedure FontChanging(AFont: TObject); virtual;
procedure BrushChanging(ABrush: TObject); virtual;
procedure RegionChanging(ARegion: TObject); virtual;
procedure BrushChanged(ABrush: TObject);
procedure FontChanged(AFont: TObject);
procedure PenChanged(APen: TObject);
procedure RegionChanged(ARegion: TObject);
protected
function DoCreateDefaultFont: TFPCustomFont; override;
function DoCreateDefaultPen: TFPCustomPen; override;
function DoCreateDefaultBrush: TFPCustomBrush; override;
procedure SetColor(x, y: integer; const Value: TFPColor); override;
function GetColor(x, y: integer): TFPColor; override;
procedure SetHeight(AValue: integer); override;
function GetHeight: integer; override;
procedure SetWidth(AValue: integer); override;
function GetWidth: integer; override;
procedure DoTextOut(x, y: integer; Text: ansistring); override;
procedure DoGetTextSize(Text: ansistring; var w, h: integer); override;
function DoGetTextHeight(Text: ansistring): integer; override;
function DoGetTextWidth(Text: ansistring): integer; override;
procedure DoRectangle(const Bounds: TRect); override;
procedure DoRectangleFill(const Bounds: TRect); override;
procedure DoEllipseFill(const Bounds: TRect); override;
procedure DoEllipse(const Bounds: TRect); override;
procedure DoPolygonFill(const points: array of TPoint); override;
procedure DoPolygon(const points: array of TPoint); override;
procedure DoPolyline(const points: array of TPoint); override;
procedure DoFloodFill(x, y: integer); override;
procedure DoLine(x1, y1, x2, y2: integer); override;
procedure DoCopyRect(x, y: integer; canvas: TFPCustomCanvas;
const SourceRect: TRect); override;
procedure DoDraw(x, y: integer; const image: TFPCustomImage); override;
procedure DoRadialPie(x1, y1, x2, y2, StartAngle16Deg, Angle16DegLength: integer);
override;
procedure DoPolyBezier(Points: PPoint; NumPts: integer;
Filled, Continuous: boolean); override;
function GetClipRect: TRect; override;
procedure SetClipRect(const AValue: TRect); override;
function GetClipping: boolean; override;
procedure SetClipping(const AValue: boolean); override;
public
constructor Create(DC: HDC);
constructor CreateFromBitmap(ABitmap: HBitmap); overload;
constructor CreateSize(AWidth, AHeight: integer);
destructor Destroy; override;
procedure StretchDraw(x, y, w, h: integer; Source: TFPCustomImage); override;
procedure StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic);
procedure Erase; override;
function GetBitmap: HBITMAP;
procedure Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: integer);
virtual;
procedure Arc(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: integer); virtual;
procedure ArcTo(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: integer); virtual;
procedure AngleArc(X, Y: integer; Radius: longword; StartAngle, SweepAngle: single);
procedure BrushCopy(ADestRect: TRect; ABitmap: Graphics.TBitmap;
ASourceRect: TRect; ATransparentColor: TColor); virtual;
procedure Chord(x1, y1, x2, y2, Angle16Deg, Angle16DegLength: integer); virtual;
procedure Chord(x1, y1, x2, y2, SX, SY, EX, EY: integer); virtual;
procedure CopyRect(const Dest: TRect; SrcCanvas: TCanvas; const Source: TRect)
virtual; reintroduce;
procedure Draw(X, Y: integer; SrcGraphic: TGraphic); virtual; reintroduce;
procedure DrawFocusRect(const ARect: TRect); virtual;
procedure Ellipse(const ARect: TRect);
procedure Ellipse(x1, y1, x2, y2: integer); virtual;
procedure FillRect(const ARect: TRect); virtual;
procedure FillRect(X1, Y1, X2, Y2: integer);
procedure FloodFill(X, Y: integer; FillColor: TColor;
FillStyle: TFillStyle); virtual;
procedure Frame3d(var ARect: TRect; const FrameWidth: integer;
const Style: TGraphicsBevelCut); virtual;
procedure Frame3D(var ARect: TRect; TopColor, BottomColor: TColor;
const FrameWidth: integer); overload;
procedure Frame(const ARect: TRect); virtual;
procedure Frame(X1, Y1, X2, Y2: integer);
procedure FrameRect(const ARect: TRect); virtual;
procedure FrameRect(X1, Y1, X2, Y2: integer);
function GetTextMetrics(out TM: TLCLTextMetric): boolean; virtual;
procedure GradientFill(ARect: TRect; AStart, AStop: TColor;
ADirection: TGradientDirection);
procedure Pie(EllipseX1, EllipseY1, EllipseX2, EllipseY2, StartX,
StartY, EndX, EndY: integer); virtual;
procedure PolyBezier(const Points: array of TPoint; Filled: boolean = False;
Continuous: boolean = True);
procedure Polygon(const Points: array of TPoint; Winding: boolean;
StartIndex: integer = 0; NumPts: integer = -1);
procedure Polygon(Points: PPoint; NumPts: integer;
Winding: boolean = False); virtual;
procedure Polygon(const Points: array of TPoint);
procedure Polyline(const Points: array of TPoint; StartIndex: integer;
NumPts: integer = -1);
procedure Polyline(Points: PPoint; NumPts: integer); virtual;
procedure Polyline(const Points: array of TPoint);
procedure Rectangle(X1, Y1, X2, Y2: integer); virtual;
procedure Rectangle(const ARect: TRect);
procedure RoundRect(X1, Y1, X2, Y2: integer; RX, RY: integer); virtual;
procedure RoundRect(const Rect: TRect; RX, RY: integer);
procedure TextOut(X, Y: integer; const Text: string); virtual;
procedure TextRect(const ARect: TRect; X, Y: integer; const Text: string);
procedure TextRect(ARect: TRect; X, Y: integer; const Text: string;
const Style: TTextStyle); virtual;
function TextExtent(const Text: string): TSize; virtual;
function TextHeight(const Text: string): integer; virtual;
function TextWidth(const Text: string): integer; virtual;
function TextFitInfo(const Text: string; MaxWidth: integer): integer;
procedure DrawToCanvas(DestCanvas: TCanvas; X, Y: integer);
procedure MoveTo(X, Y: integer); virtual;
procedure LineTo(X, Y: integer); virtual;
procedure Lock; virtual;
procedure Unlock; virtual;
procedure Refresh; virtual;
procedure SaveHandleState; virtual;
procedure RestoreHandleState; virtual;
function HandleAllocated: boolean; virtual;
function TryLock: boolean; virtual;
procedure RealizeAntialiasing; virtual;
procedure RealizeAutoRedraw; virtual;
property Pixels[X, Y: integer]: TColor read GetPixel write SetPixel;
property Handle: HDC read GetHandle write SetHandle;
property TextStyle: TTextStyle read FTextStyle write FTextStyle;
property PenPos: TPoint read FCurrentPos;
published
property AutoRedraw: boolean read FAutoRedraw write SetAutoRedraw;
property Brush: TBrush read FLazBrush write SetLazBrush;
property CopyMode: TCopyMode read FCopyMode write FCopyMode default cmSrcCopy;
property Font: TFont read FLazFont write SetLazFont;
property Height: integer read GetHeight;
property Pen: TPen read FLazPen write SetLazPen;
property Region: TRegion read FRegion write SetRegion;
property Width: integer read GetWidth;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
end;
var
GdiPlusToken: ULONG;
StartupInput: TGdiplusStartupInput;
{$ENDIF}
implementation
{$IFDEF MSWINDOWS}
{ Helper Initialization Procedures }
procedure TGDIPlusCanvas.InitFromDC(DC: HDC);
begin
FMemDC := DC;
FBitmap := 0;
FOwnsBitmap := False;
FPresentDC := 0;
FPresentX := 0;
FPresentY := 0;
CheckStatus(GdipCreateFromHDC(FMemDC, FGpGraphics));
end;
procedure TGDIPlusCanvas.InitFromBitmap(ABitmap: HBITMAP; AOwns: boolean);
var
bm: BITMAP;
bmi: BITMAPINFO;
pBits: Pointer;
srcDC: HDC;
begin
GetObject(ABitmap, SizeOf(BITMAP), @bm);
FillChar(bmi, SizeOf(bmi), 0);
with bmi.bmiHeader do
begin
biSize := SizeOf(BITMAPINFOHEADER);
biWidth := bm.bmWidth;
biHeight := -bm.bmHeight; // Top-down!
biPlanes := 1;
biBitCount := 32;
biCompression := BI_RGB;
end;
FMemDC := CreateCompatibleDC(0);
FBitmap := CreateDIBSection(FMemDC, bmi, DIB_RGB_COLORS, pBits, 0, 0);
if FBitmap = 0 then
raise Exception.Create('CreateDIBSection failed');
SelectObject(FMemDC, FBitmap);
if AOwns then
begin
srcDC := CreateCompatibleDC(0);
SelectObject(srcDC, ABitmap);
BitBlt(FMemDC, 0, 0, bm.bmWidth, bm.bmHeight, srcDC, 0, 0, SRCCOPY);
DeleteDC(srcDC);
DeleteObject(ABitmap);
end
else
begin
srcDC := CreateCompatibleDC(0);
SelectObject(srcDC, ABitmap);
BitBlt(FMemDC, 0, 0, bm.bmWidth, bm.bmHeight, srcDC, 0, 0, SRCCOPY);
DeleteDC(srcDC);
end;
FOwnsBitmap := True;
FPresentDC := 0;
FPresentX := 0;
FPresentY := 0;
CheckStatus(GdipCreateBitmapFromHBITMAP(FBitmap, 0, FImage));
CheckStatus(GdipGetImageGraphicsContext(FImage, FGpGraphics));
//CheckStatus(GdipSetTextRenderingHint(FGpGraphics, TextRenderingHintAntiAlias));
CheckStatus(GdipSetTextRenderingHint(FGpGraphics, TextRenderingHintClearTypeGridFit));
CheckStatus(GdipSetSmoothingMode(FGpGraphics, SmoothingModeHighQuality)); // для фигур
CheckStatus(GdipSetPixelOffsetMode(FGpGraphics, PixelOffsetModeHighQuality)); // четкость пикселей
CheckStatus(GdipSetCompositingQuality(FGpGraphics, CompositingQualityHighQuality));
end;
{ Constructors & Destructor }
constructor TGDIPlusCanvas.Create(DC: HDC);
begin
inherited Create;
FLazFont := TFont(inherited Font);
FLazPen := TPen(inherited Pen);
FLazBrush := TBrush(inherited Brush);
FLazFont.OnChanging := @FontChanging;
FLazFont.OnChange := @FontChanged;
FLazPen.OnChanging := @PenChanging;
FLazPen.OnChange := @PenChanged;
FLazBrush.OnChanging := @BrushChanging;
FLazBrush.OnChange := @BrushChanged;
FRegion := TRegion.Create;
FRegion.OnChanging := @RegionChanging;
FRegion.OnChange := @RegionChanged;
FPenChanged := True;
FBrushChanged := True;
FFontChanged := True;
FCurrentPos := Point(0, 0);
InitFromDC(DC);
CheckStatus(GdipCreateSolidFill(MakeColor(255, 0, 0, 0), FGpBrush));
CheckStatus(GdipCreatePen1(MakeColor(255, 0, 0, 0), 1.0, UnitPixel, FGpPen));
CheckStatus(GdipCreateFontFamilyFromName('Arial', nil, FFontFamily));
CheckStatus(GdipCreatePath(FillModeAlternate, FPath));
CheckStatus(GdipSetSmoothingMode(FGpGraphics, SmoothingModeAntiAlias));
ApplyDrawingMode;
ApplyInterpolation;
Windows.InitializeCriticalSection(FLock);
end;
constructor TGDIPlusCanvas.CreateFromBitmap(ABitmap: HBitmap);
begin
inherited Create;
FLazFont := TFont(inherited Font);
FLazPen := TPen(inherited Pen);
FLazBrush := TBrush(inherited Brush);
FLazFont.OnChanging := @FontChanging;
FLazFont.OnChange := @FontChanged;
FLazPen.OnChanging := @PenChanging;
FLazPen.OnChange := @PenChanged;
FLazBrush.OnChanging := @BrushChanging;
FLazBrush.OnChange := @BrushChanged;
FRegion := TRegion.Create;
FRegion.OnChanging := @RegionChanging;
FRegion.OnChange := @RegionChanged;
FPenChanged := True;
FBrushChanged := True;
FFontChanged := True;
FCurrentPos := Point(0, 0);
InitFromBitmap(ABitmap, False);
CheckStatus(GdipCreateSolidFill(MakeColor(255, 0, 0, 0), FGpBrush));
CheckStatus(GdipCreatePen1(MakeColor(255, 0, 0, 0), 1.0, UnitPixel, FGpPen));
CheckStatus(GdipCreateFontFamilyFromName('Arial', nil, FFontFamily));
CheckStatus(GdipCreatePath(FillModeAlternate, FPath));
CheckStatus(GdipSetSmoothingMode(FGpGraphics, SmoothingModeAntiAlias));
ApplyDrawingMode;
ApplyInterpolation;
Windows.InitializeCriticalSection(FLock);
end;
constructor TGDIPlusCanvas.CreateSize(AWidth, AHeight: integer);
var
ScreenDC: HDC;
begin
inherited Create;
FLazFont := TFont(inherited Font);
FLazPen := TPen(inherited Pen);
FLazBrush := TBrush(inherited Brush);
FLazFont.OnChanging := @FontChanging;
FLazFont.OnChange := @FontChanged;
FLazPen.OnChanging := @PenChanging;
FLazPen.OnChange := @PenChanged;
FLazBrush.OnChanging := @BrushChanging;
FLazBrush.OnChange := @BrushChanged;
FRegion := TRegion.Create;
FRegion.OnChanging := @RegionChanging;
FRegion.OnChange := @RegionChanged;
FPenChanged := True;
FBrushChanged := True;
FFontChanged := True;
FCurrentPos := Point(0, 0);
ScreenDC := GetDC(0);
try
FBitmap := CreateCompatibleBitmap(ScreenDC, AWidth, AHeight);
finally
ReleaseDC(0, ScreenDC);
end;
if FBitmap = 0 then
raise Exception.Create('CreateCompatibleBitmap failed');
InitFromBitmap(FBitmap, True);
CheckStatus(GdipCreateSolidFill(MakeColor(255, 0, 0, 0), FGpBrush));
CheckStatus(GdipCreatePen1(MakeColor(255, 0, 0, 0), 1.0, UnitPixel, FGpPen));
CheckStatus(GdipCreateFontFamilyFromName('Arial', nil, FFontFamily));
CheckStatus(GdipCreatePath(FillModeAlternate, FPath));
CheckStatus(GdipSetSmoothingMode(FGpGraphics, SmoothingModeAntiAlias));
Windows.InitializeCriticalSection(FLock);
end;
destructor TGDIPlusCanvas.Destroy;
begin
if Assigned(FGpGraphics) then GdipDeleteGraphics(FGpGraphics);
if Assigned(FGpPen) then GdipDeletePen(FGpPen);
if Assigned(FGpBrush) then GdipDeleteBrush(FGpBrush);
if Assigned(FGpFont) then GdipDeleteFont(FGpFont);
if Assigned(FFontFamily) then GdipDeleteFontFamily(FFontFamily);
if Assigned(FImage) then GdipDisposeImage(FImage);
if Assigned(FGpClipRegion) then GdipDeleteRegion(FGpClipRegion);
if Assigned(FPath) then GdipDeletePath(FPath);
if (FMemDC <> 0) and (FBitmap <> 0) then
begin
DeleteDC(FMemDC);
FMemDC := 0;
end;
if FOwnsBitmap and (FBitmap <> 0) then
begin
DeleteObject(FBitmap);
FBitmap := 0;
end;
Windows.DeleteCriticalSection(FLock);
inherited Destroy;
end;
procedure TGDIPlusCanvas.PenChanging(APen: TObject);
begin
if Assigned(FOnChanging) then
FOnChanging(Self);
end;
procedure TGDIPlusCanvas.FontChanging(AFont: TObject);
begin
if Assigned(FOnChanging) then
FOnChanging(Self);
end;
procedure TGDIPlusCanvas.BrushChanging(ABrush: TObject);
begin
if Assigned(FOnChanging) then
FOnChanging(Self);
end;
procedure TGDIPlusCanvas.RegionChanging(ARegion: TObject);
begin
if Assigned(FOnChanging) then
FOnChanging(Self);
end;
procedure TGDIPlusCanvas.BrushChanged(ABrush: TObject);
begin
FBrushChanged := True;
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TGDIPlusCanvas.FontChanged(AFont: TObject);
begin
FFontChanged := True;
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TGDIPlusCanvas.PenChanged(APen: TObject);
begin
FPenChanged := True;
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TGDIPlusCanvas.RegionChanged(ARegion: TObject);
begin
SetRegion(FRegion);
if Assigned(FOnChange) then
FOnChange(Self);
end;
function TGDIPlusCanvas.FPColorToARGB(const Color: TFPColor): ARGB;
begin
Result := (Color.Alpha shl 24) or (Color.Red shl 16) or
(Color.Green shl 8) or Color.Blue;
end;
function TGDIPlusCanvas.GetHandle: HDC;
{var
dc: HDC;
begin
if FGpGraphics <> nil then
begin
CheckStatus(GdipGetDC(FGpGraphics, dc));
Result := dc;
end
else
Result := 0;}
begin
Result := FMemDC;
end;
function TGDIPlusCanvas.GetPixel(X, Y: integer): TColor;
var
color: ARGB;
r, g, b: byte;
dc: HDC;
begin
if FImage <> nil then
begin
CheckStatus(GdipBitmapGetPixel(FImage, X, Y, color));
end
else if FGpGraphics <> nil then
begin
dc := GetHandle;
try
Result := Windows.GetPixel(dc, X, Y);
if Result = CLR_INVALID then
Result := clBlack;
Exit;
finally
//GdipReleaseDC(FGpGraphics, dc);
end;
end
else
begin
Result := clBlack;
Exit;
end;
r := (color shr 16) and $FF;
g := (color shr 8) and $FF;
b := color and $FF;
Result := RGB(r, g, b);
end;
procedure TGDIPlusCanvas.SetAutoRedraw(AValue: boolean);
begin
if FAutoRedraw = AValue then Exit;
FAutoRedraw := AValue;
RealizeAutoRedraw;
end;
procedure TGDIPlusCanvas.SetHandle(AValue: HDC);
begin
// Если канвас оффскрин (есть FImage/FBitmap/FMemDC),
// то Handle используется как target для AutoRedraw/present.
if (FImage <> nil) and (FMemDC <> 0) and (FBitmap <> 0) then
begin
FPresentDC := AValue;
FPresentX := 0;
FPresentY := 0;
Exit;
end;
// Иначе (мы были созданы от DC) — действительно меняем GpGraphics
if FGpGraphics <> nil then
begin
GdipDeleteGraphics(FGpGraphics);
FGpGraphics := nil;
end;
FMemDC := AValue;
CheckStatus(GdipCreateFromHDC(AValue, FGpGraphics));
ApplyClipping;
end;
procedure TGDIPlusCanvas.SetLazBrush(AValue: TBrush);
begin
if FLazBrush = AValue then Exit;
FLazBrush.Assign(AValue);
UpdateBrush;
end;
procedure TGDIPlusCanvas.SetLazFont(AValue: TFont);
begin
if FLazFont = AValue then Exit;
FLazFont.Assign(AValue);
UpdateFont;
end;
procedure TGDIPlusCanvas.SetLazPen(AValue: TPen);
begin
if FLazPen = AValue then Exit;
FLazPen.Assign(AValue);
UpdatePen;
end;
procedure TGDIPlusCanvas.SetPixel(X, Y: integer; AValue: TColor);
var
AColor: ARGB;
r, g, b: byte;
dc: HDC;
begin
r := GetRValue(AValue);
g := GetGValue(AValue);
b := GetBValue(AValue);
if FImage <> nil then
begin
AColor := MakeColor(255, r, g, b);
CheckStatus(GdipBitmapSetPixel(FImage, X, Y, AColor));
end
else if FGpGraphics <> nil then
begin
dc := GetHandle;
try
Windows.SetPixel(dc, X, Y, AValue);
finally
//GdipReleaseDC(FGpGraphics, dc);
end;
end;
end;
procedure TGDIPlusCanvas.SetRegion(AValue: TRegion);
begin
if FRegion = AValue then Exit;
FRegion := AValue;
if FRegion <> nil then
begin
if FGpClipRegion <> nil then
GdipDeleteRegion(FGpClipRegion);
CheckStatus(GdipCreateRegionHrgn(FRegion.Handle, FGpClipRegion));
end
else
begin
if FGpClipRegion <> nil then
begin
GdipDeleteRegion(FGpClipRegion);
FGpClipRegion := nil;
end;
end;
ApplyClipping;
end;
procedure TGDIPlusCanvas.CheckStatus(Status: TStatus);
begin
if Status <> Ok then
raise Exception.Create('GDI+ error: ' + IntToStr(integer(Status)));
end;
// Resource update methods
procedure TGDIPlusCanvas.UpdateBrush;
var
color, foreColor, backColor: ARGB;
patternBitmap: GpBitmap;
textureBrush: GpTexture;
hatchStyle: THatchStyle;
begin
if not FBrushChanged then Exit;
if FGpBrush <> nil then
GdipDeleteBrush(FGpBrush);
case FLazBrush.Style of
bsSolid:
begin
color := MakeColor(255, GetRValue(FLazBrush.Color),
GetGValue(FLazBrush.Color), GetBValue(FLazBrush.Color));
CheckStatus(GdipCreateSolidFill(color, FGpBrush));
end;
bsClear:
begin
color := MakeColor(0, 0, 0, 0);
CheckStatus(GdipCreateSolidFill(color, FGpBrush));
end;
bsCross, bsDiagCross, bsFDiagonal, bsBDiagonal, bsHorizontal, bsVertical:
begin
// Convert TBrush style to GDI+ HatchStyle
case FLazBrush.Style of
bsCross: hatchStyle := HatchStyleCross;
bsDiagCross: hatchStyle := HatchStyleDiagonalCross;
bsFDiagonal: hatchStyle := HatchStyleForwardDiagonal;
bsBDiagonal: hatchStyle := HatchStyleBackwardDiagonal;
bsHorizontal: hatchStyle := HatchStyleHorizontal;
bsVertical: hatchStyle := HatchStyleVertical;
else
hatchStyle := HatchStyleHorizontal;
end;
foreColor := MakeColor(255, GetRValue(FLazBrush.Color),
GetGValue(FLazBrush.Color), GetBValue(FLazBrush.Color));
backColor := MakeColor(255, 255, 255, 255);
CheckStatus(GdipCreateHatchBrush(integer(hatchStyle), foreColor,
backColor, FGpBrush));
end;
bsPattern, bsImage:
begin
if FLazBrush.Bitmap <> nil then
begin
// Create bitmap from TBitmap
CheckStatus(GdipCreateBitmapFromHBITMAP(FLazBrush.Bitmap.Handle, 0,
patternBitmap));
// Create texture brush
CheckStatus(GdipCreateTexture(patternBitmap, WrapModeTile, textureBrush));
FGpBrush := textureBrush;
GdipDisposeImage(patternBitmap);
end
else
begin
// Default to solid brush if bitmap is nil
color := MakeColor(255, GetRValue(FLazBrush.Color),
GetGValue(FLazBrush.Color), GetBValue(FLazBrush.Color));
CheckStatus(GdipCreateSolidFill(color, FGpBrush));
end;
end;
else
begin
// Default for unsupported styles
color := MakeColor(255, GetRValue(FLazBrush.Color),
GetGValue(FLazBrush.Color), GetBValue(FLazBrush.Color));
CheckStatus(GdipCreateSolidFill(color, FGpBrush));
end;
end;
FBrushChanged := False;
end;
procedure TGDIPlusCanvas.UpdatePen;
var
color: ARGB;
dashStyle: TDashStyle;
dashArray: array[0..3] of single;
dashOffset: single;
begin
if not FPenChanged then Exit;
if FGpPen <> nil then
GdipDeletePen(FGpPen);
color := MakeColor(255, GetRValue(FLazPen.Color), GetGValue(FLazPen.Color),
GetBValue(FLazPen.Color));
CheckStatus(GdipCreatePen1(color, FLazPen.Width, UnitPixel, FGpPen));
// Set pen style
case FLazPen.Style of
psSolid: dashStyle := DashStyleSolid;
psDash: dashStyle := DashStyleDash;
psDot: dashStyle := DashStyleDot;
psDashDot: dashStyle := DashStyleDashDot;
psDashDotDot: dashStyle := DashStyleDashDotDot;
psInsideFrame:
begin
dashStyle := DashStyleSolid;
CheckStatus(GdipSetPenMode(FGpPen, PenAlignmentInset));
end;
psPattern:
begin
dashStyle := DashStyleCustom;
// Custom dash pattern for psPattern
dashArray[0] := 3.0;
dashArray[1] := 1.0;
dashArray[2] := 1.0;
dashArray[3] := 1.0;
dashOffset := 0.0;
CheckStatus(GdipSetPenDashArray(FGpPen, @dashArray[0], 4));
CheckStatus(GdipSetPenDashOffset(FGpPen, dashOffset));
end;
else
dashStyle := DashStyleSolid;
end;
if FLazPen.Style <> psPattern then
CheckStatus(GdipSetPenDashStyle(FGpPen, dashStyle));
// Set join and end cap styles
case FLazPen.JoinStyle of
pjsRound: CheckStatus(GdipSetPenLineJoin(FGpPen, LineJoinRound));
pjsBevel: CheckStatus(GdipSetPenLineJoin(FGpPen, LineJoinBevel));
pjsMiter: CheckStatus(GdipSetPenLineJoin(FGpPen, LineJoinMiter));
end;
case FLazPen.EndCap of
pecRound: CheckStatus(GdipSetPenEndCap(FGpPen, LineCapRound));
pecSquare: CheckStatus(GdipSetPenEndCap(FGpPen, LineCapSquare));
pecFlat: CheckStatus(GdipSetPenEndCap(FGpPen, LineCapFlat));
end;
FPenChanged := False;
end;
procedure TGDIPlusCanvas.UpdateFont;
var
style, fontSize: integer;
fontName: UnicodeString;
pFontFamily: GpFontFamily;
begin
style := FontStyleRegular;
if fsBold in FLazFont.Style then style := style or FontStyleBold;
if fsItalic in FLazFont.Style then style := style or FontStyleItalic;
if fsUnderline in FLazFont.Style then style := style or FontStyleUnderline;
if fsStrikeOut in FLazFont.Style then style := style or FontStyleStrikeout;
if not FLazFont.IsDefault then
begin
fontName := UnicodeString(FLazFont.Name);
fontSize := FLazFont.Size;
end
else
begin
fontName := 'Arial';
if FLazFont.Size <= 0 then fontSize := 10 else fontSize := FLazFont.Size;
end;
if (not FFontChanged) and
(FGpFont <> nil) and
(fontName = FLastFontName) and
(fontSize = FLastFontSize) and
(style = FLastFontStyle) then
Exit;
if (FGpFont <> nil) and
(fontName = FLastFontName) and
(fontSize = FLastFontSize) and
(style = FLastFontStyle) then
begin
FFontChanged := False;
Exit;
end;
if FGpFont <> nil then
begin
GdipDeleteFont(FGpFont);
FGpFont := nil;
end;
if FFontFamily <> nil then
begin
GdipDeleteFontFamily(FFontFamily);
FFontFamily := nil;
end;
if GdipCreateFontFamilyFromName(PWideChar(WideString(fontName)), nil, pFontFamily) <> Ok then
begin
fontName := 'Arial';
CheckStatus(GdipCreateFontFamilyFromName(PWideChar(WideString(fontName)), nil, pFontFamily));
end;
FFontFamily := pFontFamily;
CheckStatus(GdipCreateFont(FFontFamily, Single(fontSize), style, integer(UnitPoint), FGpFont));
// обновляем кэш-ключ
FLastFontName := fontName;
FLastFontSize := fontSize;
FLastFontStyle := style;
// сброс
FFontChanged := False;
// сброс кэша измерений текста — иначе при смене font “старые размеры”
FLastMeasuredText := '';
FLastTextSize.cx := 0;
FLastTextSize.cy := 0;
end;
// Drawing primitives implementation
procedure TGDIPlusCanvas.DoLine(x1, y1, x2, y2: integer);
begin
UpdatePen;
CheckStatus(GdipDrawLineI(FGpGraphics, FGpPen, x1, y1, x2, y2));
end;
procedure TGDIPlusCanvas.DoCopyRect(x, y: integer; canvas: TFPCustomCanvas;
const SourceRect: TRect);
var
srcDC, dstDC: HDC;
w, h: integer;
begin
w := SourceRect.Right - SourceRect.Left;
h := SourceRect.Bottom - SourceRect.Top;
if (w <= 0) or (h <= 0) then Exit;
// source
if canvas is TGDIPlusCanvas then
srcDC := TGDIPlusCanvas(canvas).Handle
else if canvas is TCanvas then
srcDC := TCanvas(canvas).Handle
else
raise Exception.Create('Unsupported canvas type for CopyRect');
// destination
dstDC := Handle;
BitBlt(dstDC, x, y, w, h, srcDC, SourceRect.Left, SourceRect.Top, SRCCOPY);
if canvas is TGDIPlusCanvas then
GdipReleaseDC(TGDIPlusCanvas(canvas).FGpGraphics, srcDC);
GdipReleaseDC(FGpGraphics, dstDC);
end;
procedure TGDIPlusCanvas.MoveTo(X, Y: integer);
begin
FCurrentPos := Point(X, Y);
end;
procedure TGDIPlusCanvas.LineTo(X, Y: integer);
begin
DoLine(FCurrentPos.X, FCurrentPos.Y, X, Y);
FCurrentPos := Point(X, Y);
end;
procedure TGDIPlusCanvas.ArcTo(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: integer);
begin
Arc(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY);
FCurrentPos := Point(EX, EY);
end;
procedure TGDIPlusCanvas.DrawToCanvas(DestCanvas: TCanvas; X, Y: integer);
var
bmpData: TBitmapData;
bmpInfo: BITMAP;
destBits: PByte;
srcBits: PByte;
row: integer;
rect: TGPRect;
begin
if (DestCanvas = nil) or (FImage = nil) or (FBitmap = 0) then Exit;
// Синхронизируем GpBitmap → HBITMAP
rect.X := 0;
rect.Y := 0;
rect.Width := Width;
rect.Height := Height;
if GdipBitmapLockBits(FImage, @rect, ImageLockModeRead, PixelFormat32bppARGB, @bmpData) = Ok then
begin
try
GetObject(FBitmap, SizeOf(bmpInfo), @bmpInfo);
destBits := bmpInfo.bmBits;
if destBits <> nil then
begin
srcBits := bmpData.Scan0;
for row := 0 to Height - 1 do
begin
Move(srcBits^, destBits^, Width * 4);
Inc(srcBits, bmpData.Stride);
Inc(destBits, bmpInfo.bmWidthBytes);
end;
end;
finally
GdipBitmapUnlockBits(FImage, @bmpData);
end;
end;
BitBlt(DestCanvas.Handle, X, Y, Width, Height, FMemDC, 0, 0, SRCCOPY);
end;
procedure TGDIPlusCanvas.DoPolyline(const points: array of TPoint);
var
gdiPoints: array of TGPPoint;
i: integer;
begin
SetLength(gdiPoints, Length(points));
for i := 0 to High(points) do
gdiPoints[i] := MakePoint(points[i].X, points[i].Y);
UpdatePen;
CheckStatus(GdipDrawLinesI(FGpGraphics, FGpPen, @gdiPoints[0], Length(points)));
end;
procedure TGDIPlusCanvas.DoPolygon(const points: array of TPoint);
var
gdiPoints: array of TGPPoint;
i: integer;
begin
SetLength(gdiPoints, Length(points));
for i := 0 to High(points) do
gdiPoints[i] := MakePoint(points[i].X, points[i].Y);
UpdatePen;
CheckStatus(GdipDrawPolygonI(FGpGraphics, FGpPen, @gdiPoints[0], Length(points)));
end;
procedure TGDIPlusCanvas.DoPolygonFill(const points: array of TPoint);
var
gdiPoints: array of TGPPoint;
i: integer;
begin
SetLength(gdiPoints, Length(points));
for i := 0 to High(points) do
gdiPoints[i] := MakePoint(points[i].X, points[i].Y);
UpdateBrush;
CheckStatus(GdipFillPolygonI(FGpGraphics, FGpBrush, @gdiPoints[0],
Length(points), FillModeAlternate));
end;
procedure TGDIPlusCanvas.DoFloodFill(x, y: integer);
var
ARegion: GpRegion;
ABrush: GpBrush;
begin
GdipCreateRegion(ARegion);
try
GdipCreateSolidFill(FPColorToARGB(Brush.FPColor), ABrush);
try
GdipSetInfinite(ARegion);
CheckStatus(GdipFillRegion(FGpGraphics, ABrush, ARegion));
finally
GdipDeleteBrush(ABrush);
end;
finally
GdipDeleteRegion(ARegion);
end;
end;
// Image operations
function TGDIPlusCanvas.CreateGDIPBitmap(Image: TFPCustomImage): GpBitmap;
var
x, y: integer;
Data: array of ARGB;
scan0: Pointer;
begin
SetLength(Data, Image.Width * Image.Height);
for y := 0 to Image.Height - 1 do
for x := 0 to Image.Width - 1 do
Data[y * Image.Width + x] := FPColorToARGB(Image.Colors[x, y]);
GdipCreateBitmapFromScan0(Image.Width, Image.Height,
Image.Width * 4, PixelFormat32bppARGB, @Data[0], Result);
end;
procedure TGDIPlusCanvas.DoDraw(x, y: integer; const image: TFPCustomImage);
var
gdiBitmap: GpBitmap;
begin
gdiBitmap := CreateGDIPBitmap(image);
try
CheckStatus(GdipDrawImageI(FGpGraphics, gdiBitmap, x, y));
finally
GdipDisposeImage(gdiBitmap);
end;
end;
procedure TGDIPlusCanvas.StretchDraw(x, y, w, h: integer; Source: TFPCustomImage);
var
gdiBitmap: GpBitmap;
destRect: TGPRect;
begin
gdiBitmap := CreateGDIPBitmap(Source);
try
destRect := MakeRect(x, y, w, h);
CheckStatus(GdipDrawImageRectI(FGpGraphics, gdiBitmap, destRect.X,
destRect.Y, destRect.Width, destRect.Height));
finally
GdipDisposeImage(gdiBitmap);
end;
end;
// Advanced shapes
procedure TGDIPlusCanvas.DoRadialPie(x1, y1, x2, y2, StartAngle16Deg,
Angle16DegLength: integer);
var
startAngle, sweepAngle: single;
begin
startAngle := StartAngle16Deg * 16.0; // Convert 16-degree units to degrees
sweepAngle := Angle16DegLength * 16.0;
UpdateBrush;
CheckStatus(GdipFillPieI(FGpGraphics, FGpBrush, x1, y1, x2 - x1,
y2 - y1, startAngle, sweepAngle));
end;
procedure TGDIPlusCanvas.DoPolyBezier(Points: PPoint; NumPts: integer;
Filled, Continuous: boolean);
var
gdiPoints: array of TGPPoint;
i: integer;
begin
SetLength(gdiPoints, NumPts);
for i := 0 to NumPts - 1 do
gdiPoints[i] := MakePoint(Points[i].X, Points[i].Y);
if Filled then
begin
GdipResetPath(FPath);
CheckStatus(GdipAddPathBeziers(FPath, @gdiPoints[0], NumPts));
UpdateBrush;
CheckStatus(GdipFillPath(FGpGraphics, FGpBrush, FPath));
end
else
begin
UpdatePen;
CheckStatus(GdipDrawBeziersI(FGpGraphics, FGpPen, @gdiPoints[0], NumPts));
end;
end;
// Clipping and region management
procedure TGDIPlusCanvas.ApplyClipping;
begin
if FGpGraphics = nil then Exit;
if Clipping and (FGpClipRegion <> nil) then
CheckStatus(GdipSetClipRegion(FGpGraphics, FGpClipRegion, CombineModeReplace))
else
CheckStatus(GdipResetClip(FGpGraphics));
end;
procedure TGDIPlusCanvas.ApplyDrawingMode;
begin
if FGpGraphics = nil then Exit;
case FCopyMode of
cmSrcCopy:
CheckStatus(GdipSetCompositingMode(FGpGraphics, CompositingModeSourceCopy));
else
// Для остальных CopyMode точного соответствия в GDI+ нет,
CheckStatus(GdipSetCompositingMode(FGpGraphics, CompositingModeSourceOver));
end;
end;
procedure TGDIPlusCanvas.ApplyInterpolation;
begin
if FGpGraphics = nil then Exit;
CheckStatus(GdipSetInterpolationMode(FGpGraphics, InterpolationModeHighQualityBicubic));
CheckStatus(GdipSetPixelOffsetMode(FGpGraphics, PixelOffsetModeHighQuality));
end;
function TGDIPlusCanvas.GetClipRect: TRect;
var
r: TGPRect;
begin
r := MakeRect(0, 0, 0, 0);
CheckStatus(GdipGetClipBoundsI(FGpGraphics, @r));
Result := Classes.Rect(r.X, r.Y, r.X + r.Width, r.Y + r.Height);
end;
procedure TGDIPlusCanvas.SetClipRect(const AValue: TRect);
var
ARegion: GpRegion;
ARect: TGPRect;
begin
ARect := MakeRect(AValue.Left, AValue.Top,
AValue.Right - AValue.Left, AValue.Bottom - AValue.Top);
CheckStatus(GdipCreateRegionRectI(@ARect, ARegion));
try
CheckStatus(GdipSetClipRegion(FGpGraphics, ARegion, CombineModeReplace));
finally
GdipDeleteRegion(ARegion);
end;
end;
function TGDIPlusCanvas.GetClipping: boolean;
begin
Result := inherited GetClipping;
end;
procedure TGDIPlusCanvas.SetClipping(const AValue: boolean);
begin
inherited SetClipping(AValue);
ApplyClipping;
end;
// Additional required methods
procedure TGDIPlusCanvas.Erase;
var
color: ARGB;
begin
color := FPColorToARGB(Brush.FPColor);
CheckStatus(GdipGraphicsClear(FGpGraphics, color));
end;
function TGDIPlusCanvas.GetBitmap: HBITMAP;
begin
if FImage <> nil then
begin
GdipCreateHBITMAPFromBitmap(FImage, Result, 0);
end
else
Result := 0;
end;
// Default resource creation
function TGDIPlusCanvas.DoCreateDefaultFont: TFPCustomFont;
begin
Result := TFont.Create;
Result.Name := 'Arial';
Result.Size := 12;
end;
function TGDIPlusCanvas.DoCreateDefaultPen: TFPCustomPen;
begin
Result := TPen.Create;
end;
function TGDIPlusCanvas.DoCreateDefaultBrush: TFPCustomBrush;
begin
Result := TBrush.Create;
end;
// Property accessors
procedure TGDIPlusCanvas.SetColor(x, y: integer; const Value: TFPColor);
var
r, g, b: byte;
col: TColor;
begin
r := Value.Red shr 8;
g := Value.Green shr 8;
b := Value.Blue shr 8;
col := RGB(r, g, b);
SetPixel(x, y, col);
end;
function TGDIPlusCanvas.GetColor(x, y: integer): TFPColor;
var
color: TColor;
r, g, b: byte;
begin
color := GetPixel(x, y);
r := GetRValue(color);
g := GetGValue(color);
b := GetBValue(color);
Result.Red := r shl 8;
Result.Green := g shl 8;
Result.Blue := b shl 8;
Result.Alpha := $FFFF;
end;
// Size management
procedure TGDIPlusCanvas.SetHeight(AValue: integer);
begin
raise Exception.Create('SetHeight not supported for TGDIPlusCanvas');
end;
function TGDIPlusCanvas.GetHeight: integer;
var
w, h: UINT;
p: TPoint;
begin
if FImage <> nil then
begin
CheckStatus(GdipGetImageHeight(FImage, h));
Result := h;
end
else
begin
lclintf.GetDeviceSize(GetHandle, p);
Result := p.y;
end;
end;
procedure TGDIPlusCanvas.SetWidth(AValue: integer);
begin
raise Exception.Create('SetWidth not supported for TGDIPlusCanvas');
end;
function TGDIPlusCanvas.GetWidth: integer;
var
w, h: UINT;
p: TPoint;
begin
if FImage <> nil then
begin
CheckStatus(GdipGetImageWidth(FImage, w));
Result := w;
end
else
begin
lclintf.GetDeviceSize(GetHandle, p);
Result := p.x;
end;
end;
procedure TGDIPlusCanvas.DoTextOut(x, y: integer; Text: ansistring);
var
abrush: GpSolidFill;
strFormat: GpStringFormat;
rect: TGPRectF;
wideText: widestring;
color: ARGB;
begin
UpdateFont;
wideText := WideString(Text);
color := MakeColor(255, GetRValue(FLazFont.Color), GetGValue(FLazFont.Color),
GetBValue(FLazFont.Color));
CheckStatus(GdipCreateSolidFill(color, abrush));
CheckStatus(GdipCreateStringFormat(0, 0, strFormat));
rect.X := x;
rect.Y := y;
rect.Width := 10000;
rect.Height := 10000;
try
if FTextStyle.Alignment <> taLeftJustify then
begin
case FTextStyle.Alignment of
taRightJustify: CheckStatus(GdipSetStringFormatAlign(strFormat,
StringAlignmentFar));
taCenter: CheckStatus(GdipSetStringFormatAlign(strFormat,
StringAlignmentCenter));
end;
end;
CheckStatus(GdipDrawString(FGpGraphics, pwidechar(wideText),
Length(wideText), FGpFont, @rect, strFormat, abrush));
finally
GdipDeleteStringFormat(strFormat);
GdipDeleteBrush(abrush);
end;
end;
procedure TGDIPlusCanvas.DoGetTextSize(Text: ansistring; var w, h: integer);
var
strFormat: GpStringFormat;
rectF: TGPRectF;
boundingBox: TGPRectF;
wideText: widestring;
begin
if Text = '' then
begin
w := 0;
h := 0;
Exit;
end;
if Text = FLastMeasuredText then
begin
w := FLastTextSize.cx;
h := FLastTextSize.cy;
Exit;
end;
UpdateFont;
wideText := WideString(Text);
CheckStatus(GdipCreateStringFormat(0, 0, strFormat));
rectF := MakeRect(0.0, 0.0, 10000.0, 10000.0);
CheckStatus(GdipMeasureString(FGpGraphics, pwidechar(wideText), Length(wideText),
FGpFont, @rectF, strFormat, @boundingBox, nil, nil));
w := Ceil(boundingBox.Width);
h := Ceil(boundingBox.Height);
GdipDeleteStringFormat(strFormat);
FLastMeasuredText := Text;
FLastTextSize.cx := w;
FLastTextSize.cy := h;
end;
function TGDIPlusCanvas.DoGetTextHeight(Text: ansistring): integer;
var
w, h: integer;
begin
DoGetTextSize(Text, w, h);
Result := h;
end;
function TGDIPlusCanvas.DoGetTextWidth(Text: ansistring): integer;
var
w, h: integer;
begin
DoGetTextSize(Text, w, h);
Result := w;
end;
procedure TGDIPlusCanvas.DoRectangle(const Bounds: TRect);
begin
UpdatePen;
UpdateBrush;
CheckStatus(GdipDrawRectangleI(FGpGraphics, FGpPen, Bounds.Left,
Bounds.Top, Bounds.Right - Bounds.Left, Bounds.Bottom - Bounds.Top));
end;
procedure TGDIPlusCanvas.DoRectangleFill(const Bounds: TRect);
begin
UpdatePen;
UpdateBrush;
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, Bounds.Left,
Bounds.Top, Bounds.Right - Bounds.Left, Bounds.Bottom - Bounds.Top));
end;
procedure TGDIPlusCanvas.DoEllipse(const Bounds: TRect);
begin
UpdatePen;
CheckStatus(GdipDrawEllipseI(FGpGraphics, FGpPen, Bounds.Left,
Bounds.Top, Bounds.Right - Bounds.Left, Bounds.Bottom - Bounds.Top));
end;
procedure TGDIPlusCanvas.DoEllipseFill(const Bounds: TRect);
begin
UpdateBrush;
CheckStatus(GdipFillEllipseI(FGpGraphics, FGpBrush, Bounds.Left,
Bounds.Top, Bounds.Right - Bounds.Left, Bounds.Bottom - Bounds.Top));
end;
procedure TGDIPlusCanvas.Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: integer);
var
startAngle, sweepAngle: single;
begin
startAngle := Angle16Deg * 16.0; // Convert 16-degree units to degrees
sweepAngle := Angle16DegLength * 16.0;
UpdatePen;
CheckStatus(GdipDrawArcI(FGpGraphics, FGpPen, ALeft, ATop, ARight -
ALeft, ABottom - ATop, startAngle, sweepAngle));
end;
procedure TGDIPlusCanvas.Arc(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: integer);
var
startAngle, sweepAngle: single;
centerX, centerY: integer;
startRad, endRad: double;
begin
centerX := (ALeft + ARight) div 2;
centerY := (ATop + ABottom) div 2;
// Calculate angles from center
startRad := ArcTan2(SY - centerY, SX - centerX);
endRad := ArcTan2(EY - centerY, EX - centerX);
// Convert to degrees
startAngle := startRad * 180 / PI;
sweepAngle := ((endRad - startRad) * 180 / PI);
// Ensure sweep is positive
if sweepAngle <= 0 then
sweepAngle := sweepAngle + 360;
UpdatePen;
CheckStatus(GdipDrawArcI(FGpGraphics, FGpPen, ALeft, ATop, ARight -
ALeft, ABottom - ATop, startAngle, sweepAngle));
end;
procedure TGDIPlusCanvas.AngleArc(X, Y: integer; Radius: longword;
StartAngle, SweepAngle: single);
begin
UpdatePen;
CheckStatus(GdipDrawArcI(FGpGraphics, FGpPen, X - Radius, Y -
Radius, Radius * 2, Radius * 2, StartAngle, SweepAngle));
end;
// Chord methods
procedure TGDIPlusCanvas.Chord(x1, y1, x2, y2, Angle16Deg, Angle16DegLength: integer);
var
startAngle, sweepAngle: single;
begin
startAngle := Angle16Deg * 16.0; // Convert 16-degree units to degrees
sweepAngle := Angle16DegLength * 16.0;
UpdatePen();
CheckStatus(GdipDrawPieI(FGpGraphics, FGpPen, x1, y1, x2 - x1, y2 -
y1, startAngle, sweepAngle));
end;
procedure TGDIPlusCanvas.Chord(x1, y1, x2, y2, SX, SY, EX, EY: integer);
var
startAngle, sweepAngle: single;
centerX, centerY: integer;
startRad, endRad: double;
begin
centerX := (x1 + x2) div 2;
centerY := (y1 + y2) div 2;
// Calculate angles from center
startRad := ArcTan2(SY - centerY, SX - centerX);
endRad := ArcTan2(EY - centerY, EX - centerX);
// Convert to degrees
startAngle := startRad * 180 / PI;
sweepAngle := ((endRad - startRad) * 180 / PI);
// Ensure sweep is positive
if sweepAngle <= 0 then
sweepAngle := sweepAngle + 360;
UpdatePen();
UpdateBrush();
CheckStatus(GdipDrawPieI(FGpGraphics, FGpPen, x1, y1, x2 - x1, y2 -
y1, startAngle, sweepAngle));
end;
// BrushCopy method
procedure TGDIPlusCanvas.BrushCopy(ADestRect: TRect; ABitmap: Graphics.TBitmap;
ASourceRect: TRect; ATransparentColor: TColor);
var
gdiBitmap: GpBitmap;
destRect, srcRect: TGPRect;
imageAttr: GpImageAttributes;
colorMatrix: TColorMatrix;
r, g, b: byte;
begin
CheckStatus(GdipCreateBitmapFromHBITMAP(ABitmap.Handle, 0, gdiBitmap));
try
// Set up source and destination rectangles
srcRect := MakeRect(ASourceRect.Left, ASourceRect.Top, ASourceRect.Right -
ASourceRect.Left, ASourceRect.Bottom - ASourceRect.Top);
destRect := MakeRect(ADestRect.Left, ADestRect.Top, ADestRect.Right -
ADestRect.Left, ADestRect.Bottom - ADestRect.Top);
// Set up image attributes for transparency
CheckStatus(GdipCreateImageAttributes(imageAttr));
try
// Set transparent color
r := GetRValue(ATransparentColor);
g := GetGValue(ATransparentColor);
b := GetBValue(ATransparentColor);
CheckStatus(GdipSetImageAttributesColorKeys(imageAttr,
ColorAdjustTypeBitmap, True, MakeColor(255, r, g, b),
MakeColor(255, r, g, b)));
// Draw bitmap with transparency
CheckStatus(GdipDrawImageRectRectI(FGpGraphics, gdiBitmap,
destRect.X, destRect.Y, destRect.Width, destRect.Height,
srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, UnitPixel,
imageAttr, nil, nil));
finally
GdipDisposeImageAttributes(imageAttr);
end;
finally
GdipDisposeImage(gdiBitmap);
end;
end;
// CopyRect method (different from DoCopyRect)
procedure TGDIPlusCanvas.CopyRect(const Dest: TRect; SrcCanvas: TCanvas; const Source: TRect);
var
srcDC: HDC;
w, h, destWidth, destHeight: integer;
Bitmap: GpBitmap;
begin
// Get dimensions
w := Source.Right - Source.Left;
h := Source.Bottom - Source.Top;
destWidth := Dest.Right - Dest.Left;
destHeight := Dest.Bottom - Dest.Top;
// Get source DC
srcDC := SrcCanvas.Handle;
// Create a bitmap from the source DC
CheckStatus(GdipCreateBitmapFromHBITMAP(srcDC, 0, Bitmap));
try
// Draw the bitmap with stretching if necessary
CheckStatus(GdipDrawImageRectRectI(FGpGraphics, Bitmap, Dest.Left,
Dest.Top, destWidth, destHeight, Source.Left, Source.Top, w,
h, UnitPixel, nil, nil, nil));
finally
GdipDisposeImage(Bitmap);
end;
end;
// Draw method
procedure TGDIPlusCanvas.Draw(X, Y: integer; SrcGraphic: TGraphic);
var
Bitmap: GpBitmap;
tempBitmap: Graphics.TBitmap;
begin
if SrcGraphic is Graphics.TBitmap then
begin
CheckStatus(GdipCreateBitmapFromHBITMAP(Graphics.TBitmap(SrcGraphic).Handle,
0, Bitmap));
try
CheckStatus(GdipDrawImageI(FGpGraphics, Bitmap, X, Y));
finally
GdipDisposeImage(Bitmap);
end;
end
else
begin
tempBitmap := Graphics.TBitmap.Create;
try
tempBitmap.Width := SrcGraphic.Width;
tempBitmap.Height := SrcGraphic.Height;
tempBitmap.Canvas.Draw(0, 0, SrcGraphic);
CheckStatus(GdipCreateBitmapFromHBITMAP(tempBitmap.Handle, 0, Bitmap));
try
CheckStatus(GdipDrawImageI(FGpGraphics, Bitmap, X, Y));
finally
GdipDisposeImage(Bitmap);
end;
finally
tempBitmap.Free;
end;
end;
end;
// DrawFocusRect method
procedure TGDIPlusCanvas.DrawFocusRect(const ARect: TRect);
var
oldPen: GpPen;
dashArray: array[0..1] of single;
begin
oldPen := FGpPen;
CheckStatus(GdipCreatePen1(MakeColor(255, 0, 0, 0), 1, UnitPixel, FGpPen));
dashArray[0] := 1.0;
dashArray[1] := 1.0;
CheckStatus(GdipSetPenDashArray(FGpPen, @dashArray[0], 2));
CheckStatus(GdipDrawRectangleI(FGpGraphics, FGpPen, ARect.Left,
ARect.Top, ARect.Right - ARect.Left, ARect.Bottom - ARect.Top));
GdipDeletePen(FGpPen);
FGpPen := oldPen;
end;
// StretchDraw with TRect parameter
procedure TGDIPlusCanvas.StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic);
var
Bitmap: GpBitmap;
tempBitmap: TBitmap;
w, h: integer;
begin
w := DestRect.Right - DestRect.Left;
h := DestRect.Bottom - DestRect.Top;
if SrcGraphic is TBitmap then
begin
CheckStatus(GdipCreateBitmapFromHBITMAP(TBitmap(SrcGraphic).Handle, 0, Bitmap));
try
CheckStatus(GdipDrawImageRectI(FGpGraphics, Bitmap, DestRect.Left,
DestRect.Top, w, h));
finally
GdipDisposeImage(Bitmap);
end;
end
else
begin
tempBitmap := TBitmap.Create;
try
tempBitmap.Width := SrcGraphic.Width;
tempBitmap.Height := SrcGraphic.Height;
tempBitmap.Canvas.Draw(0, 0, SrcGraphic);
CheckStatus(GdipCreateBitmapFromHBITMAP(tempBitmap.Handle, 0, Bitmap));
try
CheckStatus(GdipDrawImageRectI(FGpGraphics, Bitmap,
DestRect.Left, DestRect.Top, w, h));
finally
GdipDisposeImage(Bitmap);
end;
finally
tempBitmap.Free;
end;
end;
end;
// Additional rectangle and ellipse methods with TRect parameter
procedure TGDIPlusCanvas.Ellipse(const ARect: TRect);
begin
UpdatePen;
UpdateBrush;
Ellipse(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
end;
procedure TGDIPlusCanvas.Ellipse(x1, y1, x2, y2: integer);
begin
DoEllipse(Rect(x1, y1, x2, y2));
end;
// Fill methods
procedure TGDIPlusCanvas.FillRect(const ARect: TRect);
begin
UpdateBrush;
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, ARect.Left,
ARect.Top, ARect.Right - ARect.Left, ARect.Bottom - ARect.Top));
end;
procedure TGDIPlusCanvas.FillRect(X1, Y1, X2, Y2: integer);
begin
FillRect(Classes.Rect(X1, Y1, X2, Y2));
end;
procedure TGDIPlusCanvas.FloodFill(X, Y: integer; FillColor: TColor;
FillStyle: TFillStyle);
var
ABrush: GpBrush;
begin
CheckStatus(GdipCreateSolidFill(MakeColor(255, GetRValue(FillColor),
GetGValue(FillColor), GetBValue(FillColor)), ABrush));
CheckStatus(GdipFillRectangleI(FGpGraphics, ABrush, X, Y, GetWidth -
X, GetHeight - Y));
GdipDeleteBrush(ABrush);
end;
procedure TGDIPlusCanvas.Frame3d(var ARect: TRect; const FrameWidth: integer;
const Style: TGraphicsBevelCut);
begin
UpdatePen;
CheckStatus(GdipDrawRectangleI(FGpGraphics, FGpPen, ARect.Left,
ARect.Top, ARect.Width, ARect.Height));
InflateRect(ARect, -FrameWidth, -FrameWidth);
end;
procedure TGDIPlusCanvas.Frame3D(var ARect: TRect; TopColor, BottomColor: TColor;
const FrameWidth: integer);
var
TopBrush, BottomBrush: GpBrush;
begin
CheckStatus(GdipCreateSolidFill(MakeColor(255, GetRValue(TopColor),
GetGValue(TopColor), GetBValue(TopColor)), TopBrush));
CheckStatus(GdipCreateSolidFill(MakeColor(255, GetRValue(BottomColor),
GetGValue(BottomColor), GetBValue(BottomColor)), BottomBrush));
CheckStatus(GdipFillRectangleI(FGpGraphics, TopBrush, ARect.Left,
ARect.Top, ARect.Width, FrameWidth));
CheckStatus(GdipFillRectangleI(FGpGraphics, BottomBrush, ARect.Left,
ARect.Bottom - FrameWidth, ARect.Width, FrameWidth));
GdipDeleteBrush(TopBrush);
GdipDeleteBrush(BottomBrush);
InflateRect(ARect, -FrameWidth, -FrameWidth);
end;
procedure TGDIPlusCanvas.Frame(const ARect: TRect);
begin
UpdatePen;
CheckStatus(GdipDrawRectangleI(FGpGraphics, FGpPen, ARect.Left,
ARect.Top, ARect.Width, ARect.Height));
end;
procedure TGDIPlusCanvas.Frame(X1, Y1, X2, Y2: integer);
begin
Frame(Rect(X1, Y1, X2, Y2));
end;
procedure TGDIPlusCanvas.FrameRect(const ARect: TRect);
begin
UpdatePen;
UpdateBrush;
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, ARect.Left,
ARect.Top, ARect.Width, 1));
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, ARect.Left,
ARect.Bottom - 1, ARect.Width, 1));
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, ARect.Left,
ARect.Top, 1, ARect.Height));
CheckStatus(GdipFillRectangleI(FGpGraphics, FGpBrush, ARect.Right - 1,
ARect.Top, 1, ARect.Height));
end;
procedure TGDIPlusCanvas.FrameRect(X1, Y1, X2, Y2: integer);
begin
FrameRect(Rect(X1, Y1, X2, Y2));
end;
function TGDIPlusCanvas.GetTextMetrics(out TM: TLCLTextMetric): boolean;
begin
FillChar(TM, SizeOf(TM), 0);
TM.Height := DoGetTextHeight('Wg');
Result := True;
end;
procedure TGDIPlusCanvas.GradientFill(ARect: TRect; AStart, AStop: TColor;
ADirection: TGradientDirection);
var
LinBrush: GpLineGradient;
StartColor, EndColor: ARGB;
RectF: TGPRectF;
Mode: TLinearGradientMode;
begin
StartColor := MakeColor(255, GetRValue(AStart), GetGValue(AStart), GetBValue(AStart));
EndColor := MakeColor(255, GetRValue(AStop), GetGValue(AStop), GetBValue(AStop));
RectF := MakeRect(single(ARect.Left), single(ARect.Top), single(ARect.Right - ARect.Left),
single(ARect.Bottom - ARect.Top));
if ADirection = gdHorizontal then
Mode := LinearGradientModeHorizontal
else
Mode := LinearGradientModeVertical;
CheckStatus(GdipCreateLineBrushFromRect(@RectF, StartColor, EndColor,
Mode, WrapModeTile, LinBrush));
try
CheckStatus(GdipFillRectangle(FGpGraphics, LinBrush, RectF.X,
RectF.Y, RectF.Width, RectF.Height));
finally
GdipDeleteBrush(LinBrush);
end;
end;
procedure TGDIPlusCanvas.Pie(EllipseX1, EllipseY1, EllipseX2, EllipseY2,
StartX, StartY, EndX, EndY: integer);
var
StartAngle, SweepAngle: single;
CenterX, CenterY: single;
begin
CenterX := (EllipseX1 + EllipseX2) / 2;
CenterY := (EllipseY1 + EllipseY2) / 2;
StartAngle := ArcTan2(StartY - CenterY, StartX - CenterX) * 180 / Pi;
SweepAngle := ArcTan2(EndY - CenterY, EndX - CenterX) * 180 / Pi - StartAngle;
if SweepAngle < 0 then SweepAngle := SweepAngle + 360;
UpdateBrush;
CheckStatus(GdipFillPieI(FGpGraphics, FGpBrush, EllipseX1, EllipseY1,
EllipseX2 - EllipseX1, EllipseY2 - EllipseY1, StartAngle, SweepAngle));
end;
procedure TGDIPlusCanvas.PolyBezier(const Points: array of TPoint;
Filled: boolean = False; Continuous: boolean = True);
begin
DoPolyBezier(@Points[0], Length(Points), Filled, Continuous);
end;
procedure TGDIPlusCanvas.Polygon(const Points: array of TPoint; Winding: boolean;
StartIndex: integer = 0; NumPts: integer = -1);
var
GdiPoints: array of TGPPoint;
i, Count: integer;
AFillMode: FillMode;
begin
if NumPts < 0 then NumPts := Length(Points) - StartIndex;
SetLength(GdiPoints, NumPts);
for i := 0 to NumPts - 1 do
GdiPoints[i] := MakePoint(Points[StartIndex + i].X, Points[StartIndex + i].Y);
UpdateBrush;
if Winding then
AFillMode := FillModeWinding
else
AFillMode := FillModeAlternate;
CheckStatus(GdipFillPolygonI(FGpGraphics, FGpBrush, @GdiPoints[0], NumPts, AFillMode));
end;
procedure TGDIPlusCanvas.Polygon(Points: PPoint; NumPts: integer;
Winding: boolean = False);
var
GdiPoints: array of TGPPoint;
AFillMode: FillMode;
i: integer;
begin
UpdateBrush;
if Winding then
AFillMode := FillModeWinding
else
AFillMode := FillModeAlternate;
SetLength(GdiPoints, NumPts);
for i := 0 to NumPts - 1 do
begin
GdiPoints[i].X := Points[i].X;
GdiPoints[i].Y := Points[i].Y;
end;
CheckStatus(GdipFillPolygonI(FGpGraphics, FGpBrush, @GdiPoints[0], NumPts, AFillMode));
end;
procedure TGDIPlusCanvas.Polygon(const Points: array of TPoint);
begin
DoPolygon(Points);
end;
procedure TGDIPlusCanvas.Polyline(const Points: array of TPoint;
StartIndex: integer; NumPts: integer = -1);
var
GdiPoints: array of TGPPoint;
i, Count: integer;
begin
if NumPts < 0 then NumPts := Length(Points) - StartIndex;
SetLength(GdiPoints, NumPts);
for i := 0 to NumPts - 1 do
GdiPoints[i] := MakePoint(Points[StartIndex + i].X, Points[StartIndex + i].Y);
UpdatePen;
CheckStatus(GdipDrawLinesI(FGpGraphics, FGpPen, @GdiPoints[0], NumPts));
end;
procedure TGDIPlusCanvas.Polyline(Points: PPoint; NumPts: integer);
var
GdiPoints: array of TGPPoint;
i: integer;
begin
UpdatePen;
SetLength(GdiPoints, NumPts);
for i := 0 to NumPts - 1 do
begin
GdiPoints[i].X := Points[i].X;
GdiPoints[i].Y := Points[i].Y;
end;
CheckStatus(GdipDrawLinesI(FGpGraphics, FGpPen, @GdiPoints[0], NumPts));
end;
procedure TGDIPlusCanvas.Polyline(const Points: array of TPoint);
begin
DoPolyline(Points);
end;
procedure TGDIPlusCanvas.Rectangle(X1, Y1, X2, Y2: integer);
begin
DoRectangle(Rect(X1, Y1, X2, Y2));
end;
procedure TGDIPlusCanvas.Rectangle(const ARect: TRect);
begin
UpdatePen;
UpdateBrush;
if (self.Brush.Style <> bsClear) and (self.Pen.Style = psClear) then
DoRectangleFill(ARect)
else
DoRectangle(ARect);
end;
procedure TGDIPlusCanvas.RoundRect(X1, Y1, X2, Y2: integer; RX, RY: integer);
var
Path: GpPath;
W, H: integer;
begin
UpdatePen;
UpdateBrush;
W := X2 - X1;
H := Y2 - Y1;
if RX > W div 2 then RX := W div 2;
if RY > H div 2 then RY := H div 2;
CheckStatus(GdipCreatePath(FillModeAlternate, Path));
try
CheckStatus(GdipAddPathLineI(Path, X1 + RX, Y1, X2 - RX, Y1));
CheckStatus(GdipAddPathArcI(Path, X2 - 2 * RX, Y1, 2 * RX, 2 * RY, 270, 90));
CheckStatus(GdipAddPathLineI(Path, X2, Y1 + RY, X2, Y2 - RY));
CheckStatus(GdipAddPathArcI(Path, X2 - 2 * RX, Y2 - 2 * RY, 2 * RX, 2 * RY, 0, 90));
CheckStatus(GdipAddPathLineI(Path, X2 - RX, Y2, X1 + RX, Y2));
CheckStatus(GdipAddPathArcI(Path, X1, Y2 - 2 * RY, 2 * RX, 2 * RY, 90, 90));
CheckStatus(GdipAddPathLineI(Path, X1, Y2 - RY, X1, Y1 + RY));
CheckStatus(GdipAddPathArcI(Path, X1, Y1, 2 * RX, 2 * RY, 180, 90));
CheckStatus(GdipClosePathFigure(Path));
CheckStatus(GdipDrawPath(FGpGraphics, FGpPen, Path));
if (Brush.Style <> bsClear) and (FGpBrush <> nil) then
CheckStatus(GdipFillPath(FGpGraphics, FGpBrush, Path));
finally
GdipDeletePath(Path);
end;
end;
procedure TGDIPlusCanvas.RoundRect(const Rect: TRect; RX, RY: integer);
begin
RoundRect(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, RX, RY);
end;
procedure TGDIPlusCanvas.TextOut(X, Y: integer; const Text: string);
begin
DoTextOut(X, Y, ansistring(Text));
end;
procedure TGDIPlusCanvas.TextRect(const ARect: TRect; X, Y: integer; const Text: string);
begin
TextRect(ARect, X, Y, Text, FTextStyle);
end;
procedure TGDIPlusCanvas.TextRect(ARect: TRect; X, Y: integer;
const Text: string; const Style: TTextStyle);
var
gBrush: GpSolidFill;
StrFormat: GpStringFormat;
RectF: TGPRectF;
WideText: widestring;
Color: ARGB;
begin
UpdateFont;
WideText := WideString(Text);
Color := MakeColor(255, GetRValue(FLazFont.Color), GetGValue(FLazFont.Color),
GetBValue(FLazFont.Color));
CheckStatus(GdipCreateSolidFill(Color, gBrush));
CheckStatus(GdipCreateStringFormat(0, 0, StrFormat));
RectF := MakeRect(single(ARect.Left), single(ARect.Top), single(ARect.Right - ARect.Left),
single(ARect.Bottom - ARect.Top));
//RectF := MakeRect(single(ARect.Left), single(ARect.Top), single(ARect.Width),
//single(ARect.Height));
if Style.Alignment = taRightJustify then
CheckStatus(GdipSetStringFormatAlign(StrFormat, StringAlignmentFar))
else if Style.Alignment = taCenter then
CheckStatus(GdipSetStringFormatAlign(StrFormat, StringAlignmentCenter));
if Style.Layout = tlBottom then
CheckStatus(GdipSetStringFormatLineAlign(StrFormat, StringAlignmentFar))
else if Style.Layout = tlCenter then
CheckStatus(GdipSetStringFormatLineAlign(StrFormat, StringAlignmentCenter));
if Style.Clipping then
CheckStatus(GdipSetStringFormatFlags(StrFormat, StringFormatFlagsNoWrap or
StringFormatFlagsNoClip));
CheckStatus(GdipDrawString(FGpGraphics, pwidechar(WideText),
Length(WideText), FGpFont, @RectF, StrFormat, gBrush));
GdipDeleteStringFormat(StrFormat);
GdipDeleteBrush(gBrush);
end;
function TGDIPlusCanvas.TextExtent(const Text: string): TSize;
var
W, H: integer;
begin
DoGetTextSize(ansistring(Text), W, H);
Result.cx := W;
Result.cy := H;
end;
function TGDIPlusCanvas.TextHeight(const Text: string): integer;
begin
Result := DoGetTextHeight(ansistring(Text));
end;
function TGDIPlusCanvas.TextWidth(const Text: string): integer;
begin
Result := DoGetTextWidth(ansistring(Text));
end;
function TGDIPlusCanvas.TextFitInfo(const Text: string; MaxWidth: integer): integer;
var
WideText: widestring;
StrFormat: GpStringFormat;
RectF: TGPRectF;
CharsFitted, LinesFilled: integer;
begin
UpdateFont;
WideText := WideString(Text);
CheckStatus(GdipCreateStringFormat(0, 0, StrFormat));
RectF := MakeRect(single(0), single(0), single(MaxWidth), single(10000));
CheckStatus(GdipMeasureString(FGpGraphics, pwidechar(WideText),
Length(WideText), FGpFont, @RectF, StrFormat, nil, @CharsFitted, @LinesFilled));
Result := CharsFitted;
GdipDeleteStringFormat(StrFormat);
end;
procedure TGDIPlusCanvas.Lock;
begin
EnterCriticalSection(FLock);
end;
procedure TGDIPlusCanvas.Unlock;
begin
LeaveCriticalSection(FLock);
end;
function TGDIPlusCanvas.TryLock: Boolean;
begin
Result := TryEnterCriticalSection(FLock);
end;
procedure TGDIPlusCanvas.Refresh;
begin
if FAutoRedraw then RealizeAutoRedraw;
end;
procedure TGDIPlusCanvas.SaveHandleState;
begin
CheckStatus(GdipSaveGraphics(FGpGraphics, FStateToken));
end;
procedure TGDIPlusCanvas.RestoreHandleState;
begin
CheckStatus(GdipRestoreGraphics(FGpGraphics, FStateToken));
end;
function TGDIPlusCanvas.HandleAllocated: boolean;
begin
Result := (FGpGraphics <> nil);
end;
procedure TGDIPlusCanvas.RealizeAntialiasing;
begin
CheckStatus(GdipSetSmoothingMode(FGpGraphics, SmoothingModeAntiAlias));
CheckStatus(GdipSetTextRenderingHint(FGpGraphics, TextRenderingHintClearTypeGridFit));
//CheckStatus(GdipSetTextRenderingHint(FGpGraphics, TextRenderingHintAntiAlias));
end;
procedure TGDIPlusCanvas.RealizeAutoRedraw;
var
w, h: Integer;
begin
if not FAutoRedraw then Exit;
if (FMemDC = 0) or (FBitmap = 0) then Exit;
if (FPresentDC = 0) then Exit;
w := GetWidth;
h := GetHeight;
if (w <= 0) or (h <= 0) then Exit;
BitBlt(FPresentDC, FPresentX, FPresentY, w, h, FMemDC, 0, 0, SRCCOPY);
end;
initialization
StartupInput.GdiplusVersion := 1;
GdiplusStartup(GdiPlusToken, @StartupInput, nil);
finalization
GdiplusShutdown(GdiPlusToken);
{$ENDIF}
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment