Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 47 additions & 95 deletions platforms/Linux.Gtk4/src/Linux.Gtk4/Graphics/CairoCanvas.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
using System.Numerics;
using System.Runtime.InteropServices;
using Cairo;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Text;
using Pango;
using Attribute = Pango.Attribute;
using Color = Microsoft.Maui.Graphics.Color;
using LineCap = Microsoft.Maui.Graphics.LineCap;
using LineJoin = Microsoft.Maui.Graphics.LineJoin;
using Style = Pango.Style;

namespace Microsoft.Maui.Platforms.Linux.Gtk4.Graphics;

Expand Down Expand Up @@ -219,10 +225,9 @@ public void DrawText(IAttributedText value, float x, float y, float width, float

// Apply text attributes (bold, italic, color, etc.)
var attrList = BuildPangoAttrList(value);
if (attrList != IntPtr.Zero)
if (attrList is not null)
{
pango_layout_set_attributes(layout.Handle.DangerousGetHandle(), attrList);
// attrList ownership transfers to layout
layout.SetAttributes(attrList);
}

_cr.MoveTo(x, y);
Expand Down Expand Up @@ -311,8 +316,8 @@ public void DrawImage(global::Microsoft.Maui.Graphics.IImage image, float x, flo
if (surface == null)
return;

int imgW = cairo_image_surface_get_width(surface.Handle.DangerousGetHandle());
int imgH = cairo_image_surface_get_height(surface.Handle.DangerousGetHandle());
int imgW = surface.Width;
int imgH = surface.Height;
if (imgW <= 0 || imgH <= 0)
return;

Expand Down Expand Up @@ -507,16 +512,9 @@ private void ApplyLinearGradient(LinearGradientPaint paint, RectF rect)
double x1 = rect.X + paint.EndPoint.X * rect.Width;
double y1 = rect.Y + paint.EndPoint.Y * rect.Height;

var patternHandle = cairo_pattern_create_linear(x0, y0, x1, y1);
try
{
AddGradientStops(patternHandle, paint.GradientStops);
cairo_set_source(_cr.Handle.DangerousGetHandle(), patternHandle);
}
finally
{
cairo_pattern_destroy(patternHandle);
}
using var gradient = new LinearGradient(x0, y0, x1, y1);
AddGradientStops(gradient, paint.GradientStops);
_cr.SetSource(gradient);
}

private void ApplyRadialGradient(RadialGradientPaint paint, RectF rect)
Expand All @@ -525,26 +523,19 @@ private void ApplyRadialGradient(RadialGradientPaint paint, RectF rect)
double cy = rect.Y + paint.Center.Y * rect.Height;
double radius = Math.Max(rect.Width, rect.Height) * paint.Radius;

var patternHandle = cairo_pattern_create_radial(cx, cy, 0, cx, cy, radius);
try
{
AddGradientStops(patternHandle, paint.GradientStops);
cairo_set_source(_cr.Handle.DangerousGetHandle(), patternHandle);
}
finally
{
cairo_pattern_destroy(patternHandle);
}
using var gradient = new RadialGradient(cx, cy, 0, cx, cy, radius);
AddGradientStops(gradient, paint.GradientStops);
_cr.SetSource(gradient);
}

private void AddGradientStops(nint pattern, PaintGradientStop[]? stops)
private void AddGradientStops(Gradient pattern, PaintGradientStop[]? stops)
{
if (stops == null) return;

foreach (var stop in stops)
{
var color = stop.Color;
cairo_pattern_add_color_stop_rgba(pattern, stop.Offset,
pattern.AddColorStopRgba(stop.Offset,
color.Red, color.Green, color.Blue, color.Alpha * _alpha);
}
}
Expand Down Expand Up @@ -587,14 +578,14 @@ private Pango.Layout CreatePangoLayout(string text, IFont? font = null, float? f

/// <summary>
/// Builds a PangoAttrList from IAttributedText runs.
/// Returns IntPtr.Zero if no attributes to apply.
/// Returns null if no attributes to apply.
/// </summary>
private static IntPtr BuildPangoAttrList(IAttributedText attributedText)
private static AttrList? BuildPangoAttrList(IAttributedText attributedText)
{
if (attributedText.Runs == null || attributedText.Runs.Count == 0)
return IntPtr.Zero;
return null;

var attrList = pango_attr_list_new();
AttrList? attrList = null;
var text = attributedText.Text;

foreach (var run in attributedText.Runs)
Expand All @@ -607,21 +598,34 @@ private static IntPtr BuildPangoAttrList(IAttributedText attributedText)
if (attrs == null) continue;

if (attrs.ContainsKey(TextAttribute.Bold))
InsertPangoAttr(attrList, pango_attr_weight_new(700), byteStart, byteEnd);
{
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrWeightNew(Weight.Bold), byteStart, byteEnd);
}

if (attrs.ContainsKey(TextAttribute.Italic))
InsertPangoAttr(attrList, pango_attr_style_new(2), byteStart, byteEnd);
{
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrStyleNew(Style.Italic), byteStart, byteEnd);
}

if (attrs.ContainsKey(TextAttribute.Underline))
InsertPangoAttr(attrList, pango_attr_underline_new(1), byteStart, byteEnd);
{
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrUnderlineNew(Underline.Single), byteStart, byteEnd);
}

