mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-18 12:42:42 +02:00
2174 lines
59 KiB
ObjectPascal
2174 lines
59 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
* gtk3objects.pas *
|
|
* ----------------- *
|
|
* *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
*****************************************************************************
|
|
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.
|
|
*****************************************************************************
|
|
}
|
|
unit gtk3objects;
|
|
{$i gtk3defines.inc}
|
|
{$mode objfpc}
|
|
{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, types, LCLType, LCLProc, LazUTF8, IntegerList,
|
|
LazGtk3, LazGdk3, LazGObject2, LazPango1, LazPangoCairo1, LazGdkPixbuf2,
|
|
LazGLib2, LazCairo1, FPCanvas;
|
|
|
|
type
|
|
TGtk3DeviceContext = class;
|
|
|
|
{ TGtk3Object }
|
|
|
|
TGtk3Object = class(TObject)
|
|
private
|
|
FUpdateCount: Integer;
|
|
public
|
|
constructor Create; virtual; overload;
|
|
procedure Release; virtual;
|
|
procedure BeginUpdate; virtual;
|
|
procedure EndUpdate; virtual;
|
|
function InUpdate: Boolean;
|
|
end;
|
|
|
|
{ TGtk3ContextObject }
|
|
|
|
TGtk3ContextObject = class(TGtk3Object)
|
|
private
|
|
FShared: Boolean;
|
|
public
|
|
constructor Create; override;
|
|
property Shared: Boolean read FShared write FShared;
|
|
end;
|
|
|
|
{ TGtk3Font }
|
|
|
|
TGtk3Font = class(TGtk3ContextObject)
|
|
private
|
|
FLayout: PPangoLayout;
|
|
FLogFont: TLogFont;
|
|
FFontName: String;
|
|
FHandle: PPangoFontDescription;
|
|
procedure SetFontName(const AValue: String);
|
|
public
|
|
constructor Create(ACairo: Pcairo_t; AWidget: PGtkWidget = nil);
|
|
constructor Create(ALogFont: TLogFont; const ALongFontName: String);
|
|
destructor Destroy; override;
|
|
property FontName: String read FFontName write SetFontName;
|
|
property Handle: PPangoFontDescription read FHandle;
|
|
property Layout: PPangoLayout read FLayout;
|
|
property LogFont: TLogFont read FLogFont;
|
|
end;
|
|
|
|
{ TGtk3Brush }
|
|
|
|
TGtk3Brush = class(TGtk3ContextObject)
|
|
private
|
|
FColor: TColor;
|
|
FContext: TGtk3DeviceContext;
|
|
FStyle: LongWord;
|
|
function GetColor: TColor;
|
|
procedure SetColor(AValue: TColor);
|
|
procedure SetStyle(AValue: cardinal);
|
|
public
|
|
LogBrush: TLogBrush;
|
|
constructor Create; override;
|
|
property Color: TColor read GetColor write SetColor;
|
|
property Context: TGtk3DeviceContext read FContext write FContext;
|
|
property Style: LongWord read FStyle write SetStyle;
|
|
end;
|
|
|
|
{ TGtk3Pen }
|
|
|
|
TGtk3Pen = class(TGtk3ContextObject)
|
|
private
|
|
FCosmetic: Boolean;
|
|
FEndCap: TPenEndCap;
|
|
FJoinStyle: TPenJoinStyle;
|
|
FPenMode: TPenMode;
|
|
FStyle: TFPPenStyle;
|
|
FWidth: Integer;
|
|
FColor: TColor;
|
|
FContext: TGtk3DeviceContext;
|
|
FIsExtPen: Boolean;
|
|
function GetColor: TColor;
|
|
function GetWidth: Integer;
|
|
procedure SetColor(AValue: TColor);
|
|
procedure setCosmetic(b: Boolean);
|
|
procedure SetEndCap(AValue: TPenEndCap);
|
|
procedure SetJoinStyle(AValue: TPenJoinStyle);
|
|
procedure SetPenMode(AValue: TPenMode);
|
|
procedure SetStyle(AValue: TFPPenStyle);
|
|
procedure setWidth(p1: Integer);
|
|
public
|
|
LogPen: TLogPen;
|
|
constructor Create; override;
|
|
property Color: TColor read GetColor write SetColor;
|
|
property Context: TGtk3DeviceContext read FContext write FContext;
|
|
|
|
property Cosmetic: Boolean read FCosmetic write SetCosmetic;
|
|
property EndCap: TPenEndCap read FEndCap write SetEndCap;
|
|
property IsExtPen: Boolean read FIsExtPen write FIsExtPen;
|
|
property JoinStyle: TPenJoinStyle read FJoinStyle write SetJoinStyle;
|
|
property Mode: TPenMode read FPenMode write SetPenMode;
|
|
property Style: TFPPenStyle read FStyle write SetStyle;
|
|
property Width: Integer read GetWidth write SetWidth;
|
|
end;
|
|
|
|
{ TGtk3Region }
|
|
|
|
TGtk3Region = class(TGtk3ContextObject)
|
|
private
|
|
FHandle: Pcairo_region_t;
|
|
public
|
|
property Handle: Pcairo_region_t read FHandle write FHandle;
|
|
constructor Create(CreateHandle: Boolean); virtual; overload;
|
|
constructor Create(CreateHandle: Boolean; X1,Y1,X2,Y2: Integer); virtual; overload;
|
|
destructor Destroy; override;
|
|
function GetExtents: TRect;
|
|
function ContainsRect(ARect: TRect): Boolean;
|
|
function ContainsPoint(APoint: TPoint): Boolean;
|
|
end;
|
|
|
|
{ TGtk3Image }
|
|
|
|
TGtk3Image = class(TGtk3ContextObject)
|
|
private
|
|
FData: PByte;
|
|
FDataOwner: Boolean;
|
|
FHandle: PGdkPixbuf;
|
|
FFormat : cairo_format_t;
|
|
public
|
|
constructor Create; override;
|
|
constructor Create(vHandle: PGdkPixbuf); overload;
|
|
constructor Create(AData: PByte; width: Integer; height: Integer; format: cairo_format_t; const ADataOwner: Boolean = False); overload;
|
|
constructor Create(AData: PByte; width: Integer; height: Integer; bytesPerLine: Integer; format: cairo_format_t; const ADataOwner: Boolean = False); overload;
|
|
destructor Destroy; override;
|
|
procedure CopyFrom(AImage: PGdkPixbuf; x, y, w, h: integer);
|
|
function height: Integer;
|
|
function width: Integer;
|
|
function depth: Integer;
|
|
function dotsPerMeterX: Integer;
|
|
function dotsPerMeterY: Integer;
|
|
function bits: PByte;
|
|
function numBytes: LongWord;
|
|
function bytesPerLine: Integer;
|
|
function getFormat: cairo_format_t;
|
|
property Handle: PGdkPixbuf read FHandle;
|
|
end;
|
|
|
|
{ TGtk3Cursor }
|
|
|
|
TGtk3Cursor = class(TGtk3ContextObject)
|
|
// TODO
|
|
end;
|
|
|
|
{ TGtk3DeviceContext }
|
|
|
|
TGtk3DeviceContext = class (TGtk3Object)
|
|
private
|
|
FBrush: TGtk3Brush;
|
|
FFont: TGtk3Font;
|
|
FvImage: TGtk3Image;
|
|
FCanRelease: Boolean;
|
|
FCurrentBrush: TGtk3Brush;
|
|
FCurrentFont: TGtk3Font;
|
|
FCurrentImage: TGtk3Image;
|
|
FCurrentTextColor: TColorRef;
|
|
FCurrentRegion: TGtk3Region;
|
|
FOwnsCairo: Boolean;
|
|
FOwnsSurface: Boolean;
|
|
FPen: TGtk3Pen;
|
|
FvClipRect: TRect;
|
|
FCurrentPen: TGtk3Pen;
|
|
FBkMode: Integer;
|
|
function GetBkMode: Integer;
|
|
function getBrush: TGtk3Brush;
|
|
function GetFont: TGtk3Font;
|
|
function GetOffset: TPoint;
|
|
function getPen: TGtk3Pen;
|
|
function GetvImage: TGtk3Image;
|
|
procedure SetBkMode(AValue: Integer);
|
|
procedure setBrush(AValue: TGtk3Brush);
|
|
procedure SetCurrentTextColor(AValue: TColorRef);
|
|
procedure SetFont(AValue: TGtk3Font);
|
|
procedure SetOffset(AValue: TPoint);
|
|
procedure setPen(AValue: TGtk3Pen);
|
|
procedure SetvImage(AValue: TGtk3Image);
|
|
function SX(const x: double): Double;
|
|
function SY(const y: double): Double;
|
|
function SX2(const x: double): Double;
|
|
function SY2(const y: double): Double;
|
|
procedure ApplyBrush;
|
|
procedure ApplyFont;
|
|
procedure ApplyPen;
|
|
procedure FillAndStroke;
|
|
public
|
|
CairoSurface: Pcairo_surface_t;
|
|
Widget: Pcairo_t;
|
|
Parent: PGtkWidget;
|
|
Window: PGdkWindow;
|
|
ParentPixmap: PGdkPixbuf;
|
|
fncOrigin:TPoint; // non-client area offsets surface origin
|
|
constructor Create(AWidget: PGtkWidget; const APaintEvent: Boolean = False); virtual;
|
|
constructor Create(AWindow: PGdkWindow; const APaintEvent: Boolean); virtual;
|
|
constructor CreateFromCairo(AWidget: PGtkWidget; ACairo: PCairo_t); virtual;
|
|
destructor Destroy; override;
|
|
procedure CreateObjects;
|
|
procedure DeleteObjects;
|
|
public
|
|
procedure drawPoint(x1: Integer; y1: Integer);
|
|
procedure drawRect(x1: Integer; y1: Integer; w: Integer; h: Integer; const AFill: Boolean);
|
|
procedure drawRoundRect(x, y, w, h, rx, ry: Integer);
|
|
procedure drawText(x: Integer; y: Integer; const s: String); overload;
|
|
procedure drawText(x,y,w,h,flags: Integer; const s: String); overload;
|
|
procedure drawLine(x1: Integer; y1: Integer; x2: Integer; y2: Integer);
|
|
procedure drawEllipse(x: Integer; y: Integer; w: Integer; h: Integer);
|
|
procedure drawSurface(targetRect: PRect; Surface: Pcairo_surface_t; sourceRect: PRect;
|
|
mask: PGdkPixBuf; maskRect: PRect);
|
|
procedure drawImage(targetRect: PRect; image: PGdkPixBuf; sourceRect: PRect;
|
|
mask: PGdkPixBuf; maskRect: PRect);
|
|
procedure drawPixmap(p: PPoint; pm: PGdkPixbuf; sr: PRect);
|
|
procedure drawPolyLine(P: PPoint; NumPts: Integer);
|
|
procedure drawPolygon(P: PPoint; NumPts: Integer; FillRule: integer);
|
|
procedure drawPolyBezier(P: PPoint; NumPoints: Integer; Filled, Continuous: boolean);
|
|
procedure EllipseArcPath(CX, CY, RX, RY: Double; Angle1, Angle2: Double; Clockwise, Continuous: Boolean);
|
|
procedure eraseRect(ARect: PRect);
|
|
procedure fillRect(ARect: PRect; ABrush: HBRUSH); overload;
|
|
procedure fillRect(x, y, w, h: Integer; ABrush: HBRUSH); overload;
|
|
procedure fillRect(x, y, w, h: Integer); overload;
|
|
function RoundRect(X1, Y1, X2, Y2: Integer; RX, RY: Integer): Boolean;
|
|
|
|
function getBpp: integer;
|
|
function getDepth: integer;
|
|
function getDeviceSize: TPoint;
|
|
function LineTo(const X, Y: Integer): Boolean;
|
|
function MoveTo(const X, Y: Integer; OldPoint: PPoint): Boolean;
|
|
function SetClipRegion(ARgn: TGtk3Region): Integer;
|
|
procedure SetSourceColor(AColor: TColor);
|
|
procedure SetCurrentBrush(ABrush: TGtk3Brush);
|
|
procedure SetCurrentFont(AFont: TGtk3Font);
|
|
procedure SetCurrentPen(APen: TGtk3Pen);
|
|
procedure SetCurrentImage(AImage: TGtk3Image);
|
|
procedure SetImage(AImage: TGtk3Image);
|
|
function ResetClip: Integer;
|
|
procedure TranslateCairoToDevice;
|
|
procedure Translate(APoint: TPoint);
|
|
property BkMode: Integer read GetBkMode write SetBkMode;
|
|
property CanRelease: Boolean read FCanRelease write FCanRelease;
|
|
property CurrentBrush: TGtk3Brush read FCurrentBrush;
|
|
property CurrentFont: TGtk3Font read FCurrentFont;
|
|
property CurrentImage: TGtk3Image read FCurrentImage;
|
|
property CurrentPen: TGtk3Pen read FCurrentPen;
|
|
property CurrentRegion: TGtk3Region read FCurrentRegion;
|
|
property CurrentTextColor: TColorRef read FCurrentTextColor write SetCurrentTextColor;
|
|
property Offset: TPoint read GetOffset write SetOffset;
|
|
property OwnsSurface: Boolean read FOwnsSurface;
|
|
property vBrush: TGtk3Brush read getBrush write setBrush;
|
|
property vClipRect: TRect read FvClipRect write FvClipRect;
|
|
property vFont: TGtk3Font read GetFont write SetFont;
|
|
property vImage: TGtk3Image read GetvImage write SetvImage;
|
|
property vPen: TGtk3Pen read getPen write setPen;
|
|
end;
|
|
|
|
function CheckBitmap(const ABitmap: HBITMAP; const AMethodName: String;
|
|
const AParamName: String = ''): Boolean;
|
|
procedure Gtk3WordWrap(DC: HDC; AText: PChar;
|
|
MaxWidthInPixel: integer; out Lines: PPChar; out LineCount: integer);
|
|
|
|
function Gtk3DefaultContext: TGtk3DeviceContext;
|
|
function Gtk3ScreenContext: TGtk3DeviceContext;
|
|
|
|
implementation
|
|
uses math, gtk3int, gtk3procs;
|
|
|
|
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); //__ . . __
|
|
|
|
var
|
|
FDefaultContext: TGtk3DeviceContext = nil;
|
|
FScreenContext: TGtk3DeviceContext = nil;
|
|
|
|
function Gtk3DefaultContext: TGtk3DeviceContext;
|
|
begin
|
|
if FDefaultContext = nil then
|
|
FDefaultContext := TGtk3DeviceContext.Create(PGtkWidget(nil), False);
|
|
Result := FDefaultContext;
|
|
end;
|
|
|
|
function Gtk3ScreenContext: TGtk3DeviceContext;
|
|
begin
|
|
if FScreenContext = nil then
|
|
FScreenContext := TGtk3DeviceContext.Create(gdk_get_default_root_window, False);
|
|
Result := FScreenContext;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
Name: CheckBitmap
|
|
Params: Bitmap - Handle to a bitmap (TGtk3Image)
|
|
AMethodName - Method name
|
|
AParamName - Param name
|
|
Returns: If the bitmap is valid
|
|
------------------------------------------------------------------------------}
|
|
function CheckBitmap(const ABitmap: HBITMAP; const AMethodName: String;
|
|
const AParamName: String): Boolean;
|
|
begin
|
|
Result := TObject(ABitmap) is TGtk3Image;
|
|
if Result then Exit;
|
|
|
|
if Pos('.', AMethodName) = 0 then
|
|
DebugLn('Gtk3WidgetSet ' + AMethodName + ' Error - invalid bitmap ' +
|
|
AParamName + ' = ' + DbgS(ABitmap) + '!')
|
|
else
|
|
DebugLn(AMethodName + ' Error - invalid bitmap ' + AParamName + ' = ' +
|
|
DbgS(ABitmap) + '!');
|
|
end;
|
|
|
|
procedure TColorToRGB(AColor: TColor; out R, G, B: double);
|
|
begin
|
|
R := (AColor and $FF) / 255;
|
|
G := ((AColor shr 8) and $FF) / 255;
|
|
B := ((AColor shr 16) and $FF) / 255;
|
|
end;
|
|
|
|
{ TGtk3ContextObject }
|
|
|
|
constructor TGtk3ContextObject.Create;
|
|
begin
|
|
inherited Create;
|
|
FShared := False;
|
|
end;
|
|
|
|
{ TGtk3Region }
|
|
|
|
constructor TGtk3Region.Create(CreateHandle: Boolean);
|
|
begin
|
|
inherited Create;
|
|
FHandle := cairo_region_create;
|
|
end;
|
|
|
|
constructor TGtk3Region.Create(CreateHandle: Boolean; X1, Y1, X2, Y2: Integer);
|
|
var
|
|
ARect: Tcairo_rectangle_int_t;
|
|
begin
|
|
inherited Create;
|
|
FHandle := nil;
|
|
ARect.x := x1;
|
|
ARect.y := y1;
|
|
ARect.width := x2 - x1;
|
|
ARect.height := y2 - y1;
|
|
FHandle := cairo_region_create_rectangle(@ARect);
|
|
end;
|
|
|
|
destructor TGtk3Region.Destroy;
|
|
begin
|
|
if Assigned(FHandle) then
|
|
begin
|
|
cairo_region_destroy(FHandle);
|
|
FHandle := nil;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TGtk3Region.GetExtents: TRect;
|
|
var
|
|
ARect: Tcairo_rectangle_int_t;
|
|
begin
|
|
Result := Rect(0, 0, 0, 0);
|
|
if Assigned(FHandle) then
|
|
begin
|
|
cairo_region_get_extents(FHandle, @ARect);
|
|
Result.Left := ARect.x;
|
|
Result.Top := ARect.y;
|
|
Result.Right := ARect.width + ARect.x;
|
|
Result.Bottom := ARect.height + ARect.y;
|
|
end;
|
|
end;
|
|
|
|
function TGtk3Region.ContainsRect(ARect: TRect): Boolean;
|
|
var
|
|
ACairoRect: Tcairo_rectangle_int_t;
|
|
begin
|
|
with ACairoRect do
|
|
begin
|
|
x := ARect.Left;
|
|
y := ARect.Top;
|
|
width := ARect.Right - ARect.Left;
|
|
height := ARect.Bottom - ARect.Top;
|
|
end;
|
|
Result := cairo_region_contains_rectangle(FHandle, @ACairoRect) = CAIRO_REGION_OVERLAP_IN;
|
|
end;
|
|
|
|
function TGtk3Region.ContainsPoint(APoint: TPoint): Boolean;
|
|
begin
|
|
Result := cairo_region_contains_point(FHandle, APoint.x, APoint.y);
|
|
end;
|
|
|
|
{ TGtk3Font }
|
|
|
|
procedure TGtk3Font.SetFontName(const AValue: String);
|
|
begin
|
|
if FFontName=AValue then Exit;
|
|
FFontName:=AValue;
|
|
end;
|
|
|
|
constructor TGtk3Font.Create(ACairo: Pcairo_t; AWidget: PGtkWidget);
|
|
var
|
|
AContext: PPangoContext;
|
|
AOwnsContext: Boolean;
|
|
begin
|
|
inherited Create;
|
|
AOwnsContext := not Gtk3IsWidget(AWidget);
|
|
if not AOwnsContext then
|
|
begin
|
|
AContext := gtk_widget_get_pango_context(AWidget);
|
|
// DebugLn('TGtk3Font.Create AContext created from widget ....context=',dbgHex(PtrUInt(AContext)));
|
|
end else
|
|
begin
|
|
AContext := pango_cairo_create_context(ACairo);
|
|
// DebugLn('TGtk3Font.Create AContext created from pango cairo ....');
|
|
end;
|
|
FHandle := pango_font_description_copy(pango_context_get_font_description(AContext));
|
|
FFontName := pango_font_description_get_family(FHandle);
|
|
|
|
FLayout := pango_layout_new(AContext);
|
|
if FHandle^.get_size_is_absolute then
|
|
begin
|
|
FHandle^.set_absolute_size(FHandle^.get_size);
|
|
// writeln('**TGtk3Font.Create size is absolute ',FFontName,' size ',FHandle^.get_size);
|
|
end else
|
|
begin
|
|
// writeln('*TGtk3Font.Create size is not absolute ',FFontName,' size ',FHandle^.get_size);
|
|
end;
|
|
|
|
FLayout^.set_font_description(FHandle);
|
|
// writeln('TGtk3Font.Create1 ',FFontName);
|
|
if AOwnsContext then
|
|
g_object_unref(AContext);
|
|
// writeln('TGtk3Font.Create1 ',FFontName);
|
|
end;
|
|
|
|
constructor TGtk3Font.Create(ALogFont: TLogFont; const ALongFontName: String);
|
|
var
|
|
AContext: PPangoContext;
|
|
ADescription: PPangoFontDescription;
|
|
AttrList: PPangoAttrList;
|
|
AttrListTemporary: Boolean;
|
|
Attr: PPangoAttribute;
|
|
begin
|
|
FLogFont := ALogFont;
|
|
FFontName := ALogFont.lfFaceName;
|
|
AContext := gdk_pango_context_get;
|
|
if IsFontNameDefault(FFontName) or (FFontName = '') then
|
|
begin
|
|
if Gtk3WidgetSet.DefaultAppFontName <> '' then
|
|
FHandle := pango_font_description_from_string(PgChar(Gtk3WidgetSet.DefaultAppFontName))
|
|
else
|
|
begin
|
|
ADescription := pango_context_get_font_description(AContext);
|
|
FHandle := pango_font_description_copy(ADescription);
|
|
end;
|
|
FFontName := FHandle^.get_family;
|
|
end else
|
|
begin
|
|
FHandle := pango_font_description_from_string(PgChar(FFontName));
|
|
FFontName := FHandle^.get_family;
|
|
end;
|
|
if ALogFont.lfHeight <> 0 then
|
|
FHandle^.set_absolute_size(Abs(ALogFont.lfHeight) * PANGO_SCALE);
|
|
|
|
if ALogFont.lfItalic > 0 then
|
|
FHandle^.set_style(PANGO_STYLE_ITALIC);
|
|
|
|
FHandle^.set_weight(ALogFont.lfWeight);
|
|
|
|
FLayout := pango_layout_new(AContext);
|
|
FLayout^.set_font_description(FHandle);
|
|
|
|
if (ALogFont.lfUnderline<>0) or (ALogFont.lfStrikeOut<>0) then
|
|
begin
|
|
AttrListTemporary := false;
|
|
AttrList := pango_layout_get_attributes(FLayout);
|
|
if (AttrList = nil) then
|
|
begin
|
|
AttrList := pango_attr_list_new();
|
|
AttrListTemporary := True;
|
|
end;
|
|
if ALogFont.lfUnderline <> 0 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(ALogFont.lfStrikeOut<>0);
|
|
pango_attr_list_change(AttrList, Attr);
|
|
|
|
pango_layout_set_attributes(FLayout, AttrList);
|
|
|
|
if AttrListTemporary then
|
|
pango_attr_list_unref(AttrList);
|
|
end;
|
|
|
|
g_object_unref(AContext);
|
|
end;
|
|
|
|
destructor TGtk3Font.Destroy;
|
|
begin
|
|
if Assigned(FLayout) then
|
|
begin
|
|
g_object_unref(FLayout);
|
|
FLayout := nil;
|
|
end;
|
|
if Assigned(FHandle) then
|
|
begin
|
|
pango_font_description_free(FHandle);
|
|
FHandle := nil;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TGtk3Object }
|
|
|
|
constructor TGtk3Object.Create;
|
|
begin
|
|
FUpdateCount := 0;
|
|
end;
|
|
|
|
procedure TGtk3Object.Release;
|
|
begin
|
|
Free;
|
|
end;
|
|
|
|
procedure TGtk3Object.BeginUpdate;
|
|
begin
|
|
inc(FUpdateCount);
|
|
end;
|
|
|
|
procedure TGtk3Object.EndUpdate;
|
|
begin
|
|
if FUpdateCount > 0 then
|
|
dec(FUpdateCount);
|
|
end;
|
|
|
|
function TGtk3Object.InUpdate: Boolean;
|
|
begin
|
|
Result := FUpdateCount > 0;
|
|
end;
|
|
|
|
{ TGtk3Image }
|
|
|
|
constructor TGtk3Image.Create;
|
|
var
|
|
ACairo: Pcairo_t;
|
|
ASurface: Pcairo_surface_t;
|
|
ARect: TGdkRectangle;
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3Image.Create 1');
|
|
{$ENDIF}
|
|
inherited Create;
|
|
ACairo := gdk_cairo_create(gdk_get_default_root_window);
|
|
gdk_cairo_get_clip_rectangle(ACairo, @ARect);
|
|
ASurface := cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ARect.width, ARect.height);
|
|
try
|
|
FHandle := gdk_pixbuf_get_from_surface(ASurface, 0 ,0, ARect.Width, ARect.Height);
|
|
finally
|
|
cairo_surface_destroy(ASurface);
|
|
end;
|
|
FData := nil;
|
|
FDataOwner := False;
|
|
FFormat := CAIRO_FORMAT_ARGB32;
|
|
end;
|
|
|
|
constructor TGtk3Image.Create(vHandle: PGdkPixbuf);
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3Image.Create 2 vHandle=',dbgs(vHandle),' channels ',dbgs(vHandle^.get_n_channels),' bps ',dbgs(vHandle^.get_bits_per_sample),' has_alpha=',dbgs(vHandle^.get_has_alpha));
|
|
{$ENDIF}
|
|
inherited Create;
|
|
FHandle := vHandle^.copy;
|
|
FData := nil;
|
|
FDataOwner := False;
|
|
|
|
if FHandle^.get_has_alpha then
|
|
FFormat := CAIRO_FORMAT_ARGB32
|
|
else
|
|
FFormat := CAIRO_FORMAT_RGB24;
|
|
end;
|
|
|
|
constructor TGtk3Image.Create(AData: PByte; width: Integer; height: Integer;
|
|
format: cairo_format_t; const ADataOwner: Boolean);
|
|
var
|
|
ASurface: Pcairo_surface_t;
|
|
w,h: Integer;
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3Image.Create 3 AData=',dbgs(AData <> nil),' format=',dbgs(Ord(format)),' w=',dbgs(width),' h=',dbgs(height),' dataowner=',dbgs(ADataOwner));
|
|
{$ENDIF}
|
|
FFormat := format;
|
|
FData := AData;
|
|
FDataOwner := ADataOwner;
|
|
if FData = nil then
|
|
begin
|
|
w := width;
|
|
h := height;
|
|
if w <= 0 then
|
|
w := 16;
|
|
if h <= 0 then
|
|
h := 16;
|
|
|
|
ASurface := cairo_image_surface_create(format, w, h);
|
|
try
|
|
FHandle := gdk_pixbuf_get_from_surface(ASurface, 0 ,0, w, h);
|
|
finally
|
|
cairo_surface_destroy(ASurface);
|
|
end;
|
|
gdk_pixbuf_fill(FHandle, 0);
|
|
end else
|
|
begin
|
|
FHandle := TGdkPixbuf.new_from_data(AData, GDK_COLORSPACE_RGB, format=CAIRO_FORMAT_ARGB32, 8, width, height, 0, nil, nil);
|
|
end;
|
|
(*
|
|
if FData = nil then
|
|
begin
|
|
FHandle := QImage_create(width, height, format);
|
|
QImage_fill(FHandle, 0);
|
|
end
|
|
else
|
|
begin
|
|
FHandle := QImage_create(FData, width, height, format);
|
|
if format=QImageFormat_Mono then
|
|
QImage_setNumColors(FHandle, 2);
|
|
end;
|
|
*)
|
|
|
|
end;
|
|
|
|
constructor TGtk3Image.Create(AData: PByte; width: Integer; height: Integer;
|
|
bytesPerLine: Integer; format: cairo_format_t; const ADataOwner: Boolean);
|
|
var
|
|
ASurface: Pcairo_surface_t;
|
|
w, h: Integer;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3Image.Create 4 AData=',dbgs(AData <> nil),' format=',dbgs(Ord(format)),' w=',dbgs(width),' h=',dbgs(height),' dataowner=',dbgs(ADataOwner),' bpl=',dbgs(bytesPerLine));
|
|
{$endif}
|
|
inherited Create;
|
|
FFormat := format;
|
|
FData := AData;
|
|
FDataOwner := ADataOwner;
|
|
|
|
if FData = nil then
|
|
begin
|
|
w := width;
|
|
h := height;
|
|
if (w <= 0) then
|
|
w := 16;
|
|
if (h <= 0) then
|
|
h := 16;
|
|
ASurface := cairo_image_surface_create(format, w, h);
|
|
try
|
|
FHandle := gdk_pixbuf_get_from_surface(ASurface, 0 ,0, w, h);
|
|
finally
|
|
cairo_surface_destroy(ASurface);
|
|
end;
|
|
gdk_pixbuf_fill(FHandle, 0);
|
|
end else
|
|
begin
|
|
FHandle := TGdkPixbuf.new_from_data(AData, GDK_COLORSPACE_RGB, format=CAIRO_FORMAT_ARGB32, 8, width, height, bytesPerLine, nil, nil);
|
|
end;
|
|
end;
|
|
|
|
destructor TGtk3Image.Destroy;
|
|
begin
|
|
if FHandle <> nil then
|
|
begin
|
|
FHandle^.unref;
|
|
FHandle := nil;
|
|
end;
|
|
if (FDataOwner) and (FData <> nil) then
|
|
FreeMem(FData);
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TGtk3Image.CopyFrom(AImage: PGdkPixbuf; x, y, w, h: integer);
|
|
begin
|
|
if FHandle = nil then
|
|
begin
|
|
DebugLn('*TGtk3Image.CopyFrom create subpixbuf ...');
|
|
FHandle := gdk_pixbuf_new_subpixbuf(AImage, x, y, w, h);
|
|
//TODO: must
|
|
// FHandle := gdk_pixbuf_copy(AImage);
|
|
end else
|
|
begin
|
|
DebugLn('*TGtk3Image.CopyFrom AImage ...');
|
|
g_object_unref(FHandle);
|
|
FHandle := gdk_pixbuf_new_subpixbuf(AImage, x, y, w, h);
|
|
// gdk_pixbuf_copy_area(AImage, x, y, w, h, FHandle, 0, 0);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3Image.height: Integer;
|
|
begin
|
|
Result := FHandle^.get_height;
|
|
end;
|
|
|
|
function TGtk3Image.width: Integer;
|
|
begin
|
|
Result := FHandle^.get_width;
|
|
end;
|
|
|
|
function TGtk3Image.depth: Integer;
|
|
var
|
|
AOption: Pgchar;
|
|
i: Integer;
|
|
begin
|
|
Result := 32;
|
|
AOption := FHandle^.get_option('depth');
|
|
if AOption <> nil then
|
|
begin
|
|
TryStrToInt(StrPas(AOption), Result);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3Image.dotsPerMeterX: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TGtk3Image.dotsPerMeterY: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
function TGtk3Image.bits: PByte;
|
|
begin
|
|
Result := FHandle^.pixels;
|
|
end;
|
|
|
|
function TGtk3Image.numBytes: LongWord;
|
|
begin
|
|
Result := FHandle^.get_byte_length;
|
|
end;
|
|
|
|
function TGtk3Image.bytesPerLine: Integer;
|
|
begin
|
|
Result := FHandle^.rowstride;
|
|
end;
|
|
|
|
function TGtk3Image.getFormat: cairo_format_t;
|
|
begin
|
|
Result := FFormat;
|
|
end;
|
|
|
|
{ TGtk3Pen }
|
|
|
|
function TGtk3Pen.GetColor: TColor;
|
|
begin
|
|
Result := FColor;
|
|
end;
|
|
|
|
function TGtk3Pen.GetWidth: Integer;
|
|
begin
|
|
Result := FWidth;
|
|
end;
|
|
|
|
procedure TGtk3Pen.SetColor(AValue: TColor);
|
|
var
|
|
ARed, AGreen, ABlue: Double;
|
|
begin
|
|
FColor := AValue;
|
|
ColorToCairoRGB(FColor, ARed, AGreen, ABlue);
|
|
if Assigned(FContext) and Assigned(FContext.Widget) then
|
|
cairo_set_source_rgb(FContext.Widget, ARed, AGreen, ABlue);
|
|
end;
|
|
|
|
procedure TGtk3Pen.SetEndCap(AValue: TPenEndCap);
|
|
begin
|
|
FEndCap := AValue;
|
|
end;
|
|
|
|
procedure TGtk3Pen.SetJoinStyle(AValue: TPenJoinStyle);
|
|
begin
|
|
FJoinStyle:=AValue;
|
|
end;
|
|
|
|
procedure TGtk3Pen.SetPenMode(AValue: TPenMode);
|
|
begin
|
|
if FPenMode=AValue then Exit;
|
|
FPenMode:=AValue;
|
|
end;
|
|
|
|
procedure TGtk3Pen.SetStyle(AValue: TFPPenStyle);
|
|
begin
|
|
FStyle := AValue;
|
|
end;
|
|
|
|
constructor TGtk3Pen.Create;
|
|
begin
|
|
inherited Create;
|
|
FillChar(LogPen, SizeOf(LogPen), #0);
|
|
FIsExtPen := False;
|
|
FContext := nil;
|
|
FColor := clBlack;
|
|
FCosmetic := True;
|
|
FWidth := 0;
|
|
FStyle := psSolid;
|
|
FEndCap := pecFlat;
|
|
FJoinStyle := pjsRound;
|
|
FPenMode := pmCopy; // default pen mode
|
|
end;
|
|
|
|
procedure TGtk3Pen.setCosmetic(b: Boolean);
|
|
begin
|
|
FCosmetic := B;
|
|
if Assigned(FContext) and Assigned(FContext.Widget) then
|
|
begin
|
|
if b then
|
|
cairo_set_line_width(FContext.Widget, 0)
|
|
else
|
|
cairo_set_line_width(FContext.Widget, 1);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3Pen.setWidth(p1: Integer);
|
|
begin
|
|
FWidth := p1;
|
|
if Assigned(FContext) then
|
|
cairo_set_line_width(FContext.Widget, p1);
|
|
end;
|
|
|
|
{ TGtk3Brush }
|
|
|
|
function TGtk3Brush.GetColor: TColor;
|
|
begin
|
|
Result := FColor;
|
|
end;
|
|
|
|
procedure TGtk3Brush.SetColor(AValue: TColor);
|
|
var
|
|
ARed, AGreen, ABlue: Double;
|
|
begin
|
|
FColor := AValue;
|
|
ColorToCairoRGB(FColor, ARed, AGreen, ABlue);
|
|
if Assigned(FContext) then
|
|
cairo_set_source_rgb(FContext.Widget, ARed, AGreen, ABlue);
|
|
end;
|
|
|
|
procedure TGtk3Brush.SetStyle(AValue: cardinal);
|
|
begin
|
|
if FStyle=AValue then Exit;
|
|
FStyle:=AValue;
|
|
end;
|
|
|
|
constructor TGtk3Brush.Create;
|
|
begin
|
|
inherited Create;
|
|
{$note IMPORTANT TODO: use cairo_pattern_t for brush }
|
|
// cairo_pattern_create_for_surface();
|
|
FContext := nil;
|
|
FColor := clNone;
|
|
FillChar(LogBrush, SizeOf(TLogBrush), #0);
|
|
end;
|
|
|
|
{ TGtk3DeviceContext }
|
|
|
|
function TGtk3DeviceContext.getBrush: TGtk3Brush;
|
|
begin
|
|
Result := FBrush;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.GetBkMode: Integer;
|
|
begin
|
|
Result := FBkMode;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.GetFont: TGtk3Font;
|
|
begin
|
|
Result := FFont;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.GetOffset: TPoint;
|
|
var
|
|
dx,dy: Double;
|
|
begin
|
|
cairo_surface_get_device_offset(cairo_get_target(Widget), @dx, @dy);
|
|
Result := Point(Round(dx), Round(dy));
|
|
end;
|
|
|
|
function TGtk3DeviceContext.getPen: TGtk3Pen;
|
|
begin
|
|
Result := FPen;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.GetvImage: TGtk3Image;
|
|
begin
|
|
Result := FvImage;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetBkMode(AValue: Integer);
|
|
begin
|
|
FBkMode := AValue;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.setBrush(AValue: TGtk3Brush);
|
|
begin
|
|
if Assigned(FBrush) then
|
|
FBrush.Free;
|
|
FBrush := AValue;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetCurrentTextColor(AValue: TColorRef);
|
|
begin
|
|
if FCurrentTextColor=AValue then Exit;
|
|
FCurrentTextColor:=AValue;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetFont(AValue: TGtk3Font);
|
|
begin
|
|
if Assigned(FFont) then
|
|
FFont.Free;
|
|
FFont := AValue;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetOffset(AValue: TPoint);
|
|
var
|
|
dx, dy: Double;
|
|
begin
|
|
dx := AValue.X;
|
|
dy := AValue.Y;
|
|
cairo_surface_set_device_offset(cairo_get_target(Widget), dx, dy);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.setPen(AValue: TGtk3Pen);
|
|
begin
|
|
if Assigned(FPen) then
|
|
FPen.Free;
|
|
FPen := AValue;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetvImage(AValue: TGtk3Image);
|
|
begin
|
|
if Assigned(FvImage) then
|
|
FvImage.Free;
|
|
FvImage.Free;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.SX(const x: double): Double;
|
|
begin
|
|
Result := 1*(x+vClipRect.Left);
|
|
end;
|
|
|
|
function TGtk3DeviceContext.SY(const y: double): Double;
|
|
begin
|
|
Result := 1*(y+vClipRect.Top);
|
|
end;
|
|
|
|
function TGtk3DeviceContext.SX2(const x: double): Double;
|
|
begin
|
|
Result := x;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.SY2(const y: double): Double;
|
|
begin
|
|
Result := y;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.ApplyBrush;
|
|
begin
|
|
if FBkMode = TRANSPARENT then
|
|
begin
|
|
DebugLn('TGtk3DeviceContext.ApplyBrush setting transparent source');
|
|
//cairo_set_source_surface(Widget, CairoSurface, 0 , 0);
|
|
end else
|
|
SetSourceColor(FCurrentBrush.Color);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.ApplyFont;
|
|
var
|
|
AFont: TGtk3Font;
|
|
begin
|
|
if Assigned(FCurrentFont) then
|
|
AFont := FCurrentFont
|
|
else
|
|
AFont := FFont;
|
|
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.ApplyPen;
|
|
procedure SetDash(d: array of double);
|
|
begin
|
|
cairo_set_dash(Widget, @d, High(d)+1, 0);
|
|
end;
|
|
var
|
|
cap: cairo_line_cap_t;
|
|
w: Double;
|
|
begin
|
|
SetSourceColor(FCurrentPen.Color);
|
|
case FCurrentPen.Mode of
|
|
pmBlack: begin
|
|
SetSourceColor(clBlack);
|
|
cairo_set_operator(Widget, CAIRO_OPERATOR_OVER);
|
|
end;
|
|
pmWhite: begin
|
|
SetSourceColor(clWhite);
|
|
cairo_set_operator(Widget, CAIRO_OPERATOR_OVER);
|
|
end;
|
|
pmCopy: cairo_set_operator(Widget, CAIRO_OPERATOR_OVER);
|
|
pmXor: cairo_set_operator(Widget, CAIRO_OPERATOR_XOR);
|
|
pmNotXor: cairo_set_operator(Widget, CAIRO_OPERATOR_XOR);
|
|
{pmNop,
|
|
pmNot,
|
|
pmCopy,
|
|
pmNotCopy,
|
|
pmMergePenNot,
|
|
pmMaskPenNot,
|
|
pmMergeNotPen,
|
|
pmMaskNotPen,
|
|
pmMerge,
|
|
pmNotMerge,
|
|
pmMask,
|
|
pmNotMask,}
|
|
else
|
|
cairo_set_operator(Widget, CAIRO_OPERATOR_OVER);
|
|
end;
|
|
|
|
if FCurrentPen.Cosmetic then
|
|
cairo_set_line_width(Widget, 1.0)
|
|
else
|
|
begin
|
|
w := FCurrentPen.Width;
|
|
if w = 0 then
|
|
w := 0.5;
|
|
cairo_set_line_width(Widget, w {* ScaleX}); //line_width is diameter of the pen circle
|
|
end;
|
|
|
|
case FCurrentPen.Style of
|
|
psSolid: cairo_set_dash(Widget, nil, 0, 0);
|
|
psDash: SetDash(Dash_Dash);
|
|
psDot: SetDash(Dash_Dot);
|
|
psDashDot: SetDash(Dash_DashDot);
|
|
psDashDotDot: SetDash(Dash_DashDotDot);
|
|
else
|
|
cairo_set_dash(Widget, nil, 0, 0);
|
|
end;
|
|
|
|
case FCurrentPen.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
|
|
case FCurrentPen.Style of
|
|
psDash, psDot, psDashDot, psDashDotDot:
|
|
cap := CAIRO_LINE_CAP_BUTT
|
|
end;
|
|
cairo_set_line_cap(Widget, cap);
|
|
|
|
case FCurrentPen.JoinStyle of
|
|
pjsRound: cairo_set_line_join(Widget, CAIRO_LINE_JOIN_ROUND);
|
|
pjsBevel: cairo_set_line_join(Widget, CAIRO_LINE_JOIN_BEVEL);
|
|
pjsMiter: cairo_set_line_join(Widget, CAIRO_LINE_JOIN_MITER);
|
|
end;
|
|
end;
|
|
|
|
constructor TGtk3DeviceContext.Create(AWidget: PGtkWidget;
|
|
const APaintEvent: Boolean);
|
|
var
|
|
W: gint;
|
|
H: gint;
|
|
ARect: TGdkRectangle;
|
|
AWindow: PGdkWindow;
|
|
x: gint;
|
|
y: gint;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
WriteLn('TGtk3DeviceContext.Create (',
|
|
' WidgetHandle: ', dbghex(PtrInt(AWidget)),
|
|
' FromPaintEvent:',BoolToStr(APaintEvent),' )');
|
|
{$endif}
|
|
inherited Create;
|
|
FvClipRect := Rect(0, 0, 0, 0);
|
|
Window := nil;
|
|
Parent := nil;
|
|
ParentPixmap := nil;
|
|
CairoSurface := nil;
|
|
// FMetrics := nil;
|
|
// SelFont := nil;
|
|
// SelBrush := nil;
|
|
// SelPen := nil;
|
|
FCanRelease := False;
|
|
FOwnsCairo := True;
|
|
FOwnsSurface := False;
|
|
FCurrentTextColor := clBlack;
|
|
|
|
if AWidget = nil then
|
|
begin
|
|
AWindow := gdk_get_default_root_window;
|
|
AWindow^.get_geometry(@x, @y, @w, @h);
|
|
// ParentPixmap := gdk_pixbuf_get_from_window(AWindow, x, y, w, h);
|
|
// Widget := gdk_cairo_create(AWindow);
|
|
// gdk_cairo_set_source_pixbuf(Widget, ParentPixmap, 0, 0);
|
|
CairoSurface := cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
|
|
Widget := cairo_create(CairoSurface);
|
|
ParentPixmap := gdk_pixbuf_get_from_surface(CairoSurface, 0, 0, 1, 1);
|
|
FOwnsSurface := True;
|
|
end else
|
|
begin
|
|
Parent := AWidget;
|
|
if not APaintEvent then
|
|
begin
|
|
{avoid paints on null pixmaps !}
|
|
W := gtk_widget_get_allocated_width(AWidget);
|
|
H := gtk_widget_get_allocated_height(AWidget);
|
|
if W <= 0 then W := 1;
|
|
if H <= 0 then H := 1;
|
|
Widget := gdk_cairo_create(gtk_widget_get_window(AWidget));
|
|
end else
|
|
begin
|
|
W := gtk_widget_get_allocated_width(AWidget);
|
|
H := gtk_widget_get_allocated_height(AWidget);
|
|
if W <= 0 then W := 1;
|
|
if H <= 0 then H := 1;
|
|
Widget := gdk_cairo_create(gtk_widget_get_window(AWidget));
|
|
end;
|
|
end;
|
|
if not FOwnsSurface then
|
|
CairoSurface := cairo_get_target(Widget);
|
|
CreateObjects;
|
|
(*
|
|
FRopMode := R2_COPYPEN;
|
|
FOwnPainter := True;
|
|
CreateObjects;
|
|
FPenPos.X := 0;
|
|
FPenPos.Y := 0;
|
|
*)
|
|
end;
|
|
|
|
constructor TGtk3DeviceContext.Create(AWindow: PGdkWindow;
|
|
const APaintEvent: Boolean);
|
|
var
|
|
x, y, w, h: gint;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
WriteLn('TGtk3DeviceContext.Create (',
|
|
' WindowHandle: ', dbghex(PtrInt(AWindow)),
|
|
' FromPaintEvent:',BoolToStr(APaintEvent),' )');
|
|
{$endif}
|
|
inherited Create;
|
|
FvClipRect := Rect(0, 0, 0, 0);
|
|
Parent := nil;
|
|
ParentPixmap := nil;
|
|
CairoSurface := nil;
|
|
Window := AWindow;
|
|
FOwnsSurface := False;
|
|
FCanRelease := False;
|
|
FOwnsCairo := True;
|
|
FCurrentTextColor := clBlack;
|
|
AWindow^.get_geometry(@x, @y, @w, @h);
|
|
// ParentPixmap := gdk_pixbuf_get_from_window(AWindow, x, y, w, h);
|
|
Widget := gdk_cairo_create(AWindow);
|
|
// gdk_cairo_set_source_pixbuf(Widget, ParentPixmap, 0, 0);
|
|
gdk_cairo_set_source_window(Widget, AWindow, 0, 0);
|
|
CairoSurface := cairo_get_target(Widget);
|
|
CreateObjects;
|
|
end;
|
|
|
|
constructor TGtk3DeviceContext.CreateFromCairo(AWidget: PGtkWidget;
|
|
ACairo: PCairo_t);
|
|
var
|
|
AGdkRect: TGdkRectangle;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
WriteLn('TGtk3DeviceContext.CreateFromCairo (',
|
|
' WidgetHandle: ', dbghex(PtrInt(AWidget)),
|
|
' FromPaintEvent:',BoolToStr(True),' )');
|
|
{$endif}
|
|
inherited Create;
|
|
FOwnsCairo := False;
|
|
Window := nil;
|
|
Parent := AWidget;
|
|
ParentPixmap := nil;
|
|
CairoSurface := nil;
|
|
FOwnsSurface := False;
|
|
FCurrentTextColor := clBlack;
|
|
gdk_cairo_get_clip_rectangle(ACairo, @AGdkRect);
|
|
FvClipRect := RectFromGdkRect(AGdkRect);
|
|
Widget := ACairo;
|
|
CairoSurface := cairo_get_target(Widget);
|
|
CreateObjects;
|
|
end;
|
|
|
|
destructor TGtk3DeviceContext.Destroy;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
WriteLn('TGtk3DeviceContext.Destroy ',dbgHex(PtrUInt(Self)));
|
|
{$endif}
|
|
DeleteObjects;
|
|
if FOwnsCairo and (Widget <> nil) then
|
|
cairo_destroy(Widget);
|
|
if (ParentPixmap <> nil) then
|
|
g_object_unref(ParentPixmap);
|
|
if FOwnsSurface and (CairoSurface <> nil) then
|
|
cairo_surface_destroy(CairoSurface);
|
|
Parent := nil;
|
|
Widget := nil;
|
|
ParentPixmap := nil;
|
|
CairoSurface := nil;
|
|
Window := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.CreateObjects;
|
|
var
|
|
Matrix:cairo_matrix_t;
|
|
begin
|
|
FBkMode := TRANSPARENT;
|
|
FCurrentImage := nil;
|
|
FCurrentRegion := nil;
|
|
FBrush := TGtk3Brush.Create;
|
|
FBrush.Context := Self;
|
|
FBrush.Color := clNone;
|
|
FBrush.Style := BS_SOLID;
|
|
FPen := TGtk3Pen.Create;
|
|
FPen.Context := Self;
|
|
FPen.Color := clBlack;
|
|
FCurrentPen := FPen;
|
|
FCurrentBrush := FBrush;
|
|
FFont := TGtk3Font.Create(Widget, Parent);
|
|
FCurrentFont := FFont;
|
|
FvImage := TGtk3Image.Create(nil, 1, 1, 8, CAIRO_FORMAT_ARGB32);
|
|
FCurrentImage := FvImage;
|
|
|
|
cairo_get_matrix(Widget, @Matrix);
|
|
// widget with menu or other non-client exclusions have offset in trasform matrix
|
|
fncOrigin:=Point(round(Matrix.x0),round(Matrix.y0));
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.DeleteObjects;
|
|
begin
|
|
if Assigned(FBrush) then
|
|
FreeAndNil(FBrush);
|
|
if Assigned(FPen) then
|
|
FreeAndNil(FPen);
|
|
if Assigned(FFont) then
|
|
FreeAndNil(FFont);
|
|
if Assigned(FvImage) then
|
|
FreeAndNil(FvImage);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawPoint(x1: Integer; y1: Integer);
|
|
begin
|
|
applyPen;
|
|
cairo_move_to(Widget , x1, y1);
|
|
cairo_line_to(Widget, x1, y1);
|
|
cairo_stroke(Widget);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawRect(x1: Integer; y1: Integer; w: Integer;
|
|
h: Integer; const AFill: Boolean);
|
|
begin
|
|
cairo_save(Widget);
|
|
try
|
|
applyPen;
|
|
// strange about adding +1 -1 to rectangle, but this works ok.
|
|
//cairo_rectangle(Widget, x1 + 1, y1 + 1, w - 1, h -1);
|
|
cairo_rectangle(Widget, x1, y1, w, h);
|
|
if AFill then
|
|
begin
|
|
cairo_stroke_preserve(Widget);
|
|
applyBrush;
|
|
cairo_fill_preserve(Widget);
|
|
end else
|
|
cairo_stroke(Widget);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawRoundRect(x, y, w, h, rx, ry: Integer);
|
|
begin
|
|
RoundRect(x, y, w, h, rx, ry);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawText(x: Integer; y: Integer; const s: String);
|
|
var
|
|
e: cairo_font_extents_t;
|
|
R: Double;
|
|
G: Double;
|
|
B: Double;
|
|
begin
|
|
cairo_save(Widget);
|
|
try
|
|
// TranslateCairoToDevice;
|
|
// cairo_surface_get_device_offset(CairoSurface, @dx, @dy);
|
|
cairo_font_extents(Widget, @e);
|
|
if e.ascent <> 0 then
|
|
begin
|
|
// writeln('EXTENTS !!!! ',Format('%2.2n',[e.ascent]));
|
|
end;
|
|
cairo_move_to(Widget, x, y {+ e.ascent});
|
|
// writeln('DevOffset ',Format('dx %2.2n dy %2.2n x %d y %d text %s',
|
|
// [dx, dy, x, y, s]));
|
|
// pango_renderer_activate();
|
|
// pango_cairo_show_layout(Widget, Layout);
|
|
ColorToCairoRGB(TColor(CurrentTextColor), R, G , B);
|
|
cairo_set_source_rgb(Widget, R, G, B);
|
|
// writeln('DRAWINGTEXT ',S,' WITH R=',dbgs(R),' G=',dbgs(G),' B=',dbgs(B));
|
|
FCurrentFont.Layout^.set_text(PChar(S), length(S));
|
|
// writeln('Family: ',FCurrentFont.Handle^.get_family,' size ',FCurrentFont.Handle^.get_size,' weight ',FCurrentFont.Handle^.get_weight);
|
|
pango_cairo_show_layout(Widget, FCurrentFont.Layout);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawText(x, y, w, h, flags: Integer; const s: String
|
|
);
|
|
var
|
|
e: cairo_font_extents_t;
|
|
R: Double;
|
|
G: Double;
|
|
B: Double;
|
|
// dx, dy: Double;
|
|
begin
|
|
cairo_save(Widget);
|
|
try
|
|
// TranslateCairoToDevice;
|
|
// cairo_surface_get_device_offset(CairoSurface, @dx, @dy);
|
|
cairo_font_extents(Widget, @e);
|
|
if e.ascent <> 0 then
|
|
begin
|
|
// writeln('2.EXTENTS !!!! ',Format('%2.2n',[e.ascent]));
|
|
end;
|
|
cairo_move_to(Widget, x, y + e.ascent);
|
|
ColorToCairoRGB(CurrentTextColor, R, G , B);
|
|
cairo_set_source_rgb(Widget, R, G, B);
|
|
// cairo_show_text(Widget, PChar(s));
|
|
FCurrentFont.Layout^.set_text(PChar(S), length(S));
|
|
pango_cairo_show_layout(Widget, FCurrentFont.Layout);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawLine(x1: Integer; y1: Integer; x2: Integer;
|
|
y2: Integer);
|
|
begin
|
|
ApplyPen;
|
|
cairo_move_to(Widget, x1, y1);
|
|
cairo_line_to(Widget, x2, y2);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawEllipse(x: Integer; y: Integer; w: Integer;
|
|
h: Integer);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawSurface(targetRect: PRect;
|
|
Surface: Pcairo_surface_t; sourceRect: PRect; mask: PGdkPixBuf;
|
|
maskRect: PRect);
|
|
var
|
|
M: cairo_matrix_t;
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3DeviceContext.DrawSurface ');
|
|
{$ENDIF}
|
|
cairo_save(Widget);
|
|
try
|
|
with targetRect^ do
|
|
cairo_rectangle(Widget, Left, Top, Right - Left, Bottom - Top);
|
|
cairo_set_source_surface(Widget, Surface, 0, 0);
|
|
cairo_matrix_init_identity(@M);
|
|
cairo_matrix_translate(@M, SourceRect^.Left, SourceRect^.Top);
|
|
cairo_matrix_scale(@M, (sourceRect^.Right-sourceRect^.Left) / (targetRect^.Right-targetRect^.Left),
|
|
(sourceRect^.Bottom-sourceRect^.Top) / (targetRect^.Bottom-targetRect^.Top));
|
|
cairo_matrix_translate(@M, -targetRect^.Left, -targetRect^.Top);
|
|
cairo_pattern_set_matrix(cairo_get_source(Widget), @M);
|
|
cairo_clip(Widget);
|
|
cairo_paint(Widget);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawImage(targetRect: PRect; image: PGdkPixBuf;
|
|
sourceRect: PRect; mask: PGdkPixBuf; maskRect: PRect);
|
|
var
|
|
pm: PGdkPixbuf;
|
|
AData: PByte;
|
|
ASurface: Pcairo_surface_t;
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3DeviceContext.DrawImage ');
|
|
{$ENDIF}
|
|
cairo_save(Widget);
|
|
try
|
|
pm := Image;
|
|
// AData := PByte(gdk_pixbuf_get_pixels(pm));
|
|
// ASurface := cairo_image_surface_create_for_data(AData, CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(pm), gdk_pixbuf_get_height(pm), gdk_pixbuf_get_rowstride(pm));
|
|
// cairo_set_source_surface(Widget, ASurface, targetRect^.Left, targetRect^.Top);
|
|
gdk_cairo_set_source_pixbuf(Widget, Image, 0, 0);
|
|
cairo_paint(Widget);
|
|
finally
|
|
// cairo_surface_destroy(ASurface);
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawPixmap(p: PPoint; pm: PGdkPixbuf; sr: PRect);
|
|
var
|
|
AImage: PGtkImage;
|
|
ASurface: Pcairo_surface_t;
|
|
AData: PByte;
|
|
begin
|
|
{$IFDEF VerboseGtk3DeviceContext}
|
|
DebugLn('TGtk3DeviceContext.DrawPixmap ');
|
|
{$ENDIF}
|
|
cairo_save(Widget);
|
|
try
|
|
AData := PByte(gdk_pixbuf_get_pixels(pm));
|
|
ASurface := cairo_image_surface_create_for_data(AData, CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(pm), gdk_pixbuf_get_height(pm), gdk_pixbuf_get_rowstride(pm));
|
|
cairo_set_source_surface(Widget, ASurface, sr^.Left, sr^.Top);
|
|
cairo_paint(Widget);
|
|
finally
|
|
cairo_surface_destroy(ASurface);
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawPolyLine(P: PPoint; NumPts: Integer);
|
|
const
|
|
PixelOffset = 0.5;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
cairo_save(Widget);
|
|
try
|
|
ApplyPen;
|
|
cairo_move_to(Widget, P[0].X+PixelOffset, P[0].Y+PixelOffset);
|
|
for i := 1 to NumPts-1 do
|
|
cairo_line_to(Widget, P[i].X+PixelOffset, P[i].Y+PixelOffset);
|
|
cairo_stroke(Widget);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawPolygon(P: PPoint; NumPts: Integer;
|
|
FillRule: integer);
|
|
var
|
|
i: Integer;
|
|
const
|
|
PixelOffset = 0.5;
|
|
begin
|
|
cairo_save(Widget);
|
|
try
|
|
// first apply the fill because the line is drawn over the filled area after
|
|
applyBrush;
|
|
cairo_set_fill_rule(Widget, cairo_fill_rule_t(FillRule));
|
|
// + Offset is so the center of the pixel is used.
|
|
cairo_move_to(Widget, P[0].X+PixelOffset, P[0].Y+PixelOffset);
|
|
for i := 1 to NumPts-1 do
|
|
cairo_line_to(Widget, P[i].X+PixelOffset, P[i].Y+PixelOffset);
|
|
|
|
cairo_close_path(Widget);
|
|
cairo_fill_preserve(Widget);
|
|
|
|
// now draw the line
|
|
ApplyPen;
|
|
//cairo_set_antialias(widget, CAIRO_ANTIALIAS_SUBPIXEL);
|
|
cairo_move_to(Widget, P[0].X+PixelOffset, P[0].Y+PixelOffset);
|
|
for i := 1 to NumPts-1 do
|
|
cairo_line_to(Widget, P[i].X+PixelOffset, P[i].Y+PixelOffset);
|
|
cairo_close_path(Widget);
|
|
cairo_stroke_preserve(Widget);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.drawPolyBezier(P: PPoint; NumPoints: Integer; Filled, Continuous: boolean);
|
|
var
|
|
i: Integer;
|
|
const
|
|
PixelOffset = 0.5;
|
|
begin
|
|
// 3 points per curve + a starting point for the first curve
|
|
if (NumPoints < 4) then
|
|
Exit;
|
|
|
|
cairo_save(Widget);
|
|
try
|
|
ApplyPen;
|
|
|
|
i := 0;
|
|
// we need 3 points left for continuous and 4 for not continous
|
|
while i < NumPoints-1 - (3 + ord(not Continuous)) do
|
|
begin
|
|
if (i = 0) or Not Continuous then
|
|
begin
|
|
cairo_move_to(Widget, P[i].X+PixelOffset, P[i].Y+PixelOffset); // start point
|
|
Inc(i);
|
|
end;
|
|
cairo_curve_to(Widget,
|
|
P[i].X+PixelOffset, P[i].Y+PixelOffset, // control point 1
|
|
P[i+1].X+PixelOffset, P[i+1].Y+PixelOffset, // control point 2
|
|
P[i+2].X+PixelOffset, P[i+2].Y+PixelOffset); // end point and start point of next
|
|
Inc(i, 3);
|
|
end;
|
|
cairo_stroke_preserve(Widget);
|
|
|
|
if Filled then
|
|
begin
|
|
ApplyBrush;
|
|
// join start and end points
|
|
cairo_close_path(Widget);
|
|
cairo_fill(Widget);
|
|
end;
|
|
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.eraseRect(ARect: PRect);
|
|
begin
|
|
// cairo_surface_
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.fillRect(ARect: PRect; ABrush: HBRUSH);
|
|
begin
|
|
with ARect^ do
|
|
fillRect(Left, Top, Right - Left, Bottom - Top, ABrush);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.fillRect(x, y, w, h: Integer; ABrush: HBRUSH);
|
|
var
|
|
devx, devy, dx, dy, dw, dh: Double;
|
|
ATarget: Pcairo_surface_t;
|
|
ANewSurface: Pcairo_surface_t;
|
|
ACairo: Pcairo_t;
|
|
ATempBrush: TGtk3Brush;
|
|
begin
|
|
{$ifdef VerboseGtk3DeviceContext}
|
|
// WriteLn('TGtk3DeviceContext.fillRect ',Format('x %d y %d w %d h %d',[x, y, w, h]));
|
|
{$endif}
|
|
|
|
cairo_save(Widget);
|
|
ATempBrush := nil;
|
|
if ABrush <> 0 then
|
|
begin
|
|
ATempBrush := FCurrentBrush;
|
|
fBkMode:=OPAQUE;
|
|
SetCurrentBrush(TGtk3Brush(ABrush));
|
|
end;
|
|
|
|
applyBrush;
|
|
cairo_rectangle(Widget, x, y, w, h);
|
|
cairo_stroke_preserve(Widget);
|
|
cairo_fill(Widget);
|
|
// cairo_clip(Widget);
|
|
|
|
// cairo_fill_preserve(Widget);
|
|
if ABrush <> 0 then
|
|
SetCurrentBrush(ATempBrush);
|
|
cairo_restore(Widget);
|
|
|
|
// ATarget := cairo_get_target(Widget);
|
|
(*
|
|
cairo_save(Widget);
|
|
dx := x;
|
|
dy := y;
|
|
dw := w;
|
|
dh := h;
|
|
ANewSurface := cairo_surface_create_similar(ATarget, cairo_surface_get_content(ATarget), w, h);
|
|
cairo_set_source_surface(Widget, ANewSurface, x , y);
|
|
cairo_clip(Widget);
|
|
vBrush.SetColor(clRed);
|
|
cairo_rectangle(Widget, dx, dy, dw, dh);
|
|
cairo_fill(Widget);
|
|
cairo_surface_destroy(ANewSurface);
|
|
cairo_restore(Widget);
|
|
*)
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.fillRect(x, y, w, h: Integer);
|
|
begin
|
|
fillRect(x, y, w, h , 0);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.FillAndStroke;
|
|
begin
|
|
if Assigned(FCurrentBrush) and (FCurrentBrush.Style <> BS_NULL) then
|
|
begin
|
|
ApplyBrush;
|
|
if Assigned(FCurrentPen) and (FCurrentPen.Style = psClear) then
|
|
cairo_fill(Widget)
|
|
else
|
|
cairo_fill_preserve(Widget);
|
|
end;
|
|
if Assigned(FCurrentPen) and (FCurrentPen.Style <> psClear) then
|
|
begin
|
|
ApplyPen;
|
|
cairo_stroke(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.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(Widget);
|
|
try
|
|
cairo_translate(Widget, SX(CX), SY(CY));
|
|
cairo_scale(Widget, SX2(RX), SY2(RY));
|
|
if not Continuous then
|
|
cairo_move_to(Widget, cos(Angle1), sin(Angle1)); //Move to arcs starting point
|
|
if Clockwise then
|
|
cairo_arc(Widget, 0, 0, 1, Angle1, Angle2)
|
|
else
|
|
cairo_arc_negative(Widget, 0, 0, 1, Angle1, Angle2);
|
|
finally
|
|
cairo_restore(Widget);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.RoundRect(X1, Y1, X2, Y2: Integer; RX, RY: Integer
|
|
): Boolean;
|
|
var
|
|
DX: Double;
|
|
DY: Double;
|
|
Pt: TPoint;
|
|
begin
|
|
Result := False;
|
|
cairo_surface_get_device_offset(cairo_get_target(Widget), @DX, @DY);
|
|
cairo_translate(Widget, DX, DY);
|
|
try
|
|
cairo_move_to(Widget, SX(X1+RX), SY(Y1));
|
|
cairo_line_to(Widget, SX(X2-RX), SY(Y1));
|
|
EllipseArcPath(X2-RX, Y1+RY, RX, RY, -PI/2, 0, True, True);
|
|
cairo_line_to(Widget, SX(X2), SY(Y2-RY));
|
|
EllipseArcPath(X2-RX, Y2-RY, RX, RY, 0, PI/2, True, True);
|
|
cairo_line_to(Widget, SX(X1+RX), SY(Y2));
|
|
EllipseArcPath(X1+RX, Y2-RY, RX, RY, PI/2, PI, True, True);
|
|
cairo_line_to(Widget, SX(X1), SY(Y1+RX));
|
|
EllipseArcPath(X1+RX, Y1+RY, RX, RY, PI, PI*1.5, True, True);
|
|
FillAndStroke;
|
|
Result := True;
|
|
finally
|
|
cairo_translate(Widget, -DX, -DY);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.getBpp: integer;
|
|
var
|
|
AVisual: PGdkVisual;
|
|
begin
|
|
if (Parent <> nil) and (Parent^.get_has_window) then
|
|
begin
|
|
AVisual := gdk_window_get_visual(Parent^.get_window);
|
|
Result := gdk_visual_get_bits_per_rgb(AVisual);
|
|
g_object_unref(AVisual);
|
|
end else
|
|
if (ParentPixmap <> nil) and (Parent = nil) then
|
|
begin
|
|
Result := ParentPixmap^.get_bits_per_sample;
|
|
end else
|
|
begin
|
|
AVisual := gdk_window_get_visual(gdk_get_default_root_window);
|
|
Result := gdk_visual_get_bits_per_rgb(AVisual);
|
|
g_object_unref(AVisual);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.getDepth: integer;
|
|
var
|
|
AVisual: PGdkVisual;
|
|
begin
|
|
Result := 0;
|
|
if (Parent <> nil) and Gtk3IsGdkWindow(Parent^.get_window) then
|
|
begin
|
|
AVisual := gdk_window_get_visual(Parent^.get_window);
|
|
if Gtk3IsGdkVisual(AVisual) then
|
|
begin
|
|
Result := gdk_visual_get_depth(AVisual);
|
|
exit;
|
|
end;
|
|
end;
|
|
AVisual := gdk_window_get_visual(gdk_get_default_root_window);
|
|
if Gtk3IsGdkVisual(AVisual) then
|
|
begin
|
|
Result := gdk_visual_get_depth(AVisual);
|
|
end;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.getDeviceSize: TPoint;
|
|
begin
|
|
Result := Point(0 , 0);
|
|
if Parent <> nil then
|
|
begin
|
|
Result.y := Parent^.get_allocated_height;
|
|
Result.x := Parent^.get_allocated_width;
|
|
end else
|
|
if ParentPixmap <> nil then
|
|
begin
|
|
Result.y := ParentPixmap^.height;
|
|
Result.x := ParentPixmap^.width;
|
|
end else
|
|
if Gtk3IsGdkWindow(Window) then
|
|
begin
|
|
Result.X := Window^.get_width;
|
|
Result.y := Window^.get_height;
|
|
end;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.LineTo(const X, Y: Integer): Boolean;
|
|
begin
|
|
if not Assigned(Widget) then
|
|
exit(False);
|
|
ApplyPen;
|
|
cairo_line_to(Widget, X, Y);
|
|
cairo_stroke(Widget);
|
|
Result := True;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.MoveTo(const X, Y: Integer; OldPoint: PPoint
|
|
): Boolean;
|
|
var
|
|
dx: Double;
|
|
dy: Double;
|
|
begin
|
|
if not Assigned(Widget) then
|
|
exit(False);
|
|
if OldPoint <> nil then
|
|
begin
|
|
cairo_get_current_point(Widget, @dx, @dy);
|
|
OldPoint^.X := Round(dx);
|
|
OldPoint^.Y := Round(dy);
|
|
end;
|
|
cairo_move_to(Widget, X, Y);
|
|
Result := True;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.SetClipRegion(ARgn: TGtk3Region): Integer;
|
|
begin
|
|
Result := SimpleRegion;
|
|
if Assigned(Widget) then
|
|
begin
|
|
cairo_reset_clip(Widget);
|
|
gdk_cairo_region(Self.Widget, ARgn.FHandle);
|
|
cairo_clip(Widget);
|
|
end;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetSourceColor(AColor: TColor);
|
|
var
|
|
R, G, B: double;
|
|
begin
|
|
TColorToRGB(AColor, R, G, B);
|
|
cairo_set_source_rgb(Widget, R, G, B);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetCurrentBrush(ABrush: TGtk3Brush);
|
|
begin
|
|
FCurrentBrush := ABrush;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetCurrentFont(AFont: TGtk3Font);
|
|
begin
|
|
FCurrentFont := AFont;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetCurrentPen(APen: TGtk3Pen);
|
|
begin
|
|
FCurrentPen := APen;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetCurrentImage(AImage: TGtk3Image);
|
|
begin
|
|
FCurrentImage := AImage;
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.SetImage(AImage: TGtk3Image);
|
|
var
|
|
APixBuf: PGdkPixbuf;
|
|
begin
|
|
FCurrentImage := AImage;
|
|
cairo_destroy(Widget);
|
|
APixBuf := AImage.Handle;
|
|
if not Gtk3IsGdkPixbuf(APixBuf) then
|
|
begin
|
|
DebugLn('ERROR: TGtk3DeviceContext.SetImage image handle isn''t PGdkPixbuf.');
|
|
exit;
|
|
end;
|
|
(*
|
|
DebugLn('TGtk3DeviceContext.SetImage w=',dbgs(APixBuf^.width),' h=',dbgs(APixBuf^.height),
|
|
' RowStride ',dbgs(APixBuf^.rowstride),' BPS=',dbgs(APixBuf^.get_bits_per_sample),
|
|
' BLEN ',dbgs(APixbuf^.get_byte_length),' channels ',dbgs(APixBuf^.get_n_channels),
|
|
' ALPHA ',dbgs(APixbuf^.get_has_alpha));
|
|
*)
|
|
if FOwnsSurface and (CairoSurface <> nil) then
|
|
cairo_surface_destroy(CairoSurface);
|
|
CairoSurface := cairo_image_surface_create_for_data(APixBuf^.pixels,
|
|
AImage.getFormat,
|
|
APixBuf^.get_width,
|
|
APixBuf^.get_height,
|
|
APixBuf^.rowstride);
|
|
Widget := cairo_create(CairoSurface);
|
|
FOwnsSurface := true;
|
|
end;
|
|
|
|
function TGtk3DeviceContext.ResetClip: Integer;
|
|
begin
|
|
Result := NullRegion;
|
|
if Assigned(Widget) then
|
|
cairo_reset_clip(Widget);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.TranslateCairoToDevice;
|
|
var
|
|
Pt: TPoint;
|
|
begin
|
|
Pt := Offset;
|
|
Translate(Pt);
|
|
end;
|
|
|
|
procedure TGtk3DeviceContext.Translate(APoint: TPoint);
|
|
begin
|
|
cairo_translate(Widget, APoint.X, APoint.Y);
|
|
end;
|
|
|
|
|
|
//various routines for text , copied from gtk2.
|
|
|
|
{-------------------------------------------------------------------------------
|
|
function RemoveAmpersands(Src: PChar; LineLength : Longint) : PChar;
|
|
|
|
Creates a new PChar removing all escaping ampersands.
|
|
-------------------------------------------------------------------------------}
|
|
function RemoveAmpersands(Src: PChar; LineLength : Longint) : PChar;
|
|
var
|
|
i, j: Longint;
|
|
ShortenChars, NewLength, SrcLength: integer;
|
|
begin
|
|
// count ampersands and find first ampersand
|
|
ShortenChars:= 0; // chars to delete
|
|
SrcLength:= LineLength;
|
|
|
|
{ Look for amperands. If found, check if it is an escaped ampersand.
|
|
If it is, don't count it in. }
|
|
i:=0;
|
|
while i<SrcLength do
|
|
begin
|
|
if Src[i] = '&' then
|
|
begin
|
|
if (i < SrcLength - 1) and (Src[i+1] = '&') then
|
|
begin
|
|
// escaping ampersand found
|
|
inc(ShortenChars);
|
|
inc(i,2);
|
|
Continue;
|
|
end
|
|
else
|
|
inc(ShortenChars);
|
|
end;
|
|
inc(i);
|
|
end;
|
|
// create new PChar
|
|
NewLength:= SrcLength - ShortenChars;
|
|
|
|
Result:=StrAlloc(NewLength+1); // +1 for #0 char at end
|
|
|
|
// copy string without ampersands
|
|
i:=0;
|
|
j:=0;
|
|
while (j < NewLength) do begin
|
|
if Src[i] <> '&' then begin
|
|
// copy normal char
|
|
Result[j]:= Src[i];
|
|
end else begin
|
|
// ampersand
|
|
if (i < (SrcLength - 1)) and (Src[i+1] = '&') then begin
|
|
// escaping ampersand found
|
|
inc(i);
|
|
Result[j]:='&';
|
|
end else
|
|
// delete single ampersand
|
|
dec(j);
|
|
end;
|
|
Inc(i);
|
|
Inc(j);
|
|
end;
|
|
Result[NewLength]:=#0;
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
function GetTextExtentIgnoringAmpersands(TheFont: PGDKFont;
|
|
Str : PChar; StrLength: integer;
|
|
MaxWidth: Longint; lbearing, rbearing, width, ascent, descent : Pgint);
|
|
|
|
Gets text extent of a string, ignoring escaped Ampersands.
|
|
That means, ampersands are not counted.
|
|
Negative MaxWidth means no limit.
|
|
-------------------------------------------------------------------------------}
|
|
procedure GetTextExtentIgnoringAmpersands(TheFont: TGtk3Font;
|
|
Str : PChar; StrLength: integer;
|
|
lbearing, rbearing, width, ascent, descent : Pgint);
|
|
var
|
|
NewStr : PChar;
|
|
i: integer;
|
|
AInkRect: TPangoRectangle;
|
|
ALogicalRect: TPangoRectangle;
|
|
AMetrics: PPangoFontMetrics;
|
|
ACharWidth: gint;
|
|
begin
|
|
NewStr:=Str;
|
|
// first check if Str contains an ampersand:
|
|
if (Str<>nil) then
|
|
begin
|
|
i:=0;
|
|
while (Str[i]<>'&') and (i<StrLength) do inc(i);
|
|
if i<StrLength then
|
|
begin
|
|
NewStr := RemoveAmpersands(Str, StrLength);
|
|
StrLength:=StrLen(NewStr);
|
|
end;
|
|
end;
|
|
TheFont.Layout^.set_text(Str, StrLength);
|
|
|
|
// TheFont.Layout^.get_extents(@AInkRect, @ALogicalRect);
|
|
|
|
AMetrics := pango_context_get_metrics(TheFont.Layout^.get_context, TheFont.Handle, TheFont.Layout^.get_context^.get_language);
|
|
// if not Gtk3IsPangoFontMetrics(PGObject(AMetrics)) then
|
|
// exit;
|
|
if AMetrics = nil then
|
|
begin
|
|
Debugln('WARNING: GetTextExtentIgnoringAmpersands AMetrics=nil');
|
|
exit;
|
|
end;
|
|
|
|
if ascent <> nil then
|
|
ascent^ := AMetrics^.get_ascent;
|
|
if descent <> nil then
|
|
descent^ := AMetrics^.get_descent;
|
|
if width <> nil then
|
|
begin
|
|
ACharWidth := AMetrics^.get_approximate_char_width;
|
|
width^ := (StrLength * ACharWidth) div PANGO_SCALE;
|
|
end;
|
|
// PANGO_PIXELS(char_width)
|
|
|
|
// lBearing^ := 0;
|
|
// rBearing^ := 0;
|
|
// gdk_text_extents(TheFont, NewStr, StrLength,
|
|
// lbearing, rBearing, width, ascent, descent);
|
|
if NewStr<>Str then
|
|
StrDispose(NewStr);
|
|
AMetrics^.unref;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
procedure Gtk3WordWrap(DC: HDC; AText: PChar; MaxWidthInPixel: integer;
|
|
var Lines: PPChar; var LineCount: integer); virtual;
|
|
|
|
Breaks AText into several lines and creates a list of PChar. The last entry
|
|
will be nil.
|
|
Lines break at new line chars and at spaces if a line is longer than
|
|
MaxWidthInPixel or in a word.
|
|
Lines will be one memory block so that you can free the list and all lines
|
|
with FreeMem(Lines).
|
|
------------------------------------------------------------------------------}
|
|
procedure Gtk3WordWrap(DC: HDC; AText: PChar;
|
|
MaxWidthInPixel: integer; out Lines: PPChar; out LineCount: integer);
|
|
var
|
|
UseFont: TGtk3Font;
|
|
|
|
function GetLineWidthInPixel(LineStart, LineLen: integer): integer;
|
|
var
|
|
width: LongInt;
|
|
begin
|
|
GetTextExtentIgnoringAmpersands(UseFont, @AText[LineStart], LineLen,
|
|
nil, nil, @width, nil, nil);
|
|
Result := Width;
|
|
end;
|
|
|
|
function FindLineEnd(LineStart: integer): integer;
|
|
var
|
|
CharLen,
|
|
LineStop,
|
|
LineWidth, WordWidth, WordEnd, CharWidth: integer;
|
|
begin
|
|
// first search line break or text break
|
|
Result:=LineStart;
|
|
while not (AText[Result] in [#0,#10,#13]) do inc(Result);
|
|
if Result<=LineStart+1 then exit;
|
|
lineStop:=Result;
|
|
|
|
// get current line width in pixel
|
|
LineWidth:=GetLineWidthInPixel(LineStart,Result-LineStart);
|
|
if LineWidth>MaxWidthInPixel then
|
|
begin
|
|
// line too long
|
|
// -> add words till line size reached
|
|
LineWidth:=0;
|
|
WordEnd:=LineStart;
|
|
WordWidth:=0;
|
|
repeat
|
|
Result:=WordEnd;
|
|
inc(LineWidth,WordWidth);
|
|
// find word start
|
|
while AText[WordEnd] in [' ',#9] do inc(WordEnd);
|
|
// find word end
|
|
while not (AText[WordEnd] in [#0,' ',#9,#10,#13]) do inc(WordEnd);
|
|
// calculate word width
|
|
WordWidth:=GetLineWidthInPixel(Result,WordEnd-Result);
|
|
until LineWidth+WordWidth>MaxWidthInPixel;
|
|
if LineWidth=0 then
|
|
begin
|
|
// the first word is longer than the maximum width
|
|
// -> add chars till line size reached
|
|
Result:=LineStart;
|
|
LineWidth:=0;
|
|
repeat
|
|
charLen:=UTF8CodepointSize(@AText[result]);
|
|
CharWidth:=GetLineWidthInPixel(Result,charLen);
|
|
inc(LineWidth,CharWidth);
|
|
if LineWidth>MaxWidthInPixel then break;
|
|
if result>=lineStop then break;
|
|
inc(Result,charLen);
|
|
until false;
|
|
// at least one char
|
|
if Result=LineStart then begin
|
|
charLen:=UTF8CodepointSize(@AText[result]);
|
|
inc(Result,charLen);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function IsEmptyText: boolean;
|
|
begin
|
|
if (AText=nil) or (AText[0]=#0) then
|
|
begin
|
|
// no text
|
|
GetMem(Lines,SizeOf(PChar));
|
|
Lines[0]:=nil;
|
|
LineCount:=0;
|
|
Result:=true;
|
|
end else
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure InitFont;
|
|
begin
|
|
UseFont := TGtk3DeviceContext(DC).CurrentFont;
|
|
end;
|
|
|
|
var
|
|
LinesList: TIntegerList;
|
|
LineStart, LineEnd, LineLen: integer;
|
|
ArraySize, TotalSize: integer;
|
|
i: integer;
|
|
CurLineEntry: PPChar;
|
|
CurLineStart: PChar;
|
|
begin
|
|
if IsEmptyText then
|
|
begin
|
|
Lines:=nil;
|
|
LineCount:=0;
|
|
exit;
|
|
end;
|
|
InitFont;
|
|
LinesList:=TIntegerList.Create;
|
|
LineStart:=0;
|
|
|
|
// find all line starts and line ends
|
|
repeat
|
|
LinesList.Add(LineStart);
|
|
// find line end
|
|
LineEnd:=FindLineEnd(LineStart);
|
|
LinesList.Add(LineEnd);
|
|
// find next line start
|
|
LineStart:=LineEnd;
|
|
if AText[LineStart] in [#10,#13] then
|
|
begin
|
|
// skip new line chars
|
|
inc(LineStart);
|
|
if (AText[LineStart] in [#10,#13])
|
|
and (AText[LineStart]<>AText[LineStart-1]) then
|
|
inc(LineStart);
|
|
end else
|
|
if AText[LineStart] in [' ',#9] then
|
|
begin
|
|
// skip space
|
|
while AText[LineStart] in [' ',#9] do
|
|
inc(LineStart);
|
|
end;
|
|
until AText[LineStart]=#0;
|
|
|
|
// create mem block for 'Lines': array of PChar + all lines
|
|
LineCount:=LinesList.Count shr 1;
|
|
ArraySize:=(LineCount+1)*SizeOf(PChar);
|
|
TotalSize:=ArraySize;
|
|
i:=0;
|
|
while i<LinesList.Count do
|
|
begin
|
|
// add LineEnd - LineStart + 1 for the #0
|
|
LineLen:=LinesList[i+1]-LinesList[i]+1;
|
|
inc(TotalSize,LineLen);
|
|
inc(i,2);
|
|
end;
|
|
GetMem(Lines,TotalSize);
|
|
FillChar(Lines^,TotalSize,0);
|
|
|
|
// create Lines
|
|
CurLineEntry:=Lines;
|
|
CurLineStart:=PChar(CurLineEntry)+ArraySize;
|
|
i:=0;
|
|
while i<LinesList.Count do
|
|
begin
|
|
// set the pointer to the start of the current line
|
|
CurLineEntry[i shr 1]:=CurLineStart;
|
|
// copy the line
|
|
LineStart:=LinesList[i];
|
|
LineEnd:=LinesList[i+1];
|
|
LineLen:=LineEnd-LineStart;
|
|
if LineLen>0 then
|
|
Move(AText[LineStart],CurLineStart^,LineLen);
|
|
inc(CurLineStart,LineLen);
|
|
// add #0 as line end
|
|
CurLineStart^:=#0;
|
|
inc(CurLineStart);
|
|
// next line
|
|
inc(i,2);
|
|
end;
|
|
if {%H-}PtrUInt(CurLineStart)-{%H-}PtrUInt(Lines)<>TotalSize then
|
|
RaiseGDBException('Gtk3WordWrap Consistency Error:'
|
|
+' Lines+TotalSize<>CurLineStart');
|
|
CurLineEntry[i shr 1]:=nil;
|
|
|
|
LinesList.Free;
|
|
end;
|
|
|
|
end.
|