lazarus/lcl/include/canvas.inc

1879 lines
56 KiB
PHP

{%MainUnit ../graphics.pp}
{******************************************************************************
TCANVAS
******************************************************************************
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
const
csAllValid = [csHandleValid..csBrushValid];
{-----------------------------------------------}
{-- TCanvas.Draw --}
{-----------------------------------------------}
procedure TCanvas.Draw(X, Y: Integer; SrcGraphic: TGraphic);
var
ARect: TRect;
begin
if not Assigned(SrcGraphic) then exit;
ARect:=Bounds(X,Y,SrcGraphic.Width,SrcGraphic.Height);
StretchDraw(ARect,SrcGraphic);
end;
{-----------------------------------------------}
{-- TCanvas.DrawFocusRect --}
{-----------------------------------------------}
procedure TCanvas.DrawFocusRect(const ARect: TRect);
begin
Changing;
RequiredState([csHandleValid]);
LCLIntf.DrawFocusRect(FHandle, ARect);
Changed;
end;
{-----------------------------------------------}
{-- TCanvas.StretchDraw --}
{-----------------------------------------------}
procedure TCanvas.StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic);
begin
if not Assigned(SrcGraphic) then exit;
Changing;
RequiredState([csHandleValid]);
SrcGraphic.Draw(Self, DestRect);
Changed;
end;
{-----------------------------------------------}
{-- TCanvas.GetClipRect --}
{-----------------------------------------------}
function TCanvas.GetClipRect: TRect;
begin
RequiredState([csHandleValid]);
// return actual clipping rectangle
if GetClipBox(FHandle, @Result) = ERROR then
Result := Rect(0, 0, 2000, 2000);{Just in Case}
end;
procedure TCanvas.SetClipRect(const ARect: TRect);
var
RGN: HRGN;
LogicalRect: TRect;
begin
inherited SetClipRect(ARect);
if inherited GetClipping then
begin
// ARect is in logical coords. CreateRectRGN accepts device coords.
// So we need to translate them
LogicalRect := ARect;
LPtoDP(Handle, LogicalRect, 2);
with LogicalRect do
RGN := CreateRectRGN(Left, Top, Right, Bottom);
SelectClipRGN(Handle, RGN);
DeleteObject(RGN);
end;
end;
function TCanvas.GetClipping: Boolean;
var
R: TRect;
begin
Result := GetClipBox(FHandle, @R) > NullRegion;
end;
procedure TCanvas.SetClipping(const AValue: boolean);
begin
inherited SetClipping(AValue);
if AValue then
SetClipRect(inherited GetClipRect)
else
SelectClipRGN(Handle, 0);
end;
{-----------------------------------------------}
{-- TCanvas.CopyRect --}
{-----------------------------------------------}
procedure TCanvas.CopyRect(const Dest: TRect; SrcCanvas: TCanvas;
const Source: TRect);
var
SH, SW, DH, DW: Integer;
Begin
if SrcCanvas= nil then exit;
SH := Source.Bottom - Source.Top;
SW := Source.Right - Source.Left;
if (SH=0) or (SW=0) then exit;
DH := Dest.Bottom - Dest.Top;
DW := Dest.Right - Dest.Left;
if (Dh=0) or (DW=0) then exit;
SrcCanvas.RequiredState([csHandleValid]);
Changing;
RequiredState([csHandleValid]);
//DebugLn('TCanvas.CopyRect ',ClassName,' SrcCanvas=',SrcCanvas.ClassName,' ',
// ' Src=',Source.Left,',',Source.Top,',',SW,',',SH,
// ' Dest=',Dest.Left,',',Dest.Top,',',DW,',',DH);
StretchBlt(FHandle, Dest.Left, Dest.Top, DW, DH,
SrcCanvas.FHandle, Source.Left, Source.Top, SW, SH, CopyMode);
Changed;
end;
{-----------------------------------------------}
{-- TCanvas.GetPixel --}
{-----------------------------------------------}
function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
RequiredState([csHandleValid]);
Result := WidgetSet.DCGetPixel(FHandle, X, Y);
end;
{-----------------------------------------------}
{-- TCanvas.SetPixel --}
{-----------------------------------------------}
procedure TCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
Changing;
RequiredState([csHandleValid, csPenvalid]);
WidgetSet.DCSetPixel(FHandle, X, Y, Value);
Changed;
end;
{------------------------------------------------------------------------------
procedure TCanvas.RealizeAutoRedraw;
------------------------------------------------------------------------------}
procedure TCanvas.RealizeAutoRedraw;
begin
if FAutoRedraw and HandleAllocated then
WidgetSet.DCRedraw(Handle);
end;
procedure TCanvas.RealizeAntialiasing;
begin
if HandleAllocated then
begin
// do not call Changed, the content has not changed
case FAntialiasingMode of
amOn: WidgetSet.DCSetAntialiasing(FHandle, True);
amOff: WidgetSet.DCSetAntialiasing(FHandle, False);
else
WidgetSet.DCSetAntialiasing(FHandle, Boolean(WidgetSet.GetLCLCapability(lcAntialiasingEnabledByDefault)) )
end;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.CreateBrush
Params: None
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.CreateBrush;
const
HatchBrushes = [bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross];
var
OldHandle: HBRUSH;
begin
OldHandle := SelectObject(FHandle, HGDIOBJ(Brush.Reference.Handle));
if (OldHandle <> HBRUSH(Brush.Reference.Handle)) and (FSavedBrushHandle=0) then
FSavedBrushHandle := OldHandle;
Include(FState, csBrushValid);
// do not use color for hatched brushes. windows cannot draw hatches when SetBkColor is called
if ([Brush.Style] * HatchBrushes) = [] then
SetBkColor(FHandle, TColorRef(Brush.GetColor));
if Brush.Style = bsSolid then
SetBkMode(FHandle, OPAQUE)
else
SetBkMode(FHandle, TRANSPARENT);
end;
{------------------------------------------------------------------------------
Method: TCanvas.CreatePen
Params: None
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.CreatePen;
var
OldHandle: HPEN;
const
PenModes: array[TPenMode] of Integer =
(
{pmBlack } R2_BLACK,
{pmWhite } R2_WHITE,
{pmNop } R2_NOP,
{pmNot } R2_NOT,
{pmCopy } R2_COPYPEN,
{pmNotCopy } R2_NOTCOPYPEN,
{pmMergePenNot} R2_MERGEPENNOT,
{pmMaskPenNot } R2_MASKPENNOT,
{pmMergeNotPen} R2_MERGENOTPEN,
{pmMaskNotPen } R2_MASKNOTPEN,
{pmMerge } R2_MERGEPEN,
{pmNotMerge } R2_NOTMERGEPEN,
{pmMask } R2_MASKPEN,
{pmNotMask } R2_NOTMASKPEN,
{pmXor } R2_XORPEN,
{pmNotXor } R2_NOTXORPEN
);
begin
//DebugLn('[TCanvas.CreatePen] ',Classname,' Self=',DbgS(Self)
// ,' Pen=',DbgS(Pen));
OldHandle := SelectObject(FHandle, HGDIOBJ(Pen.Reference.Handle));
if (OldHandle <> HPEN(Pen.Reference.Handle)) and (FSavedPenHandle=0) then
FSavedPenHandle := OldHandle;
MoveTo(PenPos.X, PenPos.Y);
Include(FState, csPenValid);
SetROP2(FHandle, PenModes[Pen.Mode]);
end;
{------------------------------------------------------------------------------
Method: TCanvas.CreateFont
Params: None
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.CreateFont;
var
OldHandle: HFONT;
begin
// The first time the font handle is selected, the default font handle
// is returned. Save this font handle to restore it later in DeselectHandles.
// The TFont will call DeleteObject itself, so we never need to call it.
OldHandle := SelectObject(FHandle, HGDIOBJ(Font.Reference.Handle));
//DebugLn(['TCanvas.CreateFont OldHandle=',dbghex(OldHandle),' Font.Handle=',dbghex(Font.Handle)]);
if (OldHandle <> HFONT(Font.Reference.Handle)) and (FSavedFontHandle = 0) then
FSavedFontHandle := OldHandle;
Include(FState, csFontValid);
SetTextColor(FHandle, TColorRef(Font.GetColor));
end;
{------------------------------------------------------------------------------
procedure TCanvas.CreateRegion;
------------------------------------------------------------------------------}
procedure TCanvas.CreateRegion;
var
OldHandle: HRGN;
begin
OldHandle := SelectObject(FHandle, HGDIOBJ(Region.Reference.Handle));
if (OldHandle <> HRGN(Region.Reference.Handle)) and (FSavedRegionHandle=0) then
FSavedRegionHandle := OldHandle;
Include(FState, csRegionValid);
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetAutoReDraw
Params: Value
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.SetAutoRedraw(Value : Boolean);
begin
if FAutoRedraw=Value then exit;
FAutoRedraw := Value;
RealizeAutoRedraw;
end;
{------------------------------------------------------------------------------
procedure TCanvas.SetInternalPenPos(const Value: TPoint);
------------------------------------------------------------------------------}
procedure TCanvas.SetInternalPenPos(const Value: TPoint);
begin
inherited SetPenPos(Value);
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetLazBrush
Params: Value
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.SetLazBrush(Value : TBrush);
begin
FLazBrush.Assign(Value);
end;
procedure TCanvas.SetPenPos(const AValue: TPoint);
begin
MoveTo(AValue.X,AValue.Y);
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetLazFont
Params: Value
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.SetLazFont(Value : TFont);
begin
FLazFont.Assign(Value);
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetLazPen
Params: Value
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.SetLazPen(Value : TPen);
begin
FLazPen.Assign(Value);
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetRegion
Params: Value
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.SetRegion(Value: TRegion);
begin
FRegion.Assign(Value);
end;
function TCanvas.DoCreateDefaultFont: TFPCustomFont;
begin
Result:=TFont.Create;
end;
function TCanvas.DoCreateDefaultPen: TFPCustomPen;
begin
Result:=TPen.Create;
end;
function TCanvas.DoCreateDefaultBrush: TFPCustomBrush;
begin
Result:=TBrush.Create;
end;
procedure TCanvas.SetColor(x, y: integer; const Value: TFPColor);
begin
Pixels[x,y]:=FPColorToTColor(Value);
end;
function TCanvas.GetColor(x, y: integer): TFPColor;
begin
Result:=TColorToFPColor(Pixels[x,y]);
end;
procedure TCanvas.SetHeight(AValue: integer);
begin
RaiseGDBException('TCanvas.SetHeight not allowed for LCL canvas');
end;
function TCanvas.GetHeight: integer;
var
p: TPoint;
begin
if HandleAllocated then begin
GetDeviceSize(Handle,p);
Result:=p.y;
end else
Result:=0;
end;
procedure TCanvas.SetWidth(AValue: integer);
begin
RaiseGDBException('TCanvas.SetWidth not allowed for LCL canvas');
end;
function TCanvas.GetWidth: integer;
var
p: TPoint;
begin
if HandleAllocated then begin
GetDeviceSize(Handle,p);
Result:=p.x;
end else
Result:=0;
end;
procedure TCanvas.GradientFill(ARect: TRect; AStart, AStop: TColor;
ADirection: TGradientDirection);
var
RStart, RStop: Byte;
GStart, GStop: Byte;
BStart, BStop: Byte;
RDiff, GDiff, BDiff: Integer;
Count, I: Integer;
begin
if IsRectEmpty(ARect) then
Exit;
RedGreenBlue(ColorToRGB(AStart), RStart, GStart, BStart);
RedGreenBlue(ColorToRGB(AStop), RStop, GStop, BStop);
RDiff := RStop - RStart;
GDiff := GStop - GStart;
BDiff := BStop - BStart;
if ADirection = gdVertical then
Count := ARect.Bottom - ARect.Top
else
Count := ARect.Right - ARect.Left;
Changing;
for I := 0 to Count-1 do
begin
Pen.Color := RGBToColor(RStart + (i * RDiff) div Count,
GStart + (i * GDiff) div Count,
BStart + (i * BDiff) div Count);
RequiredState([csHandleValid, csPenValid]);
if ADirection = gdHorizontal
then begin
// draw top to bottom, because LineTo does not draw last pixel
LCLIntf.MoveToEx(FHandle, ARect.Left+I, ARect.Top, nil);
LCLIntf.LineTo(FHandle, ARect.Left+I, ARect.Bottom);
end
else begin
// draw left to right, because LineTo does not draw last pixel
LCLIntf.MoveToEx(FHandle, ARect.Left, ARect.Top+I, nil);
LCLIntf.LineTo(FHandle, ARect.Right, ARect.Top+I);
end;
end;
Changed;
end;
procedure TCanvas.DoLockCanvas;
begin
if FLock=0 then InitializeCriticalSection(FLock);
EnterCriticalSection(FLock);
inherited DoLockCanvas;
end;
procedure TCanvas.DoUnlockCanvas;
begin
LeaveCriticalSection(FLock);
inherited DoUnlockCanvas;
end;
procedure TCanvas.DoTextOut(x, y: integer; Text: string);
begin
TextOut(X,Y,Text);
end;
procedure TCanvas.DoGetTextSize(Text: string; var w, h: integer);
var
TxtSize: TSize;
begin
TxtSize:=TextExtent(Text);
w:=TxtSize.cx;
h:=TxtSize.cy;
end;
function TCanvas.DoGetTextHeight(Text: string): integer;
begin
Result:=TextHeight(Text);
end;
function TCanvas.DoGetTextWidth(Text: string): integer;
begin
Result:=TextWidth(Text);
end;
procedure TCanvas.DoRectangle(const Bounds: TRect);
begin
Frame(Bounds);
end;
procedure TCanvas.DoRectangleFill(const Bounds: TRect);
begin
FillRect(Bounds);
end;
procedure TCanvas.DoRectangleAndFill(const Bounds: TRect);
begin
Rectangle(Bounds);
end;
procedure TCanvas.DoEllipse(const Bounds: TRect);
var
x1: Integer;
y1: Integer;
x2: Integer;
y2: Integer;
begin
if Bounds.Left < Bounds.Right then
begin
x1 := Bounds.Left;
x2 := Bounds.Right;
end else
begin
x1 := Bounds.Right;
x2 := Bounds.Left;
end;
if Bounds.Top < Bounds.Bottom then
begin
y1 := Bounds.Top;
y2 := Bounds.Bottom;
end else
begin
y1 := Bounds.Bottom;
y2 := Bounds.Top;
end;
Arc(x1, y1, x2, y2, 0, 360*16);
end;
procedure TCanvas.DoEllipseFill(const Bounds: TRect);
begin
Ellipse(Bounds);
end;
procedure TCanvas.DoEllipseAndFill(const Bounds: TRect);
begin
inherited DoEllipseAndFill(Bounds);
end;
procedure TCanvas.DoPolygon(const Points: array of TPoint);
begin
Polyline(Points);
end;
procedure TCanvas.DoPolygonFill(const Points: array of TPoint);
begin
Polygon(Points);
end;
procedure TCanvas.DoPolygonAndFill(const Points: array of TPoint);
begin
inherited DoPolygonAndFill(Points);
end;
procedure TCanvas.DoPolyline(const Points: array of TPoint);
begin
Polyline(Points);
end;
procedure TCanvas.DoPolyBezier(Points: PPoint; NumPts: Integer;
Filled: boolean; Continuous: boolean);
begin
PolyBezier(Points,NumPts,Filled,Continuous);
end;
procedure TCanvas.DoFloodFill(x, y: integer);
begin
FloodFill(x, y, Brush.Color, fsSurface);
end;
procedure TCanvas.DoMoveTo(x, y: integer);
begin
RequiredState([csHandleValid]);
if LCLIntf.MoveToEx(FHandle, X, Y, nil) then
SetInternalPenPos(Point(X, Y));
end;
procedure TCanvas.DoLineTo(x, y: integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
if LCLIntf.LineTo(FHandle, X, Y) then
SetInternalPenPos(Point(X, Y));
Changed;
end;
procedure TCanvas.DoLine(x1, y1, x2, y2: integer);
begin
MoveTo(x1,y1);
LineTo(x2,y2);
end;
procedure TCanvas.DoCopyRect(x, y: integer; SrcCanvas: TFPCustomCanvas;
const SourceRect: TRect);
procedure WarnNotSupported;
begin
debugln('WARNING: TCanvas.DoCopyRect from ',DbgSName(SrcCanvas));
end;
var
SH: Integer;
SW: Integer;
Begin
if SrcCanvas=nil then exit;
if SrcCanvas is TCanvas then begin
SW := SourceRect.Right - SourceRect.Left;
SH := SourceRect.Bottom - SourceRect.Top;
if (SH=0) or (SW=0) then exit;
CopyRect(Rect(x,y,x+SW,y+SH),TCanvas(SrcCanvas),SourceRect);
end else begin
WarnNotSupported;
end;
end;
procedure TCanvas.DoDraw(x, y: integer; const Image: TFPCustomImage);
var
LazImg: TLazIntfImage;
BitmapHnd, DummyHnd: HBitmap;
begin
if Image=nil then exit;
BitmapHnd:=0;
try
if Image is TLazIntfImage
then begin
LazImg := TLazIntfImage(Image);
end
else begin
LazImg := TLazIntfImage.Create(0,0,[]);
RequiredState([csHandleValid]);
LazImg.DataDescription := GetDescriptionFromDevice(Handle, 0, 0);
LazImg.Assign(Image);
end;
LazImg.CreateBitmaps(BitmapHnd, DummyHnd, True);
if BitmapHnd=0 then exit;
Changing;
RequiredState([csHandleValid]);
StretchBlt(FHandle,x,y,LazImg.Width,LazImg.Height,
BitmapHnd, 0,0,LazImg.Width,LazImg.Height, CopyMode);
Changed;
finally
if Image <> LazImg then LazImg.Free;
if BitmapHnd <> 0 then DeleteObject(BitmapHnd);
end;
end;
procedure TCanvas.CheckHelper(AHelper: TFPCanvasHelper);
begin
debugln('TCanvas.CheckHelper ignored for ',DbgSName(AHelper));
end;
function TCanvas.GetDefaultColor(const ADefaultColorType: TDefaultColorType): TColor;
begin
Result := clDefault;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Arc
Params: ALeft, ATop, ARight, ABottom, Angle, AngleLength
Returns: Nothing
Use Arc to draw an elliptically curved line with the current Pen.
The angles Angle and AngleLength are 1/16th of a degree. For example, a full
circle equals 5760 (16*360). Positive values of Angle and AngleLength mean
counter-clockwise while negative values mean clockwise direction.
Zero degrees is at the 3'o clock position.
------------------------------------------------------------------------------}
procedure TCanvas.Arc(ALeft, ATop, ARight, ABottom,
Angle16Deg, Angle16DegLength: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
LCLIntf.Arc(FHandle, ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength);
Changed;
end;
procedure TCanvas.ArcTo(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: Integer);
var
r: TRect;
begin
r:=Rect(ALeft, ATop, ARight, ABottom);
LineTo(RadialPoint(EccentricAngle(Point(SX, SY), r), r));
Arc(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY);
MoveTo(RadialPoint(EccentricAngle(Point(EX, EY), r), r));
end;
procedure TCanvas.AngleArc(X, Y: Integer; Radius: Longword; StartAngle, SweepAngle: Single);
var
x1, y1, x2, y2: integer;
sinStartAngle, cosStartAngle, sinEndAngle, cosEndAngle: Single;
begin
SinCos(pi * StartAngle / 180, sinStartAngle, cosStartAngle);
SinCos(pi * (StartAngle + SweepAngle) / 180, sinEndAngle, cosEndAngle);
x1:=trunc(x+cosStartAngle*Radius);
y1:=trunc(y-sinStartAngle*Radius);
x2:=trunc(x+cosEndAngle*Radius);
y2:=trunc(y-sinEndAngle*Radius);
LineTo(x1,y1);
if SweepAngle>0 then
Arc(x-Radius, y-Radius, x+Radius, y+Radius, x1, y1, x2, y2)
else
Arc(x-Radius, y-Radius, x+Radius, y+Radius, x2, y2, x1, y1);
MoveTo(x2,y2);
end;
{------------------------------------------------------------------------------
Method: TCanvas.Arc
Params: ALeft, ATop, ARight, ABottom, sx, sy, ex, ey
Returns: Nothing
Use Arc to draw an elliptically curved line with the current Pen. The
values sx,sy, and ex,ey represent the starting and ending radial-points
between which the Arc is drawn.
------------------------------------------------------------------------------}
procedure TCanvas.Arc(ALeft, ATop, ARight, ABottom, SX, SY, EX, EY: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.RadialArc(FHandle, ALeft, ATop, ARight, ABottom, sx, sy, ex, ey);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.BrushCopy
Params: ADestRect, ABitmap, ASourceRect, ATransparentColor
Returns: Nothing
Makes a stretch draw operation while substituting a color of the source bitmap
with the color of the brush of the canvas
------------------------------------------------------------------------------}
procedure TCanvas.BrushCopy(ADestRect: TRect; ABitmap: TBitmap; ASourceRect: TRect;
ATransparentColor: TColor);
var
lIntfImage: TLazIntfImage;
lTransparentColor, lBrushColor, lPixelColor: TFPColor;
lPaintedBitmap: TBitmap;
x, y: Integer;
lSrcWidth, lSrcHeight: Integer;
begin
// Preparation of data
//lDestWidth := ADestRect.Right - ADestRect.Left;
//lDestHeight := ADestRect.Bottom - ADestRect.Top;
lSrcWidth := ASourceRect.Right - ASourceRect.Left;
lSrcHeight := ASourceRect.Bottom - ASourceRect.Top;
lTransparentColor := TColorToFPColor(ColorToRGB(ATransparentColor));
lBrushColor := TColorToFPColor(ColorToRGB(Brush.Color));
lPaintedBitmap := TBitmap.Create;
lIntfImage := TLazIntfImage.Create(0, 0);
try
// First copy the source rectangle to another bitmap
// So that we don't have to iterate in pixels which wont be used changing the color
lPaintedBitmap.Width := lSrcWidth;
lPaintedBitmap.Height := lSrcHeight;
lPaintedBitmap.Canvas.Draw(-ASourceRect.Left, -ASourceRect.Top, ABitmap);
// Next copy the bitmap to a intfimage to be able to make the color change
lIntfImage.LoadFromBitmap(lPaintedBitmap.Handle, 0);
for y := 0 to lSrcHeight-1 do
for x := 0 to lSrcWidth-1 do
begin
lPixelColor := lIntfImage.Colors[x, y];
if (lPixelColor.red = lTransparentColor.red) and
(lPixelColor.green = lTransparentColor.green) and
(lPixelColor.blue = lTransparentColor.blue) then
lIntfImage.Colors[x, y] := lBrushColor;
end;
// Now obtain a bitmap with the new image
lPaintedBitmap.LoadFromIntfImage(lIntfImage);
// And stretch draw it
Self.StretchDraw(ADestRect, lPaintedBitmap);
finally
lIntfImage.Free;
lPaintedBitmap.Free;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.RadialPie
Params: x1, y1, x2, y2, StartAngle16Deg, Angle16DegLength: Integer
Returns: Nothing
Use RadialPie to draw a filled pie-shaped wedge on the canvas.
The angles StartAngle16Deg and Angle16DegLength are 1/16th of a degree.
For example, a full circle equals 5760 (16*360).
Positive values of Angle and AngleLength mean
counter-clockwise while negative values mean clockwise direction.
Zero degrees is at the 3'o clock position.
------------------------------------------------------------------------------}
procedure TCanvas.RadialPie(x1, y1, x2, y2,
StartAngle16Deg, Angle16DegLength: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.RadialPie(FHandle, x1, y1, x2, y2, StartAngle16Deg,Angle16DegLength);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Pie
Params: EllipseX1, EllipseY1, EllipseX2, EllipseY2,
StartX, StartY, EndX, EndY
Returns: Nothing
Use Pie to draw a filled Pie-shaped wedge on the canvas. The pie is part of
an ellipse between the points EllipseX1, EllipseY1, EllipseX2, EllipseY2.
The values StartX, StartY and EndX, EndY represent the starting and ending
radial-points between which the Bounding-Arc is drawn.
------------------------------------------------------------------------------}
procedure TCanvas.Pie(EllipseX1, EllipseY1, EllipseX2, EllipseY2,
StartX, StartY, EndX, EndY: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.Pie(FHandle,EllipseX1,EllipseY1,EllipseX2,EllipseY2,
StartX,StartY,EndX,EndY);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.PolyBezier
Params: Points, Filled, Continous
Returns: Boolean
Use Polybezier to draw cubic Bézier curves. The first curve is drawn from the
first point to the fourth point with the second and third points being the
control points. If the Continuous flag is TRUE then each subsequent curve
requires three more points, using the end-point of the previous Curve as its
starting point, the first and second points being used as its control points,
and the third point its end-point. If the continous flag is set to FALSE,
then each subsequent Curve requires 4 additional points, which are used
exactly as in the first curve. Any additonal points which do not add up to
a full bezier(4 for Continuous, 3 otherwise) are ignored. There must be at
least 4 points for an drawing to occur. If the Filled Flag is set to TRUE
then the resulting Poly-Bézier will be drawn as a Polygon.
------------------------------------------------------------------------------}
procedure TCanvas.PolyBezier(const Points: array of TPoint;
Filled: boolean = False;
Continuous: boolean = True);
var NPoints, i: integer;
PointArray: ^TPoint;
begin
NPoints:=High(Points)-Low(Points)+1;
if NPoints<4 then exit; // Curve must have at least 4 points
GetMem(PointArray,SizeOf(TPoint)*NPoints);
try
for i:=0 to NPoints-1 do
PointArray[i]:=Points[i+Low(Points)];
PolyBezier(PointArray, NPoints, Filled, Continuous);
finally
FreeMem(PointArray);
end;
end;
procedure TCanvas.PolyBezier(Points: PPoint; NumPts: Integer;
Filled: boolean = False;
Continuous: boolean = True);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.PolyBezier(FHandle,Points,NumPts,Filled, Continuous);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Polygon
Params: Points: array of TPoint; Winding: Boolean = False;
StartIndex: Integer = 0; NumPts: Integer = -1
Returns: Nothing
Use Polygon to draw a closed, many-sided shape on the canvas, using the value
of Pen. After drawing the complete shape, Polygon fills the shape using the
value of Brush.
The Points parameter is an array of points that give the vertices of the
polygon.
Winding determines how the polygon is filled. When Winding is True, Polygon
fills the shape using the Winding fill algorithm. When Winding is False,
Polygon uses the even-odd (alternative) fill algorithm.
StartIndex gives the index of the first point in the array to use. All points
before this are ignored.
NumPts indicates the number of points to use, starting at StartIndex.
If NumPts is -1 (the default), Polygon uses all points from StartIndex to the
end of the array.
The first point is always connected to the last point.
To draw a polygon on the canvas, without filling it, use the Polyline method,
specifying the first point a second time at the end.
}
procedure TCanvas.Polygon(const Points: array of TPoint; Winding: Boolean;
StartIndex: Integer; NumPts: Integer);
var
NPoints: integer;
begin
if NumPts < 0 then
NPoints := High(Points) - StartIndex + 1
else
NPoints := NumPts;
if NPoints <= 0 then Exit;
Polygon(@Points[StartIndex], NPoints, Winding);
end;
procedure TCanvas.Polygon(Points: PPoint; NumPts: Integer;
Winding: boolean = False);
begin
if NumPts <= 0 then Exit;
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.Polygon(FHandle, Points, NumPts, Winding);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Polygon
Params: Points
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.Polygon(const Points: array of TPoint);
begin
Polygon(Points, True, Low(Points), High(Points) - Low(Points) + 1);
end;
{------------------------------------------------------------------------------
Method: TCanvas.Polyline
Params: Points: array of TPoint;
StartIndex: Integer = 0; NumPts: Integer = -1
Returns: Nothing
Use Polyline to connect a set of points on the canvas. If you specify only two
points, Polyline draws a single line.
The Points parameter is an array of points to be connected.
StartIndex identifies the first point in the array to use.
NumPts indicates the number of points to use. If NumPts is -1 (the default),
PolyLine uses all the points from StartIndex to the end of the array.
Calling the MoveTo function with the value of the first point, and then
repeatedly calling LineTo with all subsequent points will draw the same image
on the canvas. However, unlike LineTo, Polyline does not change the value of
PenPos.
}
procedure TCanvas.Polyline(const Points: array of TPoint; StartIndex: Integer;
NumPts: Integer);
var
NPoints : integer;
begin
if NumPts<0 then
NPoints:=High(Points)-StartIndex+1
else
NPoints:=NumPts;
if NPoints<=0 then exit;
Polyline(@Points[StartIndex], NPoints);
end;
procedure TCanvas.Polyline(Points: PPoint; NumPts: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
LCLIntf.Polyline(FHandle,Points,NumPts);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Polyline
Params: Points
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.Polyline(const Points: array of TPoint);
begin
Polyline(Points, Low(Points), High(Points) - Low(Points) + 1);
end;
{------------------------------------------------------------------------------
Method: TCanvas.Ellipse
Params: X1, Y1, X2, Y2
Returns: Nothing
Use Ellipse to draw a filled circle or ellipse on the canvas.
------------------------------------------------------------------------------}
procedure TCanvas.Ellipse(x1, y1, x2, y2: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.Ellipse(FHandle,x1,y1,x2,y2);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Ellipse
Params: ARect: TRect
Returns: Nothing
Use Ellipse to draw a filled circle or ellipse on the canvas.
------------------------------------------------------------------------------}
procedure TCanvas.Ellipse(const ARect: TRect);
begin
Ellipse(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom);
end;
{------------------------------------------------------------------------------
Method: TCanvas.FillRect
Params: ARect
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.FillRect(const ARect : TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
LCLIntf.FillRect(FHandle, ARect, HBRUSH(Brush.Reference.Handle));
Changed;
end;
{------------------------------------------------------------------------------
procedure TCanvas.FillRect(X1,Y1,X2,Y2 : Integer);
------------------------------------------------------------------------------}
procedure TCanvas.FillRect(X1,Y1,X2,Y2 : Integer);
begin
FillRect(Rect(X1,Y1,X2,Y2));
end;
{------------------------------------------------------------------------------
Method: TCanvas.FillRect
Params: X, Y: Integer; Color: TColor; FillStyle: TFillStyle
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.FloodFill(X, Y: Integer; FillColor: TColor;
FillStyle: TFillStyle);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
LCLIntf.FloodFill(FHandle, X, Y, FillColor, FillStyle, HBRUSH(Brush.Reference.Handle));
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Frame3d
Params: Rect
Returns: the inflated rectangle (the inner rectangle without the frame)
------------------------------------------------------------------------------}
procedure TCanvas.Frame3d(var ARect: TRect; const FrameWidth : integer;
const Style : TGraphicsBevelCut);
begin
Changing;
RequiredState([csHandleValid,csBrushValid,csPenValid]);
LCLIntf.Frame3d(FHandle, ARect, FrameWidth, Style);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Frame3D
Params: Rect
Returns: the inflated rectangle (the inner rectangle without the frame)
------------------------------------------------------------------------------}
procedure TCanvas.Frame3D(var ARect: TRect; TopColor, BottomColor: TColor;
const FrameWidth: integer);
var
W, ii : Integer;
begin
if ARect.Bottom-ARect.Top > ARect.Right-ARect.Left
then
W := ARect.Right-ARect.Left+1
else
W := ARect.Bottom-ARect.Top+1;
if FrameWidth > W then
W := W-1
else
W := FrameWidth;
for ii := 1 to W do
begin
Pen.Color := TopColor;
MoveTo(ARect.Left, ARect.Bottom-1);
LineTo(ARect.Left, ARect.Top);
LineTo(ARect.Right-1, ARect.Top);
Pen.Color := BottomColor;
LineTo(ARect.Right-1, ARect.Bottom-1);
LineTo(ARect.Left, ARect.Bottom-1);
Inc(ARect.Left);
Inc(ARect.Top);
Dec(ARect.Right);
Dec(ARect.Bottom);
end;
end;
{------------------------------------------------------------------------------
procedure TCanvas.Frame(const ARect: TRect);
Drawing the border of a rectangle with the current pen
------------------------------------------------------------------------------}
procedure TCanvas.Frame(const ARect: TRect);
var
OldBrushStyle: TFPBrushStyle;
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
OldBrushStyle := Brush.Style;
Brush.Style := bsClear;
Rectangle(ARect);
Brush.Style := OldBrushStyle;
Changed;
end;
{------------------------------------------------------------------------------
procedure TCanvas.Frame(const ARect: TRect);
Drawing the border of a rectangle with the current pen
------------------------------------------------------------------------------}
procedure TCanvas.Frame(X1, Y1, X2, Y2: Integer);
begin
Frame(Rect(X1, Y1, X2, Y2));
end;
{------------------------------------------------------------------------------
procedure TCanvas.FrameRect(const ARect: TRect);
Drawing the border of a rectangle with the current brush
------------------------------------------------------------------------------}
procedure TCanvas.FrameRect(const ARect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
LCLIntf.FrameRect(FHandle, ARect, Brush.GetHandle);
Changed;
end;
{------------------------------------------------------------------------------
procedure TCanvas.FrameRect(const ARect: TRect);
Drawing the border of a rectangle with the current brush
------------------------------------------------------------------------------}
procedure TCanvas.FrameRect(X1, Y1, X2, Y2: Integer);
begin
FrameRect(Rect(X1, Y1, X2, Y2));
end;
function TCanvas.GetTextMetrics(out TM: TLCLTextMetric): boolean;
var
TTM: TTextMetric;
begin
RequiredState([csHandleValid, csFontValid]); // csFontValid added in patch from bug 17555
Fillchar(TM, SizeOf(TM), 0);
Result := LCLIntf.GetTextMetrics(FHandle, TTM);
if Result then begin
TM.Ascender := TTM.tmAscent;
TM.Descender := TTM.tmDescent;
TM.Height := TTM.tmHeight;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Rectangle
Params: X1,Y1,X2,Y2
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.Rectangle(X1,Y1,X2,Y2 : Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.Rectangle(FHandle, X1, Y1, X2, Y2);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Rectangle
Params: Rect
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.Rectangle(const ARect: TRect);
begin
Rectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
end;
{------------------------------------------------------------------------------
Method: TCanvas.RoundRect
Params: X1, Y1, X2, Y2, RX, RY
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.RoundRect(X1, Y1, X2, Y2: Integer; RX,RY : Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.RoundRect(FHandle, X1, Y1, X2, Y2, RX, RY);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.RoundRect
Params: Rect, RX, RY
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.RoundRect(const Rect : TRect; RX,RY : Integer);
begin
RoundRect(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, RX, RY);
end;
{------------------------------------------------------------------------------
Method: TCanvas.TextRect
Params: ARect, X, Y, Text
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.TextRect(const ARect: TRect; X, Y: integer; const Text: string
);
begin
TextRect(ARect,X,Y,Text,TextStyle);
end;
{------------------------------------------------------------------------------
Method: TCanvas.TextRect
Params: ARect, X, Y, Text, Style
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.TextRect(ARect: TRect; X, Y: integer; const Text: string;
const Style: TTextStyle);
var
Options : Longint;
fRect : TRect;
DCIndex: Integer;
DC: HDC;
ReqState: TCanvasState;
procedure SaveState;
begin
if DCIndex<>0 then exit;
DCIndex:=SaveDC(DC);
end;
procedure RestoreState;
begin
if DCIndex=0 then exit;
RestoreDC(DC,DCIndex);
end;
begin
//debugln(['TCanvas.TextRect ',DbgSName(Self),' Text="',Text,'" ',dbgs(ARect),' X=',X,',Y=',Y]);
if Font.Name = '' then // Empty name is allowed in Delphi.
Font.Name := 'default';
Changing;
Options := 0;
case Style.Alignment of
taRightJustify : Options := DT_RIGHT;
taCenter : Options := DT_CENTER;
end;
case Style.Layout of
tlCenter : Options := Options or DT_VCENTER;
tlBottom : Options := Options or DT_BOTTOM;
end;
if Style.EndEllipsis then
Options := Options or DT_END_ELLIPSIS;
if Style.WordBreak then begin
Options := Options or DT_WORDBREAK;
if Style.EndEllipsis then
Options := Options and not DT_END_ELLIPSIS;
end;
if Style.SingleLine then
Options := Options or DT_SINGLELINE;
if not Style.Clipping then
Options := Options or DT_NOCLIP;
if Style.ExpandTabs then
Options := Options or DT_EXPANDTABS;
if not Style.ShowPrefix then
Options := Options or DT_NOPREFIX;
if Style.RightToLeft then
Options := Options or DT_RTLREADING;
ReqState:=[csHandleValid];
if not Style.SystemFont then
Include(ReqState,csFontValid);
if Style.Opaque then
Include(ReqState,csBrushValid);
DC:=GetUpdatedHandle(ReqState);
DCIndex:=0;
if Style.SystemFont or Style.Clipping or (not Style.Opaque) then
SaveState;
if Style.SystemFont then
SelectObject(DC, OnGetSystemFont());
// calculate text rectangle
fRect := ARect;
if Style.Alignment = taLeftJustify then
fRect.Left := X;
if Style.Layout = tlTop then
fRect.Top := Y;
if (Style.Alignment in [taRightJustify,taCenter]) or
(Style.Layout in [tlCenter,tlBottom]) then
begin
DrawText(DC, pChar(Text), Length(Text), fRect, DT_CALCRECT or Options);
case Style.Alignment of
taRightJustify : Types.OffsetRect(fRect, ARect.Right - fRect.Right, 0);
taCenter : Types.OffsetRect(fRect, (ARect.Right - fRect.Right) div 2, 0);
end;
case Style.Layout of
tlCenter : Types.OffsetRect(fRect, 0,
((ARect.Bottom - ARect.Top) - (fRect.Bottom - fRect.Top)) div 2);
tlBottom : Types.OffsetRect(fRect, 0, ARect.Bottom - fRect.Bottom);
end;
end;
if Style.Clipping then
begin
with ARect do
InterSectClipRect(DC, Left, Top, Right, Bottom);
Options := Options or DT_NOCLIP; // no clipping as we are handling it here
end;
if Style.Opaque then
FillRect(fRect)
else
SetBkMode(DC, TRANSPARENT);
if Style.SystemFont then
SetTextColor(DC, TColorRef(Font.GetColor));
//debugln('TCanvas.TextRect DRAW Text="',Text,'" ',dbgs(fRect));
DrawText(DC, pChar(Text), Length(Text), fRect, Options);
if Style.Opaque and (csBrushValid in FState) then
begin
if Brush.Style=bsSolid then // restore BKMode
SetBkMode(DC, OPAQUE)
end;
RestoreState;
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.TextOut
Params: X,Y,Text
Returns: Nothing
------------------------------------------------------------------------------}
procedure TCanvas.TextOut(X,Y: Integer; const Text: String);
var
Flags : Cardinal;
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
Flags := 0;
if TextStyle.Opaque then
Flags := ETO_Opaque;
if TextStyle.RightToLeft then
Flags := Flags or ETO_RTLREADING;
ExtUTF8Out(FHandle, X, Y, Flags, nil, PChar(Text), Length(Text), nil);
MoveTo(X + TextWidth(Text), Y);
Changed;
end;
{------------------------------------------------------------------------------
function TCanvas.HandleAllocated: boolean;
------------------------------------------------------------------------------}
function TCanvas.HandleAllocated: boolean;
begin
Result:=(FHandle<>0);
end;
{------------------------------------------------------------------------------
function TCanvas.GetUpdatedHandle(ReqState: TCanvasState): HDC;
------------------------------------------------------------------------------}
function TCanvas.GetUpdatedHandle(ReqState: TCanvasState): HDC;
begin
RequiredState(ReqState+[csHandleValid]);
Result:=FHandle;
end;
{------------------------------------------------------------------------------
Method: TCanvas.BrushChanged
Params: ABrush: The changed brush
Returns: Nothing
Notify proc for a brush change
------------------------------------------------------------------------------}
procedure TCanvas.BrushChanged(ABrush: TObject);
begin
if csBrushValid in FState then
Exclude(FState, csBrushValid);
end;
{------------------------------------------------------------------------------
Method: TCanvas.FontChanged
Params: AFont: the changed font
Returns: Nothing
Notify proc for a font change
------------------------------------------------------------------------------}
procedure TCanvas.FontChanged(AFont: TObject);
begin
if csFontValid in FState then
Exclude(FState, csFontValid);
end;
{------------------------------------------------------------------------------
Method: TCanvas.PenChanging
Params: APen: The changing pen
Returns: Nothing
Notify proc for a pen change
------------------------------------------------------------------------------}
procedure TCanvas.PenChanging(APen: TObject);
begin
if [csPenValid, csHandleValid] * FState = [csPenValid, csHandleValid] then
begin
Exclude(FState, csPenValid);
SelectObject(FHandle, FSavedPenHandle);
FSavedPenHandle := 0;
end;
end;
procedure TCanvas.FontChanging(AFont: TObject);
begin
if [csFontValid, csHandleValid] * FState = [csFontValid, csHandleValid] then
begin
Exclude(FState, csFontValid);
SelectObject(FHandle, FSavedFontHandle);
FSavedFontHandle := 0;
end;
end;
procedure TCanvas.BrushChanging(ABrush: TObject);
begin
if [csBrushValid, csHandleValid] * FState = [csBrushValid, csHandleValid] then
begin
Exclude(FState, csBrushValid);
SelectObject(FHandle, FSavedBrushHandle);
FSavedBrushHandle := 0;
end;
end;
procedure TCanvas.RegionChanging(ARegion: TObject);
begin
if [csRegionValid, csHandleValid] * FState = [csRegionValid, csHandleValid] then
begin
Exclude(FState, csRegionValid);
SelectObject(FHandle, FSavedRegionHandle);
FSavedRegionHandle := 0;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.PenChanged
Params: APen: The changed pen
Returns: Nothing
Notify proc for a pen change
------------------------------------------------------------------------------}
procedure TCanvas.PenChanged(APen: TObject);
begin
if csPenValid in FState then
Exclude(FState, csPenValid);
end;
{------------------------------------------------------------------------------
Method: TCanvas.RegionChanged
Params: ARegion: The changed Region
Returns: Nothing
Notify proc for a region change
------------------------------------------------------------------------------}
procedure TCanvas.RegionChanged(ARegion: TObject);
begin
if csRegionValid in FState then
Exclude(FState, csRegionValid);
end;
{------------------------------------------------------------------------------
Method: TCanvas.Create
Params: none
Returns: Nothing
Constructor for the class.
------------------------------------------------------------------------------}
constructor TCanvas.Create;
begin
FHandle := 0;
ManageResources := true;
inherited Create;
FLazFont := TFont(inherited Font);
FLazPen := TPen(inherited Pen);
FLazBrush := TBrush(inherited Brush);
FLazFont.OnChanging := @FontChanging;
FLazFont.OnChange := @FontChanged;
FSavedFontHandle := 0;
FLazPen.OnChanging := @PenChanging;
FLazPen.OnChange := @PenChanged;
FSavedPenHandle := 0;
FLazBrush.OnChanging := @BrushChanging;
FLazBrush.OnChange := @BrushChanged;
FSavedBrushHandle := 0;
FRegion := TRegion.Create;
FRegion.OnChanging := @RegionChanging;
FRegion.OnChange := @RegionChanged;
FSavedRegionHandle := 0;
FCopyMode := cmSrcCopy;
FAntialiasingMode := amDontCare;
// FLock will be initialized on demand, because most canvas don't use it
with FTextStyle do
begin
Alignment := taLeftJustify;
Layout := tlTop;
WordBreak := True;
SingleLine := True;
Clipping := True;
ShowPrefix := False;
Opaque := False;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Chord
Params: x1, y1, x2, y2, StartAngle16Deg, EndAngle16Deg
Returns: Nothing
Use Chord to draw a filled Chord-shape on the canvas. The angles angle1 and
angle2 are 1/16th of a degree. For example, a full circle equals 5760(16*360).
Positive values of Angle and AngleLength mean counter-clockwise while negative
values mean clockwise direction. Zero degrees is at the 3'o clock position.
------------------------------------------------------------------------------}
procedure TCanvas.Chord(x1, y1, x2, y2,
Angle16Deg, Angle16DegLength: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.AngleChord(FHandle, x1, y1, x2, y2, Angle16Deg, Angle16DegLength);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Chord
Params: x1, y1, x2, y2, sx, sy, ex, ey
Returns: Nothing
Use Chord to draw a filled Chord-shape on the canvas. The values sx,sy,
and ex,ey represent a starting and ending radial-points between which
the Arc is draw.
------------------------------------------------------------------------------}
procedure TCanvas.Chord(x1, y1, x2, y2, SX, SY, EX, EY: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
LCLIntf.RadialChord(FHandle, x1, y1, x2, y2, sx, sy, ex, ey);
Changed;
end;
{------------------------------------------------------------------------------
Method: TCanvas.Destroy
Params: None
Returns: Nothing
Destructor for the class.
------------------------------------------------------------------------------}
destructor TCanvas.Destroy;
begin
//DebugLn('[TCanvas.Destroy] ',ClassName,' Self=',DbgS(Self));
Handle := 0;
FreeThenNil(FClipRegion); {issue #24980 looks like TFPCustomCanvas bug}
FreeThenNil(FRegion);
FreeThenNil(FSavedHandleStates);
if FLock <> 0 then
DeleteCriticalSection(FLock);
inherited Destroy;
// set resources to nil, so that dangling pointers are spotted early
FLazFont:=nil;
FLazPen:=nil;
FLazBrush:=nil;
end;
{------------------------------------------------------------------------------
Function: TCanvas.GetHandle
Params: None
Returns: A handle to the GUI object
Checks if a handle is allocated, otherwise create it
------------------------------------------------------------------------------}
function TCanvas.GetHandle : HDC;
begin
//DebugLn('[TCanvas.GetHandle] ',ClassName);
RequiredState(csAllValid);
Result := FHandle;
end;
procedure TCanvas.SetAntialiasingMode(const AValue: TAntialiasingMode);
begin
if FAntialiasingMode <> AValue then
begin
FAntialiasingMode := AValue;
RealizeAntialiasing;
end;
end;
{------------------------------------------------------------------------------
Method: TCanvas.SetHandle
Params: NewHandle - the new device context
Returns: nothing
Deselect sub handles and sets the Handle
------------------------------------------------------------------------------}
procedure TCanvas.SetHandle(NewHandle: HDC);
begin
if FHandle = NewHandle then Exit;
//DebugLn('[TCanvas.SetHandle] Self=',DbgS(Self),' Old=',DbgS(FHandle,8),' New=',DbgS(NewHandle,8));
if FHandle <> 0 then
begin
DeselectHandles;
Exclude(FState, csHandleValid);
end;
FHandle := NewHandle;
if FHandle <> 0 then
begin
RealizeAntialiasing;
Include(FState, csHandleValid);
end;
//DebugLn('[TCanvas.SetHandle] END Self=',DbgS(Self),' Handle=',DbgS(FHandle,8));
end;
{------------------------------------------------------------------------------
Method: TCanvas.DeselectHandles
Params: none
Returns: nothing
Deselect all subhandles in the current device context
------------------------------------------------------------------------------}
procedure TCanvas.DeselectHandles;
begin
//debugln('TCanvas.DeselectHandles ',ClassName,' Self=',DbgS(Self),' Handle=',DbgS(FHandle),' FSavedBrushHandle=',DbgS(Cardinal(FSavedBrushHandle)));
if (FHandle <> 0) then
begin
// select default sub handles in the device context without deleting owns
if FSavedBrushHandle <> 0 then
SelectObject(FHandle, FSavedBrushHandle);
if FSavedPenHandle <> 0 then
SelectObject(FHandle, FSavedPenHandle);
if FSavedFontHandle <> 0 then
SelectObject(FHandle, FSavedFontHandle);
FState := FState - [csPenValid, csBrushValid, csFontValid];
end;
FSavedBrushHandle:=0;
FSavedPenHandle:=0;
FSavedFontHandle:=0;
end;
{------------------------------------------------------------------------------
Method: TCanvas.CreateHandle
Params: None
Returns: Nothing
Creates the handle ( = object).
------------------------------------------------------------------------------}
procedure TCanvas.CreateHandle;
begin
// Plain canvas does nothing
end;
procedure TCanvas.FreeHandle;
begin
Handle:=0;
end;
{------------------------------------------------------------------------------
Method: TCanvas.RequiredState
Params: ReqState: The required state
Returns: Nothing
Ensures that all handles needed are valid;
------------------------------------------------------------------------------}
procedure TCanvas.RequiredState(ReqState: TCanvasState);
var
Needed: TCanvasState;
begin
Needed := ReqState - FState;
//DebugLn('[TCanvas.RequiredState] ',ClassName,' ',csHandleValid in ReqState,' ',csHandleValid in FState,' Needed=',Needed<>[]);
if Needed <> [] then
begin
//DebugLn('[TCanvas.RequiredState] B ',ClassName,' ',csHandleValid in Needed,',',csFontValid in Needed,',',csPenValid in Needed,',',csBrushValid in Needed);
if csHandleValid in Needed then
begin
CreateHandle;
if FHandle = 0 then
raise EInvalidOperation.Create(rsCanvasDoesNotAllowDrawing);
RealizeAntialiasing;
Include(FState, csHandleValid);
end;
if csFontValid in Needed then
begin
CreateFont;
Include(FState, csFontValid);
end;
if csPenValid in Needed then
begin
CreatePen;
if Pen.Style in [psDash, psDot, psDashDot, psDashDotDot] then
Include(Needed, csBrushValid);
Include(FState, csPenValid);
end;
if csBrushValid in Needed then
begin
CreateBrush;
Include(FState, csBrushValid);
end;
end;
end;
procedure TCanvas.Changed;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TCanvas.SaveHandleState;
var
DCIndex: LongInt;
begin
if FSavedHandleStates = nil then
FSavedHandleStates := TFPList.Create;
DeselectHandles;
RequiredState([csHandleValid]);
DCIndex := SaveDC(Handle);
FSavedHandleStates.Add(Pointer(PtrInt(DCIndex)));
end;
procedure TCanvas.RestoreHandleState;
var
DCIndex: LongInt;
begin
DCIndex := LongInt(PtrUInt(FSavedHandleStates[FSavedHandleStates.Count-1]));
FSavedHandleStates.Delete(FSavedHandleStates.Count-1);
DeselectHandles;
RestoreDC(Handle, DCIndex);
end;
procedure TCanvas.Changing;
begin
if Assigned(FOnChanging) then FOnChanging(Self);
end;
{------------------------------------------------------------------------------
Function: TCanvas.TextExtent
Params: Text: The text to measure
Returns: The size
Gets the width and height of a text
------------------------------------------------------------------------------}
function TCanvas.TextExtent(const Text: string): TSize;
var
DCIndex: Integer;
procedure SaveState;
begin
if DCIndex <> 0 then exit;
DCIndex := SaveDC(FHandle);
end;
procedure RestoreState;
begin
if DCIndex = 0 then exit;
RestoreDC(FHandle, DCIndex);
end;
begin
Result.cX := 0;
Result.cY := 0;
if Text='' then exit;
RequiredState([csHandleValid, csFontValid]);
DCIndex := 0;
if Font.IsDefault then
begin
SaveState;
SelectObject(FHandle, OnGetSystemFont());
end;
GetTextExtentPoint(FHandle, PChar(Text), Length(Text), Result);
RestoreState;
end;
{------------------------------------------------------------------------------
Function: TCanvas.TextWidth
Params: Text: The text to measure
Returns: The width
Gets the width of a text
------------------------------------------------------------------------------}
function TCanvas.TextWidth(const Text: string): Integer;
begin
Result := TextExtent(Text).cX;
end;
{------------------------------------------------------------------------------
Function: TCanvas.TextFitInfo
Params: Text: The text in consideration
MaxWidth: The size, the major input
Returns: The number of characters which will fit into MaxWidth
Returns how many characters will fit in a specified width
------------------------------------------------------------------------------}
function TCanvas.TextFitInfo(const Text: string; MaxWidth: Integer): Integer;
var
lSize: TSize;
begin
LCLIntf.GetTextExtentExPoint(Self.Handle, PChar(Text), Length(Text),
MaxWidth, @Result, nil, lSize);
end;
{------------------------------------------------------------------------------
Function: TCanvas.TextHeight
Params: Text: The text to measure
Returns: A handle to the GUI object
Gets the height of a text
------------------------------------------------------------------------------}
function TCanvas.TextHeight(const Text: string): Integer;
begin
Result := TextExtent(Text).cY;
end;
{------------------------------------------------------------------------------
Function: TCanvas.Lock
Params: none
Returns: nothing
------------------------------------------------------------------------------}
procedure TCanvas.Lock;
begin
LockCanvas;
end;
function TCanvas.TryLock: Boolean;
begin
Result := not Locked;
if Result then
Lock;
end;
{------------------------------------------------------------------------------
Function: TCanvas.Unlock
Params: none
Returns: nothing
------------------------------------------------------------------------------}
procedure TCanvas.Unlock;
begin
UnlockCanvas;
end;
{------------------------------------------------------------------------------
procedure TCanvas.Refresh;
------------------------------------------------------------------------------}
procedure TCanvas.Refresh;
begin
DeselectHandles;
end;