if (attrs.ContainsKey(TextAttribute.Strikethrough))
InsertPangoAttr(attrList, pango_attr_strikethrough_new(true), byteStart, byteEnd);
{
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrStrikethroughNew(true), byteStart, byteEnd);
}

if (attrs.TryGetValue(TextAttribute.FontSize, out var fontSizeStr)
&& float.TryParse(fontSizeStr, out float attrFontSize))
{
InsertPangoAttr(attrList, pango_attr_size_new((int)(attrFontSize * Pango.Constants.SCALE)), byteStart, byteEnd);
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrSizeNew((int)(attrFontSize * Pango.Constants.SCALE)), byteStart, byteEnd);
}

if (attrs.TryGetValue(TextAttribute.Color, out var colorStr))
Expand All @@ -631,7 +635,8 @@ private static IntPtr BuildPangoAttrList(IAttributedText attributedText)
ushort r = (ushort)(color.Red * 65535);
ushort g = (ushort)(color.Green * 65535);
ushort b = (ushort)(color.Blue * 65535);
InsertPangoAttr(attrList, pango_attr_foreground_new(r, g, b), byteStart, byteEnd);
attrList ??= AttrList.New();
InsertPangoAttr(attrList, Pango.Functions.AttrForegroundNew(r, g, b), byteStart, byteEnd);
}
}
}
Expand All @@ -641,14 +646,12 @@ private static IntPtr BuildPangoAttrList(IAttributedText attributedText)

/// <summary>
/// Sets start/end byte indices on a PangoAttribute and inserts it into the list.
/// PangoAttribute struct layout: klass (ptr), start_index (uint), end_index (uint)
/// </summary>
private static void InsertPangoAttr(IntPtr attrList, IntPtr attr, int byteStart, int byteEnd)
private static void InsertPangoAttr(AttrList attrList, Attribute attr, int byteStart, int byteEnd)
{
if (attr == IntPtr.Zero) return;
Marshal.WriteInt32(attr, IntPtr.Size, byteStart);
Marshal.WriteInt32(attr, IntPtr.Size + 4, byteEnd);
pango_attr_list_insert(attrList, attr);
attr.StartIndex = (uint)byteStart;
attr.EndIndex = (uint)byteEnd;
attrList.Insert(attr);
}

private void ApplyOperator()
Expand Down Expand Up @@ -766,55 +769,4 @@ private void DrawPathInternal(PathF path)
}
}

// --- Cairo P/Invoke for gradient patterns ---

[DllImport("libcairo.so.2")]
private static extern nint cairo_pattern_create_linear(double x0, double y0, double x1, double y1);

[DllImport("libcairo.so.2")]
private static extern nint cairo_pattern_create_radial(double cx0, double cy0, double radius0, double cx1, double cy1, double radius1);

[DllImport("libcairo.so.2")]
private static extern void cairo_pattern_add_color_stop_rgba(nint pattern, double offset, double red, double green, double blue, double alpha);

[DllImport("libcairo.so.2")]
private static extern void cairo_pattern_destroy(nint pattern);

[DllImport("libcairo.so.2")]
private static extern void cairo_set_source(nint cr, nint pattern);

[DllImport("libcairo.so.2")]
private static extern int cairo_image_surface_get_width(nint surface);

[DllImport("libcairo.so.2")]
private static extern int cairo_image_surface_get_height(nint surface);

// --- Pango P/Invoke for attributed text ---

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_list_new();

[DllImport("libpango-1.0.so.0")]
private static extern void pango_attr_list_insert(IntPtr list, IntPtr attr);

[DllImport("libpango-1.0.so.0")]
private static extern void pango_layout_set_attributes(IntPtr layout, IntPtr attrs);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_weight_new(int weight);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_style_new(int style);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_underline_new(int underline);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_strikethrough_new(bool strikethrough);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_size_new(int size);

[DllImport("libpango-1.0.so.0")]
private static extern IntPtr pango_attr_foreground_new(ushort red, ushort green, ushort blue);
}
Loading