Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions Source/OxyPlot.Maui.Skia/OxyPlot.Maui.Skia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="OxyPlot.Core" Version="2.2.0" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.8" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.8" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="3.116.1" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="3.116.1" />
</ItemGroup>

<ItemGroup>
Expand Down
110 changes: 66 additions & 44 deletions Source/OxyPlot.Maui.Skia/SkiaRenderContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal class SkiaRenderContext : IRenderContext, IDisposable
private readonly Dictionary<FontDescriptor, SKShaper> shaperCache = new();
private readonly Dictionary<FontDescriptor, SKTypeface> typefaceCache = new();
private SKPaint paint = new();
private SKFont font = new();
private SKPath path = new();

/// <summary>
Expand Down Expand Up @@ -130,21 +131,30 @@ public void DrawImage(
double destWidth,
double destHeight,
double opacity,
bool interpolate)
bool interpolate = false)
{
if (source == null)
{
return;
}

var bytes = source.GetData();
var image = SKBitmap.Decode(bytes);
var bmp = SKBitmap.Decode(bytes);

var src = new SKRect((float)srcX, (float)srcY, (float)(srcX + srcWidth), (float)(srcY + srcHeight));
var dest = new SKRect(this.Convert(destX), this.Convert(destY), this.Convert(destX + destWidth), this.Convert(destY + destHeight));

var paint = this.GetImagePaint(opacity, interpolate);
this.SkCanvas.DrawBitmap(image, src, dest, paint);
var paint = this.GetImagePaint(opacity);
if (interpolate)
{
var sampling = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.Linear);
var img = SKImage.FromBitmap(bmp);
this.SkCanvas.DrawImage(img, src, dest, sampling, paint);
}
else
{
this.SkCanvas.DrawBitmap(bmp, src, dest, paint);
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -364,14 +374,15 @@ public void DrawText(
return;
}

var paint = this.GetTextPaint(fontFamily, fontSize, fontWeight, out var shaper);
var font = this.GetTextFont(fontFamily, fontSize, fontWeight, out var shaper);
var paint = this.GetTextPaint();
paint.Color = fill.ToSKColor();

var x = this.Convert(p.X);
var y = this.Convert(p.Y);

var lines = StringHelper.SplitLines(text);
var lineHeight = paint.GetFontMetrics(out var metrics);
var lineHeight = font.GetFontMetrics(out var metrics);

var deltaY = verticalAlignment switch
{
Expand All @@ -389,7 +400,7 @@ public void DrawText(
{
if (this.UseTextShaping)
{
var width = this.MeasureText(line, shaper, paint);
var width = this.MeasureText(line, shaper, font);
var deltaX = horizontalAlignment switch
{
HorizontalAlignment.Left => 0,
Expand All @@ -398,20 +409,19 @@ public void DrawText(
_ => throw new ArgumentOutOfRangeException(nameof(horizontalAlignment))
};

this.paint.TextAlign = SKTextAlign.Left;
this.SkCanvas.DrawShapedText(shaper, line, deltaX, deltaY, paint);
this.SkCanvas.DrawShapedText(shaper, line, deltaX, deltaY, SKTextAlign.Left, font, paint);
}
else
{
paint.TextAlign = horizontalAlignment switch
var align = horizontalAlignment switch
{
HorizontalAlignment.Left => SKTextAlign.Left,
HorizontalAlignment.Center => SKTextAlign.Center,
HorizontalAlignment.Right => SKTextAlign.Right,
_ => throw new ArgumentOutOfRangeException(nameof(horizontalAlignment))
};

this.SkCanvas.DrawText(line, 0, deltaY, paint);
this.SkCanvas.DrawText(line, 0, deltaY, align, font, paint);
}

deltaY += lineHeight;
Expand All @@ -427,9 +437,10 @@ public OxySize MeasureText(string text, string fontFamily = null, double fontSiz
}

var lines = StringHelper.SplitLines(text);
var paint = this.GetTextPaint(fontFamily, fontSize, fontWeight, out var shaper);
var height = paint.GetFontMetrics(out _) * lines.Length;
var width = lines.Max(line => this.MeasureText(line, shaper, paint));
var font = this.GetTextFont(fontFamily, fontSize, fontWeight, out var shaper);
var paint = this.GetTextPaint();
var height = font.GetFontMetrics(out _) * lines.Length;
var width = lines.Max(line => this.MeasureText(line, shaper, font));

return new OxySize(this.ConvertBack(width), this.ConvertBack(height));
}
Expand Down Expand Up @@ -739,12 +750,10 @@ private SKPaint GetFillPaint(OxyColor fillColor, EdgeRenderingMode edgeRendering
/// This modifies and returns the local <see cref="paint"/> instance.
/// </remarks>
/// <param name="opacity">The opacity.</param>
/// <param name="interpolate">A value indicating whether interpolation should be used.</param>
/// <returns>The paint.</returns>
private SKPaint GetImagePaint(double opacity, bool interpolate)
private SKPaint GetImagePaint(double opacity)
{
this.paint.Color = new SKColor(0, 0, 0, (byte)(255 * opacity));
this.paint.FilterQuality = interpolate ? SKFilterQuality.High : SKFilterQuality.None;
this.paint.IsAntialias = true;
return this.paint;
}
Expand Down Expand Up @@ -833,17 +842,17 @@ private SKPaint GetStrokePaint(OxyColor strokeColor, double strokeThickness, Edg
}

/// <summary>
/// Gets a <see cref="SKPaint"/> containing information needed to render text.
/// Gets a <see cref="SKFont"/> containing information needed to render text.
/// </summary>
/// <remarks>
/// This modifies and returns the local <see cref="paint"/> instance.
/// This modifies and returns the local <see cref="font"/> instance.
/// </remarks>
/// <param name="fontFamily">The font family.</param>
/// <param name="fontSize">The font size.</param>
/// <param name="fontWeight">The font weight.</param>
/// <param name="shaper">The font shaper.</param>
/// <returns>The paint.</returns>
private SKPaint GetTextPaint(string fontFamily, double fontSize, double fontWeight, out SKShaper shaper)
/// <returns>The font.</returns>
private SKFont GetTextFont(string fontFamily, double fontSize, double fontWeight, out SKShaper shaper)
{
var fontDescriptor = new FontDescriptor(fontFamily, fontWeight);
if (!this.typefaceCache.TryGetValue(fontDescriptor, out var typeface))
Expand All @@ -865,12 +874,24 @@ private SKPaint GetTextPaint(string fontFamily, double fontSize, double fontWeig
shaper = null;
}

this.paint.Typeface = typeface;
this.paint.TextSize = this.Convert(fontSize);
this.font.Typeface = typeface;
this.font.Size = this.Convert(fontSize);
this.font.Hinting = this.RendersToScreen ? SKFontHinting.Full : SKFontHinting.None;
this.font.Subpixel = this.RendersToScreen;
return this.font;
}

/// <summary>
/// Gets a <see cref="SKPaint"/> containing information needed to render text.
/// </summary>
/// <remarks>
/// This modifies and returns the local <see cref="paint"/> instance.
/// </remarks>
/// <returns>The paint.</returns>
private SKPaint GetTextPaint()
{
this.paint.IsAntialias = true;
this.paint.Style = SKPaintStyle.Fill;
this.paint.HintingLevel = this.RendersToScreen ? SKPaintHinting.Full : SKPaintHinting.NoHinting;
this.paint.SubpixelText = this.RendersToScreen;
return this.paint;
}

Expand All @@ -879,36 +900,37 @@ private SKPaint GetTextPaint(string fontFamily, double fontSize, double fontWeig
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="shaper">The text shaper.</param>
/// <param name="paint">The paint.</param>
/// <param name="font">The font.</param>
/// <returns>The width of the text when rendered using the specified shaper and paint.</returns>
private float MeasureText(string text, SKShaper shaper, SKPaint paint)
private float MeasureText(string text, SKShaper shaper, SKFont font)
{
if (!this.UseTextShaping)
{
return paint.MeasureText(text);
return font.MeasureText(text);
}

// we have to get a bit creative here as SKShaper does not offer a direct overload for this.
// see also https://github.com/mono/SkiaSharp/blob/master/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz.Shared/SKShaper.cs
using var buffer = new HarfBuzzSharp.Buffer();
switch (paint.TextEncoding)
{
case SKTextEncoding.Utf8:
buffer.AddUtf8(text);
break;
case SKTextEncoding.Utf16:
buffer.AddUtf16(text);
break;
case SKTextEncoding.Utf32:
buffer.AddUtf32(text);
break;
default:
throw new NotSupportedException("TextEncoding is not supported.");
}

// switch (paint.TextEncoding)
// {
// case SKTextEncoding.Utf8:
buffer.AddUtf8(text);
// break;
// case SKTextEncoding.Utf16:
// buffer.AddUtf16(text);
// break;
// case SKTextEncoding.Utf32:
// buffer.AddUtf32(text);
// break;
// default:
// throw new NotSupportedException("TextEncoding is not supported.");
// }

buffer.GuessSegmentProperties();
shaper.Shape(buffer, paint);
return buffer.GlyphPositions.Sum(gp => gp.XAdvance) * paint.TextSize / 512;
shaper.Shape(buffer, font);
return buffer.GlyphPositions.Sum(gp => gp.XAdvance) * font.Size / 512;
}

/// <summary>
Expand Down