lazarus/components/cairocanvas/cairocanvas.pas
2021-01-09 20:41:19 +00:00

1529 lines
41 KiB
ObjectPascal

unit CairoCanvas;
(*TFilePrinterCanvas (printers.pas)
|
|___TCairoPrinterCanvas
|
|___TCairoControlCanvas (cairographics.pas)
|
|___TCairoFileCanvas
|
|____TCairoFilePrinter (cairoprinter.pas)
|
|____TCairoPdfCanvas
|
|____TCairoPngCanvas
|
|____TCairoPsCanvas
|
|____TCairoSvgCanvas
*)
{$mode objfpc}{$H+}
{$if (FPC_FULLVERSION>=20701)}
{$Packset 1}
{$endif}
{$define pangocairo}
{-$define DebugClip}
interface
uses
Types, SysUtils, Classes, LCLType, LCLProc, Graphics, math,
// LCL
Printers,
// LazUtils
GraphMath,
//CairoCanvas
Cairo
{$ifdef pangocairo}
,Pango, PangoCairo, GLib2
{$endif}
;
type
TSquaredCorners = set of (scTopLeft,scBottomLeft,scBottomRight,scTopRight);
{ TCairoPrinterCanvas }
TCairoPrinterCanvas = class(TFilePrinterCanvas)
private
FUserClipRect: Pcairo_rectangle_t;
FLazClipRect: TRect;
{$ifdef pangocairo}
fFontDesc: PPangoFontDescription;
fFontDescStr: string;
fPageBegun: boolean;
function StylesToStr(Styles: TFontStyles):string;
procedure UpdatePangoLayout(Layout: PPangoLayout);
{$endif}
procedure SelectFontEx(AStyle: TFontStyles; const AName: string;ASize: double; aPitch: TFontPitch);
function SX(x: double): double;
function SY(y: double): double;
function SX2(x: double): double;
function SY2(y: double): double;
procedure SetSourceColor(Color: TColor);
procedure SetPenProperties;
procedure SetBrushProperties;
procedure SelectFont;
procedure PolylinePath(Points: PPoint; NumPts: Integer);
procedure EllipseArcPath(CX, CY, RX, RY: Double; Angle1, Angle2: Double;
Clockwise, Continuous: Boolean);
procedure ArcPath(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: double);
procedure ArcPath(ALeft, ATop, ARight, ABottom, StX, StY, EX, EY: double);
procedure FillAndStroke;
procedure FillOnly;
procedure StrokeOnly;
procedure TColorToRGB(Color: TColor; out R,G,B: double);
// debug tools
procedure DrawPoint(x,y: double; color: TColor);
procedure DrawRefRect(x,y,awidth,aheight: double; color:TColor);
procedure DebugSys;
protected
cr: Pcairo_t;
FontScale,ScaleX, ScaleY: Double;
procedure SetLazClipRect(r: TRect);
procedure DoLineTo(X1,Y1: Integer); override;
procedure DoMoveTo({%H-}x, {%H-}y: integer); override;
function CreateCairoHandle: HDC; virtual; abstract;
procedure DestroyCairoHandle; virtual;
procedure SetHandle(NewHandle: HDC); override;
function GetClipRect: TRect; override;
procedure SetClipRect(const ARect: TRect); override;
function GetClipping: Boolean; override;
procedure SetClipping(const AValue: boolean); override;
//
procedure CreateBrush; override;
procedure CreateFont; override;
procedure CreateHandle; override;
procedure CreatePen; override;
procedure CreateRegion; override;
procedure RealizeAntialiasing; override;
procedure DestroyHandle;
procedure SetPenMode;virtual;
public
SurfaceXDPI, SurfaceYDPI: Integer;
constructor Create(APrinter : TPrinter); override;
constructor Create; overload;
destructor Destroy; override;
procedure BeginDoc; override;
procedure EndDoc; override;
procedure NewPage; override;
procedure BeginPage; override;
procedure EndPage; override;
procedure FillRect(const ARect: TRect); override;
procedure Rectangle(X1,Y1,X2,Y2: Integer); override;
procedure Polyline(Points: PPoint; NumPts: Integer); override;
procedure Polygon(Points: PPoint; NumPts: Integer; {%H-}Winding: boolean = False); override;
procedure FrameRect(const ARect: TRect); override;
procedure Frame(const ARect: TRect); override;
procedure RoundRect(X1, Y1, X2, Y2: Integer; RX, RY: Integer); override;
procedure Ellipse(X1, Y1, X2, Y2: Integer); override;
procedure Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer); override;
procedure Arc(ALeft, ATop, ARight, ABottom, StX, StY, EX, EY: Integer); override;
procedure Chord(X1, Y1, X2, Y2, Angle1, Angle2: Integer); override;
procedure Chord(X1, Y1, X2, Y2, StX, StY, EX, EY: Integer); override;
procedure Pie(EllipseX1, EllipseY1, EllipseX2, EllipseY2, StartX, StartY, EndX, EndY: Integer); override;
procedure RadialPie(Left, Top, Right, Bottom, Angle1, Angle2: Integer); override;
procedure PolyBezier(Points: PPoint; NumPts: Integer; Filled: boolean = False; Continuous: boolean = False); override;
procedure TextOut(X,Y: Integer; const Text: String); override;
procedure TextRect(ARect: TRect; X1, Y1: integer; const Text: string; const Style: TTextStyle); override;
function TextExtent(const Text: string): TSize; override;
function GetTextMetrics(out M: TLCLTextMetric): boolean; override;
procedure StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic); override;
procedure SetPixel(X,Y: Integer; Value: TColor); override;
public
procedure MixedRoundRect(X1, Y1, X2, Y2: Integer; RX, RY: Integer; SquaredCorners: TSquaredCorners);
procedure DrawSurface(const SourceRect, DestRect: TRect; surface: Pcairo_surface_t);
procedure UpdatePageSize; virtual;
{ Not implemented
procedure FloodFill(X, Y: Integer; FillColor: TColor; FillStyle: TFillStyle); override;
procedure CopyRect(const Dest: TRect; SrcCanvas: TCanvas; const Source: TRect); override;
procedure Frame3d(var ARect: TRect; const FrameWidth: integer; const Style: TGraphicsBevelCut); override;}
end;
{ TCairoFileCanvas }
TCairoFileCanvas = class (TCairoPrinterCanvas)
protected
fStream: TStream;
sf: Pcairo_surface_t;
procedure DestroyCairoHandle; override;
procedure UpdatePageTransform;
procedure SetHandle(NewHandle: HDC); override;
public
function GetPageProperties(out aWidth, aHeight: double):String;
property Stream: TStream read fStream write fStream;
end;
{ TCairoPdfCanvas }
TCairoPdfCanvas = class(TCairoFileCanvas)
protected
function CreateCairoHandle: HDC; override;
public
procedure UpdatePageSize; override;
end;
{ TCairoSvgCanvas }
TCairoSvgCanvas = class(TCairoFileCanvas)
protected
function CreateCairoHandle: HDC; override;
end;
{ TCairoPngCanvas }
TCairoPngCanvas = class(TCairoFileCanvas)
protected
procedure SetPenMode;override;
function CreateCairoHandle: HDC; override;
procedure DestroyCairoHandle; override;
end;
{ TCairoPsCanvas }
TCairoPsCanvas = class(TCairoFileCanvas)
protected
function CreateCairoHandle: HDC; override;
public
procedure UpdatePageSize; override;
end;
function GraphicToARGB32(Source: TGraphic; buf: PByte): Boolean;
implementation
uses
IntfGraphics, GraphType, FPimage;
const
Dash_Dash: array [0..1] of double = (18, 6); //____ ____
Dash_Dot: array [0..1] of double = (3, 3); //.........
Dash_DashDot: array [0..3] of double = (9, 6, 3, 6); //__ . __ .
Dash_DashDotDot: array [0..5] of double = (9, 3, 3, 3, 3, 3); //__ . . __
function WriteToStream(closure: Pointer; data: PByte; length: LongWord): cairo_status_t; cdecl;
var
Stream: TStream absolute closure;
begin
if Stream.Write(data^, Length) = int64(Length) then
result := CAIRO_STATUS_SUCCESS
else
result := CAIRO_STATUS_WRITE_ERROR;
end;
function GraphicToARGB32(Source: TGraphic; buf: PByte): Boolean;
var
p: PDWord;
x, y: Integer;
c: TFPColor;
Img: TLazIntfImage;
begin
Img := TRasterImage(Source).CreateIntfImage;
try
if Img.DataDescription.Format=ricfNone then begin
Result := False;
Exit;
end;
p := Pointer(buf);
for y := 0 to Source.Height-1 do begin
for x := 0 to Source.Width-1 do begin
c := Img.Colors[x, y];
p^ := Hi(c.alpha) shl 24 + Hi(c.red) shl 16 + Hi(c.green) shl 8 + Hi(c.blue);
inc(p);
end;
end;
finally
Img.Free;
end;
Result := True;
end;
{ TCairoPrinterCanvas }
procedure TCairoPrinterCanvas.SetPenMode;
begin
case Pen.Mode of
pmBlack: begin
SetSourceColor(clBlack);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
end;
pmWhite: begin
SetSourceColor(clWhite);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
end;
pmCopy: cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
else
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
end;
end;
procedure TCairoPrinterCanvas.SetPenProperties;
procedure SetDash(d: array of double);
begin
cairo_set_dash(cr, @d, High(d)+1, 0);
end;
var
cap: cairo_line_cap_t;
w: double;
begin
SetSourceColor(Pen.Color);
SetPenMode;
w := Pen.Width;
if w = 0 then
w := 0.5;
w := w * ScaleY;
cairo_set_line_width(cr, w); //line_width is diameter of the pen circle
case Pen.Style of
psSolid: cairo_set_dash(cr, nil, 0, 0);
psDash: SetDash(Dash_Dash);
psDot: SetDash(Dash_Dot);
psDashDot: SetDash(Dash_DashDot);
psDashDotDot: SetDash(Dash_DashDotDot);
else
cairo_set_dash(cr, nil, 0, 0);
end;
case Pen.EndCap of
pecRound: cap := CAIRO_LINE_CAP_ROUND;
pecSquare: cap := CAIRO_LINE_CAP_SQUARE;
pecFlat: cap := CAIRO_LINE_CAP_BUTT;
end;
// dashed patterns do not look ok combined with round or squared caps
// make it flat until a solution is found
{%H-}case Pen.Style of
psDash, psDot, psDashDot, psDashDotDot:
cap := CAIRO_LINE_CAP_BUTT
end;
cairo_set_line_cap(cr, cap);
case Pen.JoinStyle of
pjsRound: cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
pjsBevel: cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
pjsMiter: cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER);
end;
end;
procedure TCairoPrinterCanvas.SetBrushProperties;
begin
SetSourceColor(Brush.Color);
end;
procedure TCairoPrinterCanvas.DoLineTo(X1, Y1: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
SetPenProperties;
cairo_move_to(cr, SX(PenPos.X), SY(PenPos.Y));
cairo_line_to(cr, SX(X1), SY(Y1));
SetInternalPenPos(Point(X1,Y1));
StrokeOnly;
Changed;
end;
procedure TCairoPrinterCanvas.DoMoveTo(x, y: integer);
begin
// should not call inherited DoMoveTo which would end calling
// interface MoveToEx which breaks things for Qt
end;
procedure TCairoPrinterCanvas.DestroyCairoHandle;
begin
//virtual
end;
procedure TCairoPrinterCanvas.SetHandle(NewHandle: HDC);
begin
if NewHandle = {%H-}HDC(cr) then exit;
if (NewHandle=0) and (cr<>nil) then DestroyHandle;
cr := {%H-}Pcairo_t(NewHandle); //Set CairoRecord Handle
inherited SetHandle(NewHandle);
end;
procedure TCairoPrinterCanvas.BeginDoc;
begin
inherited BeginDoc;
BeginPage;
end;
procedure TCairoPrinterCanvas.EndDoc;
begin
inherited EndDoc;
EndPage;
//if caller is printer, then at the end destroy cairo handles (flush output)
//and establishes CreateCairoHandle call on the next print
Handle := 0;
end;
procedure TCairoPrinterCanvas.NewPage;
begin
EndPage;
inherited NewPage;
end;
procedure TCairoPrinterCanvas.BeginPage;
begin
if assigned(printer) then
begin
FLazClipRect:=printer.PaperSize.PaperRect.WorkRect;
if HandleAllocated then
UpdatePageSize;
end else begin
RequiredState([csHandleValid]);
UpdatePageSize;
end;
fPageBegun := true;
end;
procedure TCairoPrinterCanvas.EndPage;
begin
if fPageBegun then
begin
cairo_show_page(cr);
FLazClipRect := Rect(0, 0, 0, 0);
fPageBegun := false;
end;
end;
procedure TCairoPrinterCanvas.CreateBrush;
begin
//revoked
end;
procedure TCairoPrinterCanvas.CreateFont;
begin
//revoked
end;
procedure TCairoPrinterCanvas.CreateHandle;
begin
ScaleX := SurfaceXDPI/XDPI;
ScaleY := SurfaceYDPI/YDPI;
Handle := CreateCairoHandle;
end;
procedure TCairoPrinterCanvas.CreatePen;
begin
//revoked
end;
procedure TCairoPrinterCanvas.CreateRegion;
begin
//revoked
end;
procedure TCairoPrinterCanvas.RealizeAntialiasing;
begin
//revoked
end;
procedure TCairoPrinterCanvas.DestroyHandle;
begin
cairo_destroy(cr);
cr := nil;
DestroyCairoHandle;
end;
function TCairoPrinterCanvas.GetClipRect: TRect;
var
x1,y1,x2,y2: double;
begin
RequiredState([csHandleValid]);
// it doesn't matter what the clip is in use, default or user
// this returns always the current clip
cairo_clip_extents(cr, @x1, @y1, @x2, @y2);
result.Left:=round(x1/ScaleX);
result.Top:=round(y1/ScaleY);
result.Right:=round(x2/ScaleX);
result.Bottom:=round(y2/ScaleY);
end;
procedure TCairoPrinterCanvas.SetClipRect(const ARect: TRect);
begin
RequiredState([csHandleValid]);
if FUserClipRect=nil then
New(FUserClipRect);
fUserClipRect^.x := SX(ARect.Left);
fUserClipRect^.y := SY(ARect.Top);
fUserClipRect^.width := SX2(ARect.Right-ARect.Left);
fUserClipRect^.height:= SY2(ARect.Bottom-ARect.Top);
cairo_reset_clip(cr);
{$ifdef DebugClip}
with fUserClipRect^ do begin
DrawPoint(x, y, clRed);
DrawPoint(x+Width, y+Height, clBlue);
DrawRefRect(x, y, width, height, clAqua);
end;
{$endif}
with fUserClipRect^ do
cairo_rectangle(cr, x, y, width, Height);
cairo_Clip(cr);
end;
function TCairoPrinterCanvas.GetClipping: Boolean;
begin
result := (fUserClipRect<>nil);
end;
procedure TCairoPrinterCanvas.SetClipping(const AValue: boolean);
begin
RequiredState([csHandleValid]);
cairo_reset_clip(cr);
if not AValue then begin // free user cliprect if exists
if fUserClipRect<>nil then
Dispose(fUserClipRect);
fUserClipRect := nil;
end
else begin
if fUserClipRect<>nil then
begin
with fUserClipRect^ do
begin
cairo_rectangle(cr, x, y, width, height);
cairo_clip(cr);
end;
end; // cairo_reset_clip always clip
end;
end;
procedure TCairoPrinterCanvas.DrawPoint(x, y: double; color: TColor);
var
r,g,b: Double;
begin
TColorToRGB(color, r, g, b);
cairo_set_source_rgb(cr, r, g, b);
cairo_rectangle(cr, x-2, y-2, 4, 4);
cairo_fill(cr);
end;
procedure TCairoPrinterCanvas.DrawRefRect(x, y, awidth, aheight: double;
color: TColor);
var
r,g,b: double;
begin
TColorToRGB(color, r, g, b);
cairo_set_source_rgb(cr, r, g, b);
cairo_rectangle(cr, x, y, awidth, aheight);
cairo_move_to(cr, x, y);
cairo_line_to(cr, x+awidth, y+aheight);
cairo_move_to(cr, x+awidth, y);
cairo_line_to(cr, x, y+aheight);
cairo_stroke(cr);
end;
procedure TCairoPrinterCanvas.DebugSys;
var
x,y: double;
matrix: cairo_matrix_t;
begin
cairo_get_current_point(cr, @x, @y);
cairo_get_matrix(cr, @matrix);
DebugLn('CurPoint: x=%f y=%f',[x, y]);
with matrix do
DebugLn('CurMatrix: xx=%f yx=%f xy=%f yy=%f x0=%f y0=%f',[xx,yx,xy,yy,x0,y0]);
end;
procedure TCairoPrinterCanvas.SetLazClipRect(r: TRect);
begin
FLazClipRect := r;
end;
constructor TCairoPrinterCanvas.Create(APrinter: TPrinter);
begin
inherited Create(APrinter);
ScaleX := 1;
ScaleY := 1;
FontScale := 1;
SurfaceXDPI := 72;
SurfaceYDPI := 72;
XDPI := SurfaceXDPI;
YDPI := SurfaceXDPI;
end;
constructor TCairoPrinterCanvas.Create;
begin
Create(nil);
end;
destructor TCairoPrinterCanvas.Destroy;
begin
if fUserClipRect<>nil then
Dispose(fUserClipRect);
fUserClipRect := nil;
{$ifdef pangocairo}
if fFontDesc<>nil then
pango_font_description_free(fFontDesc);
{$endif}
inherited Destroy;
end;
function TCairoPrinterCanvas.SX(x: double): double;
begin
Result := ScaleX*(x+FLazClipRect.Left);
end;
function TCairoPrinterCanvas.SY(y: double): double;
begin
Result := ScaleY*(y+FLazClipRect.Top);
end;
function TCairoPrinterCanvas.SX2(x: double): double;
begin
Result := ScaleX*x;
end;
function TCairoPrinterCanvas.SY2(y: double): double;
begin
Result := ScaleY*y;
end;
procedure TCairoPrinterCanvas.SetSourceColor(Color: TColor);
var
R, G, B: double;
begin
//TColor je ve formatu BGR
TColorToRGB(Color, R, G, B);
cairo_set_source_rgb(cr, R, G, B);
end;
procedure TCairoPrinterCanvas.Rectangle(X1, Y1, X2, Y2: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
SetPenProperties;
cairo_rectangle(cr, SX(X1), SY(Y1), SX2(X2-X1), SY2(Y2-Y1));
FillAndStroke;
Changed;
end;
//1 point rectangle in _Brush_ color
procedure TCairoPrinterCanvas.FrameRect(const ARect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
cairo_rectangle(cr, SX(ARect.Left), SY(ARect.Top), SX2(ARect.Right-ARect.Left), SY2(ARect.Bottom-ARect.Top));
SetSourceColor(Brush.Color);
cairo_set_line_width(cr, 1);
cairo_stroke(cr); //Don't touch
Changed;
end;
procedure TCairoPrinterCanvas.Frame(const ARect: TRect);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
cairo_rectangle(cr, SX(ARect.Left), SY(ARect.Top), SX2(ARect.Right-ARect.Left), SY2(ARect.Bottom-ARect.Top));
cairo_set_line_width(cr, 1);
SetSourceColor(Pen.Color);
cairo_stroke(cr); //Don't touch
Changed;
end;
//C* - center, R* - halfaxis
procedure TCairoPrinterCanvas.EllipseArcPath(CX, CY, RX, RY: Double; Angle1, Angle2: Double; Clockwise, Continuous: Boolean);
begin
if (RX=0) or (RY=0) then //cairo_scale do not likes zero params
Exit;
cairo_save(cr);
try
cairo_translate(cr, SX(CX), SY(CY));
cairo_scale(cr, SX2(RX), SY2(RY));
if not Continuous then
cairo_move_to(cr, cos(Angle1), sin(Angle1)); //Move to arcs starting point
if Clockwise then
cairo_arc(cr, 0, 0, 1, Angle1, Angle2)
else
cairo_arc_negative(cr, 0, 0, 1, Angle1, Angle2);
finally
cairo_restore(cr);
end;
end;
procedure TCairoPrinterCanvas.FillOnly;
begin
if Brush.Style <> bsClear then begin
SetBrushProperties;
cairo_fill(cr);
end;
end;
procedure TCairoPrinterCanvas.StrokeOnly;
begin
if Pen.Style <> psClear then begin
SetPenProperties;
cairo_stroke(cr);
end;
end;
procedure TCairoPrinterCanvas.TColorToRGB(Color: TColor; out R, G, B: double);
begin
R := (Color and $FF) / 255;
G := ((Color shr 8) and $FF) / 255;
B := ((Color shr 16) and $FF) / 255;
end;
{$ifdef pangocairo}
function TCairoPrinterCanvas.StylesToStr(Styles: TFontStyles): string;
begin
Result := '';
if fsBold in Styles then
Result := Result + 'bold ';
if fsItalic in Styles then
Result := Result + 'italic ';
end;
procedure TCairoPrinterCanvas.UpdatePangoLayout(Layout: PPangoLayout);
var
AttrListTemporary: Boolean;
AttrList: PPangoAttrList;
Attr: PPangoAttribute;
begin
if Font.Underline or Font.StrikeThrough then begin
AttrListTemporary := false;
AttrList := pango_layout_get_attributes(Layout);
if (AttrList = nil) then
begin
AttrList := pango_attr_list_new();
AttrListTemporary := True;
end;
if Font.Underline then
Attr := pango_attr_underline_new(PANGO_UNDERLINE_SINGLE)
else
Attr := pango_attr_underline_new(PANGO_UNDERLINE_NONE);
pango_attr_list_change(AttrList, Attr);
Attr := pango_attr_strikethrough_new(Font.StrikeThrough);
pango_attr_list_change(AttrList, Attr);
pango_layout_set_attributes(Layout, AttrList);
pango_cairo_update_layout(cr, Layout);
if AttrListTemporary then
pango_attr_list_unref(AttrList);
end;
end;
{$endif}
procedure TCairoPrinterCanvas.FillAndStroke;
begin
if Brush.Style <> bsClear then begin
SetBrushProperties;
if Pen.Style = psClear then
cairo_fill(cr)
else
cairo_fill_preserve(cr);
end;
if Pen.Style <> psClear then begin
SetPenProperties;
cairo_stroke(cr);
end;
end;
procedure TCairoPrinterCanvas.RoundRect(X1, Y1, X2, Y2: Integer; RX, RY: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
cairo_move_to(cr, SX(X1+RX), SY(Y1));
cairo_line_to(cr, SX(X2-RX), SY(Y1));
EllipseArcPath(X2-RX, Y1+RY, RX, RY, -PI/2, 0, True, True);
cairo_line_to(cr, SX(X2), SY(Y2-RY));
EllipseArcPath(X2-RX, Y2-RY, RX, RY, 0, PI/2, True, True);
cairo_line_to(cr, SX(X1+RX), SY(Y2));
EllipseArcPath(X1+RX, Y2-RY, RX, RY, PI/2, PI, True, True);
cairo_line_to(cr, SX(X1), SY(Y1+RX));
EllipseArcPath(X1+RX, Y1+RY, RX, RY, PI, PI*1.5, True, True);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.MixedRoundRect(X1, Y1, X2, Y2: Integer; RX,
RY: Integer; SquaredCorners: TSquaredCorners);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
cairo_move_to(cr, SX(X1+RX), SY(Y1));
cairo_line_to(cr, SX(X2-RX), SY(Y1));
if scTopRight in SquaredCorners then
begin
cairo_line_to(cr, SX(X2), SY(Y1));
cairo_line_to(cr, SX(X2), SY(Y1+RY));
end else
EllipseArcPath(X2-RX, Y1+RY, RX, RY, -PI/2, 0, True, True);
cairo_line_to(cr, SX(X2), SY(Y2-RY));
if scBottomRight in SquaredCorners then
begin
cairo_line_to(cr, SX(X2), SY(Y2));
cairo_line_to(cr, SX(X2-RX), SY(Y2));
end else
EllipseArcPath(X2-RX, Y2-RY, RX, RY, 0, PI/2, True, True);
cairo_line_to(cr, SX(X1+RX), SY(Y2));
if scBottomLeft in SquaredCorners then
begin
cairo_line_to(cr, SX(X1), SY(Y2));
cairo_line_to(cr, SX(X1), SY(Y2-RY));
end else
EllipseArcPath(X1+RX, Y2-RY, RX, RY, PI/2, PI, True, True);
cairo_line_to(cr, SX(X1), SY(Y1+RX));
if scTopLeft in SquaredCorners then
begin
cairo_line_to(cr, SX(X1), SY(Y1));
cairo_line_to(cr, SX(X1+RX), SY(Y1));
end else
EllipseArcPath(X1+RX, Y1+RY, RX, RY, PI, PI*1.5, True, True);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.DrawSurface(const SourceRect, DestRect: TRect;
surface: Pcairo_surface_t);
var
SW, SH: Double;
begin
Changing;
RequiredState([csHandleValid]);
cairo_save(cr);
cairo_translate(cr, SX(DestRect.Left), SY(DestRect.Top));
SW := (DestRect.Right - DestRect.Left)/(SourceRect.Right-SourceRect.Left);
SH := (DestRect.Bottom - DestRect.Top)/(SourceRect.Bottom-SourceRect.Top);
cairo_scale(cr, SX2(SW), SY2(SH));
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
cairo_restore(cr);
Changed;
end;
procedure TCairoPrinterCanvas.UpdatePageSize;
begin
//virtual
end;
procedure TCairoPrinterCanvas.Ellipse(X1, Y1, X2, Y2: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
EllipseArcPath((X2+X1)/2, (Y2+Y1)/2, (X2-X1)/2, (Y2-Y1)/2, 0, 2*PI, True, False);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
ArcPath(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength);
StrokeOnly;
Changed;
end;
procedure TCairoPrinterCanvas.Chord(X1, Y1, X2, Y2, Angle1, Angle2: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
ArcPath(X1, Y1, X2, Y2, Angle1, Angle2);
cairo_close_path(cr);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.RadialPie(Left, Top, Right, Bottom, Angle1, Angle2: Integer);
var
cx, cy: double;
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
ArcPath(Left, Top, Right, Bottom, Angle1, Angle2);
cx := (Right+Left)/2;
cy := (Bottom+Top)/2;
cairo_line_to(cr, SX(cx), SY(cy));
cairo_close_path(cr);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.ArcPath(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: double);
var
k: Double;
begin
k := - 2*PI/(360*16);
EllipseArcPath((ARight+ALeft)/2, (ABottom+ATop)/2, (ARight-ALeft)/2, (ABottom-ATop)/2,
Angle16Deg*k, Angle16DegLength*k, False, False);
end;
procedure TCairoPrinterCanvas.Arc(ALeft, ATop, ARight, ABottom, StX, StY, EX, EY: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
ArcPath(ALeft, ATop, ARight, ABottom, StX, StY, EX, EY);
StrokeOnly;
Changed;
end;
procedure TCairoPrinterCanvas.Chord(X1, Y1, X2, Y2, StX, StY, EX, EY: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
ArcPath(X1, Y1, X2, Y2, StX, StY, EX, EY);
cairo_close_path(cr);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.Pie(EllipseX1, EllipseY1, EllipseX2, EllipseY2,
StartX, StartY, EndX, EndY: Integer);
var
cx, cy: double;
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
ArcPath(EllipseX1, EllipseY1, EllipseX2, EllipseY2, StartX, StartY, EndX, EndY);
cx := (EllipseX2+EllipseX1)/2;
cy := (EllipseY2+EllipseY1)/2;
cairo_line_to(cr, SX(cx), SY(cy));
cairo_close_path(cr);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.ArcPath(ALeft, ATop, ARight, ABottom, StX, StY, EX, EY: double);
function ATanInt(x, y: double): double;
begin
if x <> 0 then begin
result := ArcTan(y/x);
if x < 0 then
result := result + PI;
end else begin
if y > 0 then
result := PI/2
else
result := - PI/2;
end;
end;
var
Angle1, Angle2: double;
cx, cy: double;
begin
cx := (ARight+ALeft)/2;
cy := (ABottom+ATop)/2;
Angle1 := ATanInt(StX-cx, StY-cy);
Angle2 := ATanInt(EX-cx, EY-cy);
EllipseArcPath(cx, cy, (ARight-ALeft)/2, (ABottom-ATop)/2, Angle1, Angle2, False, False);
end;
procedure TCairoPrinterCanvas.PolyBezier(Points: PPoint; NumPts: Integer; Filled: boolean; Continuous: boolean);
var
p, ep: PPoint;
begin
p := Points;
ep := Points + NumPts;
while p < ep do begin
if (p = Points) or not Continuous then begin //First or non cont.
cairo_move_to(cr, SX(p^.X), SY(p^.Y));
inc(p);
end;
cairo_curve_to(cr, SX(p^.X), SY(p^.Y), SX((p+1)^.X), SY((p+1)^.Y), SX((p+2)^.X), SY((p+2)^.Y));
inc(p, 3);
end;
if Filled then begin
cairo_close_path(cr);
FillAndStroke;
end else
StrokeOnly;
end;
//Toy interface
procedure TCairoPrinterCanvas.SelectFont;
begin
RequiredState([csHandleValid]);
SelectFontEx(Font.Style, Font.Name, abs(Font.Size), Font.Pitch);
SetSourceColor(Font.Color);
end;
procedure TCairoPrinterCanvas.SelectFontEx(AStyle: TFontStyles;
const AName: string; ASize: double; aPitch: TFontPitch);
var
slant: cairo_font_slant_t;
weight: cairo_font_weight_t;
{$ifdef pangocairo}
S, aFontName: string;
{$endif}
begin
if fsBold in Font.Style then
weight := CAIRO_FONT_WEIGHT_BOLD
else
weight := CAIRO_FONT_WEIGHT_NORMAL;
if fsItalic in Font.Style then
slant := CAIRO_FONT_SLANT_ITALIC
else
slant := CAIRO_FONT_SLANT_NORMAL;
{$ifdef pangocairo}
if ASize<0.001 then
ASize := 10.0;
aFontName := AName;
if (aFontName='') or SameText(aFontName, 'default') then begin
if aPitch=fpFixed then
aFontName := 'monospace'
else
aFontName := 'sans-serif';
end;
S := format('%s %s %dpx',[aFontName, StylesToStr(AStyle), round(ASize)]);
if (fFontDesc=nil) or (S<>fFontDescStr) then
begin
if fFontDesc<>nil then
pango_font_description_free(fFontDesc);
fFontDesc := pango_font_description_from_string(pchar(s));
end;
fFontDescStr := s;
{$endif}
cairo_select_font_face(cr, PChar(AName), slant, weight);
cairo_set_font_size(cr, ASize*FontScale)
end;
procedure TCairoPrinterCanvas.TextOut(X, Y: Integer; const Text: String);
var
e: cairo_font_extents_t;
{$ifdef pangocairo}
Layout: PPangoLayout;
{$endif}
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
SelectFont;
cairo_font_extents(cr, @e);
cairo_save(cr);
{$ifdef pangocairo}
// use absolute font size sintax (px)
Layout := Pango_Cairo_Create_Layout(cr);
pango_layout_set_font_description(layout, fFontDesc);
UpdatePangoLayout(Layout);
{$endif}
if Font.Orientation = 0 then
begin
cairo_move_to(cr, SX(X), SY(Y)+e.ascent);
{$ifdef pangocairo}
//DebugLn('TextOut ',Text);
//DebugSys;
pango_layout_set_text(layout, PChar(Text), -1);
{$else}
cairo_show_text(cr, PChar(Text)); //Reference point is on the base line
{$endif}
end
else
begin
cairo_move_to(cr, SX(X)+e.ascent, SY(Y));
cairo_rotate(cr, -gradtorad(Font.Orientation));
{$ifdef pangocairo}
pango_layout_set_text(layout, PChar(Text), -1);
{$else}
cairo_show_text(cr, PChar(Text)); //Reference point is on the base line
{$endif}
end;
{$ifdef pangocairo}
pango_cairo_update_layout(cr, layout);
// get the same text origin as cairo_show_text (baseline left, instead of Pango's top left)
pango_cairo_show_layout_line (cr, pango_layout_get_line (layout, 0));
g_object_unref(layout);
{$endif}
cairo_restore(cr);
Changed;
end;
procedure TCairoPrinterCanvas.TextRect(ARect: TRect; X1, Y1: integer; const Text: string; const Style: TTextStyle);
var
s: string;
var
fd: TFontData;
s1: string;
i: integer;
BoxLeft, BoxTop, BoxWidth, BoxHeight: Double;
StartLeft, StartTop: Double;
x, y: Double;
r,b: double;
{$ifdef pangocairo}
Layout: PPangoLayout;
ink,logical: TPangoRectangle;
{$endif}
Lines: TStringList;
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
cairo_save(cr);
try
s := Text;
BoxWidth := SX2(ARect.Right-ARect.Left);
BoxHeight := SY2(ARect.Bottom-ARect.Top);
BoxLeft := SX(ARect.Left);
BoxTop := SY(ARect.Top);
StartLeft := SX(X1);
StartTop := SY(Y1);
//DebugLn('Box= l=%f t=%f',[BoxLeft,BoxTop]);
//DebugLn(' x=%f y=%f',[StartLeft,StartTop]);
if Style.Clipping then begin
r := BoxWidth+Pen.Width;
b := BoxHeight+Pen.Width;
{$ifdef DebugClip}
DrawPoint(boxLeft, boxTop, clRed);
DrawPoint(boxLeft+r, boxTop+b, clBlue);
DrawRefRect(boxLeft, boxTop, r, b, clGreen);
{$endif}
cairo_rectangle(cr, BoxLeft, BoxTop, r, b);
cairo_clip(cr);
end;
if (Font.Orientation=900) or (Font.Orientation=2700) then begin
x := BoxWidth;
BoxWidth := BoxHeight;
BoxHeight := x;
end;
if Style.ExpandTabs then
s := StringReplace(s, #9, ' ', [rfReplaceAll])
else
s := StringReplace(s, #9, ' ', [rfReplaceAll]);
if Style.SingleLine then begin
s := StringReplace(s, #13+#10, ' ', [rfReplaceAll]);
s := StringReplace(s, #13, ' ', [rfReplaceAll]);
s := StringReplace(s, #10, ' ', [rfReplaceAll]);
end;
if Style.Opaque then begin
SetSourceColor(Brush.Color);
cairo_rectangle(cr, BoxLeft, BoxTop, BoxWidth, BoxHeight);
cairo_fill(cr)
end;
if Style.SystemFont and Assigned(OnGetSystemFont) then begin
fd := GetFontData(OnGetSystemFont());
SelectFontEx(fd.Style, fd.Name, fd.Height, fd.Pitch);
SetSourceColor(clWindowText);
end else
SelectFont;
{$ifdef pangocairo}
Layout := Pango_Cairo_Create_Layout(cr);
pango_layout_set_font_description(layout, fFontDesc);
UpdatePangolayout(Layout);
{$else}
cairo_font_extents(cr, @fe);
{$endif}
Lines := TStringList.Create;
Lines.Text := s;
{$ifdef pangocairo}
if Style.Wordbreak then begin
pango_layout_set_width(layout, Round(BoxWidth*PANGO_SCALE));
pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
case Style.Alignment of //Works only with pango_layout_set_width
taLeftJustify: pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
taCenter: pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
taRightJustify: pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
end;
end;
pango_layout_set_text(layout, pchar(s), -1);
pango_layout_get_extents(Layout, @ink, @logical);
//Calc start 'box' relative positions
case Style.Layout of
tlTop: y := 0;
tlCenter: y := BoxHeight/2 - logical.Height/PANGO_SCALE/2;
tlBottom: y := BoxHeight - logical.height/PANGO_SCALE;
end;
{$else}
//Calc start positions
case Style.Layout of
tlTop: y := 0;
tlCenter: y := BoxHeight/2 - fe.height*Lines.Count/2;
tlBottom: y := BoxHeight - fe.height*Lines.Count;
end;
{$endif}
// translate origin
cairo_translate(cr, StartLeft, StartTop);
// rotate
cairo_rotate(cr, -DegToRad(Font.Orientation/10));
//Text output
for i := 0 to Lines.Count-1 do begin
s1 := Lines[i];
//DebugLn('i=%i y=%f s1=%s',[i,y,s1]);
{$ifdef pangocairo}
pango_layout_set_text(layout, pchar(s1), -1);
pango_layout_get_extents(Layout, @ink, @logical);
x := 0;
if not Style.Wordbreak then begin
{%H-}case Style.Alignment of
taCenter: x := BoxWidth/2 - logical.width/PANGO_SCALE/2;
taRightJustify: x := BoxWidth - logical.Width/PANGO_SCALE;
end;
end;
cairo_move_to(cr, x, y);
//DebugLn('TextRect ',S1);
//DebugSys;
pango_cairo_show_layout(cr, layout);
y := y + logical.height/PANGO_SCALE;
{$else}
case Style.Alignment of
taLeftJustify: x := StartLeft;
taCenter: x := BoxLeft + BoxWidth/2 - CurLine.Width/2;
taRightJustify: x := BoxLeft+BoxWidth - CurLine.Width;
end;
cairo_move_to(cr, x, y+fe.ascent);
cairo_show_text(cr, PChar(s1)); //Reference point is on the base line
y := y + fe.height;
{$endif}
end;
{$ifdef pangocairo}
g_object_unref(layout);
{$endif}
finally
cairo_restore(cr);
Lines.Free;
end;
Changed;
end;
function TCairoPrinterCanvas.TextExtent(const Text: string): TSize;
var
extents: cairo_text_extents_t;
{$ifdef pangocairo}
Layout: PPangoLayout;
theRect: TPangoRectangle;
{$endif}
begin
RequiredState([csHandleValid, csFontValid]);
SelectFont;
{$ifdef pangocairo}
Layout := Pango_Cairo_Create_Layout(cr);
pango_layout_set_font_description(Layout, fFontDesc);
cairo_text_extents(cr, PChar(Text), @extents);
pango_layout_set_text(Layout, pchar(Text), -1);
pango_layout_get_extents(Layout, nil, @theRect);
Result.cx := Round((theRect.width/PANGO_SCALE)/ScaleX);
Result.cy := Round((theRect.height/PANGO_SCALE)/ScaleY);
g_object_unref(Layout);
{$else}
cairo_text_extents(cr, PChar(Text), @extents); //transformation matrix is here ignored
Result.cx := Round((extents.width)/ScaleX+extents.x_bearing);
Result.cy := Round((extents.height)/ScaleY-extents.y_bearing);
{$endif}
end;
function TCairoPrinterCanvas.GetTextMetrics(out M: TLCLTextMetric): boolean;
var
e: cairo_font_extents_t;
begin
RequiredState([csHandleValid, csFontValid]);
SelectFont;
cairo_font_extents(cr, @e); //transformation matrix is here ignored
FillChar(M{%H-}, SizeOf(M), 0);
M.Ascender := Round(e.ascent/ScaleY);
M.Descender := Round(e.descent/ScaleY);
M.Height := Round(e.height/ScaleY);
Result := True;
end;
procedure TCairoPrinterCanvas.StretchDraw(const DestRect: TRect; SrcGraphic: TGraphic);
var
sf: Pcairo_surface_t;
buf: PByte;
W, H: Integer;
SW, SH: Double;
begin
if not (SrcGraphic is TRasterImage) then begin
inherited StretchDraw(DestRect, SrcGraphic);
Exit;
end;
Changing;
RequiredState([csHandleValid]);
W := SrcGraphic.Width;
H := SrcGraphic.Height;
buf := GetMem(W*H*4);
try
cairo_save(cr);
//FillDWord(buf^, W*H, $00000000);
if not GraphicToARGB32(SrcGraphic, buf) then
Exit;
sf := cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, W, H, W*4);
cairo_translate(cr, SX(DestRect.Left), SY(DestRect.Top));
SW := (DestRect.Right - DestRect.Left)/W;
SH := (DestRect.Bottom - DestRect.Top)/H;
cairo_scale(cr, SX2(SW), SY2(SH));
cairo_set_source_surface(cr, sf, 0, 0);
cairo_paint(cr);
cairo_surface_destroy(sf);
cairo_restore(cr);
finally
FreeMem(buf);
end;
Changed;
end;
procedure TCairoPrinterCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
SetSourceColor(Value);
cairo_rectangle(cr, SX(X), SY(Y), 1, 1);
cairo_fill(cr);
Changed;
end;
procedure TCairoPrinterCanvas.PolylinePath(Points: PPoint; NumPts: Integer);
var
p: PPoint;
i: integer;
begin
p := Points;
cairo_move_to(cr, SX(p^.X), SY(p^.Y));
for i := 0 to NumPts-2 do begin
inc(p);
cairo_line_to(cr, SX(p^.X), SY(p^.Y));
end;
end;
procedure TCairoPrinterCanvas.Polyline(Points: PPoint; NumPts: Integer);
begin
if NumPts <= 0 then
Exit;
Changing;
RequiredState([csHandleValid, csPenValid]);
PolylinePath(Points, NumPts);
StrokeOnly;
Changed;
end;
procedure TCairoPrinterCanvas.Polygon(Points: PPoint; NumPts: Integer; Winding: boolean);
begin
if NumPts <= 0 then
Exit;
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
PolylinePath(Points, NumPts);
cairo_close_path(cr);
FillAndStroke;
Changed;
end;
procedure TCairoPrinterCanvas.FillRect(const ARect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
cairo_rectangle(cr, SX(ARect.Left), SY(ARect.Top), SX2(ARect.Right-ARect.Left), SY2(ARect.Bottom-ARect.Top));
FillOnly;
Changed;
end;
{ TCairoFileCanvas }
procedure TCairoFileCanvas.SetHandle(NewHandle: HDC);
begin
inherited SetHandle(NewHandle);
if HandleAllocated then
UpdatePageTransform;
end;
procedure TCairoFileCanvas.DestroyCairoHandle;
begin
cairo_surface_finish(sf);
cairo_surface_destroy(sf);
sf := nil;
end;
procedure TCairoFileCanvas.UpdatePageTransform;
var
W, H: double;
procedure TranslateAndRotate(W,H,PiRelative:Double);
begin
cairo_translate(cr, W, H);
cairo_rotate(cr, PI * PiRelative);
end;
begin
cairo_identity_matrix(cr);
GetPageProperties(W, H);
case Orientation of
poPortrait : TranslateAndRotate( 0 , 0 , 0 );
poLandscape : TranslateAndRotate( 0 ,max(W,h),-0.5);
poReverseLandscape: TranslateAndRotate(min(H,W), 0 , 0.5);
poReversePortrait : TranslateAndRotate(min(H,W),max(W,h), 1 );
end;
end;
function TCairoFileCanvas.GetPageProperties(out aWidth, aHeight: double):String;
begin
// Case sensitive in PS file:
// "%%PageOrientation: portrait|landscape" differs from "%%Orientation: Portrait|Landscape".
if Orientation in [poLandscape, poReverseLandscape] then begin
Result := '%%PageOrientation: landscape';
aWidth := PaperHeight*ScaleY; //switch H, W
aHeight := PaperWidth*ScaleX;
end else begin
Result := '%%PageOrientation: portait';
aWidth := PaperWidth*ScaleX;
aHeight := PaperHeight*ScaleY;
end;
end;
{ TCairoPdfCanvas }
function TCairoPdfCanvas.CreateCairoHandle: HDC;
begin
//Sizes are in Points, 72DPI (1pt = 1/72")
if fStream<>nil then
sf := cairo_pdf_surface_create_for_stream(@WriteToStream, fStream, PaperWidth*ScaleX, PaperHeight*ScaleY)
else
sf := cairo_pdf_surface_create(PChar(FOutputFileName), PaperWidth*ScaleX, PaperHeight*ScaleY);
result := {%H-}HDC(cairo_create(sf));
end;
procedure TCairoPdfCanvas.UpdatePageSize;
var
H,W:Double;
begin
GetPageProperties(W,H);
if Orientation in [poLandscape,poReverseLandscape] then //PDF's
cairo_pdf_surface_set_size(sf, H, W)
else
cairo_pdf_surface_set_size(sf, W, H);
UpdatePageTransform;
end;
{ TCairoPsCanvas }
function TCairoPsCanvas.CreateCairoHandle: HDC;
var
s: string;
W, H: Double;
begin
s:=GetPageProperties(W, H);
//Sizes are in Points, 72DPI (1pt = 1/72")
if fStream<>nil then
sf := cairo_ps_surface_create_for_stream(@WriteToStream, fStream, W, H)
else
sf := cairo_ps_surface_create(PChar(FOutputFileName), W, H);
cairo_ps_surface_dsc_begin_setup(sf);
cairo_ps_surface_dsc_comment(sf, PChar(s));
if (fStream=nil) and not FileExists(FOutputFileName) then
begin
DebugLn('Error: unable to write cairo ps to "'+FOutputFileName+'"');
DestroyCairoHandle;
exit(0);
end;
result := {%H-}HDC(cairo_create(sf));
end;
procedure TCairoPsCanvas.UpdatePageSize;
var
W, H: Double;
S: string;
begin
s:=GetPageProperties(W, H);
cairo_ps_surface_dsc_begin_page_setup(sf);
cairo_ps_surface_dsc_comment(sf, PChar(s));
UpdatePageTransform;
end;
{ TCairoSvgCanvas }
function TCairoSvgCanvas.CreateCairoHandle: HDC;
begin
//Sizes are in Points, 72DPI (1pt = 1/72")
if fStream<>nil then
sf := cairo_svg_surface_create_for_stream(@WriteToStream, fStream, PaperWidth*ScaleX, PaperHeight*ScaleY)
else
sf := cairo_svg_surface_create(PChar(FOutputFileName), PaperWidth*ScaleX, PaperHeight*ScaleY);
result := {%H-}HDC(cairo_create(sf));
end;
{ TCairoPngCanvas }
procedure TCairoPngCanvas.SetPenMode;
begin
inherited SetPenMode;
{ bitwise color operators make sense only for raster graphics }
{%H-}case Pen.Mode of
pmXor: cairo_set_operator(cr, CAIRO_OPERATOR_XOR);
pmNotXor: cairo_set_operator(cr, CAIRO_OPERATOR_XOR);
end;
end;
function TCairoPngCanvas.CreateCairoHandle: HDC;
var
acr: Pcairo_t;
begin
//I do not know how to retrieve DPI of cairo_image_surface
//It looks like that Cairo uses same DPI as Screen, but how much is it in case of console app???
//You must set Surface?DPI externally. For example:
//c := TCairoPngCanvas.Create;
//c.SurfaceXDPI := GetDeviceCaps(DC, LOGPIXELSX);
//c.SurfaceYDPI := GetDeviceCaps(DC, LOGPIXELSY);
sf := cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PaperWidth, PaperHeight);
acr := cairo_create(sf);
cairo_scale(acr, 1/ScaleX, 1/ScaleY);
result := {%H-}HDC(acr);
end;
procedure TCairoPngCanvas.DestroyCairoHandle;
begin
if Assigned(fStream) then
cairo_surface_write_to_png_stream(sf, @WriteToStream, fStream)
else
cairo_surface_write_to_png(sf, PChar(FOutputFileName));
inherited DestroyCairoHandle;
end;
end.