{%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;