|
|
@@ -1,7 +1,7 @@
|
|
|
using System;
|
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
|
-using System.IO;
|
|
|
+using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Threading;
|
|
|
|
|
|
@@ -12,6 +12,8 @@ using Avalonia.OpenGL.Imaging;
|
|
|
using Avalonia.Platform;
|
|
|
using Avalonia.Media.Imaging;
|
|
|
using SkiaSharp;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using System.Drawing;
|
|
|
|
|
|
namespace Avalonia.Skia
|
|
|
{
|
|
|
@@ -33,13 +35,17 @@ namespace Avalonia.Skia
|
|
|
}
|
|
|
|
|
|
var gl = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
|
|
|
- if (gl != null)
|
|
|
+ if (gl != null)
|
|
|
_skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
|
|
|
-
|
|
|
- //TODO: SKFont crashes when disposed in finalizer so we keep it alive
|
|
|
- GC.SuppressFinalize(s_font);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ public bool SupportsIndividualRoundRects => true;
|
|
|
+
|
|
|
+ public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
|
|
|
+
|
|
|
+ public PixelFormat DefaultPixelFormat { get; }
|
|
|
+
|
|
|
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
|
|
|
|
|
|
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
|
|
|
@@ -228,133 +234,95 @@ namespace Avalonia.Skia
|
|
|
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
|
|
|
}
|
|
|
|
|
|
- private static readonly SKFont s_font = new SKFont
|
|
|
- {
|
|
|
- Subpixel = true,
|
|
|
- Edging = SKFontEdging.SubpixelAntialias,
|
|
|
- Hinting = SKFontHinting.Full,
|
|
|
- LinearMetrics = true
|
|
|
- };
|
|
|
-
|
|
|
- private static readonly ThreadLocal<SKTextBlobBuilder> s_textBlobBuilderThreadLocal = new ThreadLocal<SKTextBlobBuilder>(() => new SKTextBlobBuilder());
|
|
|
-
|
|
|
- /// <inheritdoc />
|
|
|
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
|
|
|
+ public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
|
|
|
{
|
|
|
- var count = glyphRun.GlyphIndices.Count;
|
|
|
- var textBlobBuilder = s_textBlobBuilderThreadLocal.Value;
|
|
|
-
|
|
|
- var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl;
|
|
|
+ if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
|
|
|
+ return glAware.CreateOpenGlBitmap(size, dpi);
|
|
|
+ if (_skiaGpu == null)
|
|
|
+ throw new PlatformNotSupportedException("GPU acceleration is not available");
|
|
|
+ throw new PlatformNotSupportedException(
|
|
|
+ "Current GPU acceleration backend does not support OpenGL integration");
|
|
|
+ }
|
|
|
|
|
|
- var typeface = glyphTypeface.Typeface;
|
|
|
+ public IGlyphRunBuffer AllocateGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
|
|
+ => new SKGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
|
|
|
|
|
|
- s_font.Size = (float)glyphRun.FontRenderingEmSize;
|
|
|
- s_font.Typeface = typeface;
|
|
|
- s_font.Embolden = glyphTypeface.IsFakeBold;
|
|
|
- s_font.SkewX = glyphTypeface.IsFakeItalic ? -0.2f : 0;
|
|
|
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
|
|
+ => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
|
|
|
|
|
|
- SKTextBlob textBlob;
|
|
|
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
|
|
+ => new SKPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
|
|
|
|
|
|
- var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
|
|
|
+ private abstract class SKGlyphRunBufferBase : IGlyphRunBuffer
|
|
|
+ {
|
|
|
+ protected readonly SKTextBlobBuilder _builder;
|
|
|
+ protected readonly SKFont _font;
|
|
|
|
|
|
- if (glyphRun.GlyphOffsets == null)
|
|
|
+ public SKGlyphRunBufferBase(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
|
|
|
{
|
|
|
- if (glyphTypeface.IsFixedPitch)
|
|
|
- {
|
|
|
- var buffer = textBlobBuilder.AllocateRun(s_font, glyphRun.GlyphIndices.Count, 0, 0);
|
|
|
-
|
|
|
- var glyphs = buffer.GetGlyphSpan();
|
|
|
+ _builder = new SKTextBlobBuilder();
|
|
|
|
|
|
- for (int i = 0; i < glyphs.Length; i++)
|
|
|
- {
|
|
|
- glyphs[i] = glyphRun.GlyphIndices[i];
|
|
|
- }
|
|
|
+ var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface.PlatformImpl;
|
|
|
|
|
|
- textBlob = textBlobBuilder.Build();
|
|
|
- }
|
|
|
- else
|
|
|
+ _font = new SKFont
|
|
|
{
|
|
|
- var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0);
|
|
|
-
|
|
|
- var positions = buffer.GetPositionSpan();
|
|
|
-
|
|
|
- var width = 0d;
|
|
|
-
|
|
|
- for (var i = 0; i < count; i++)
|
|
|
- {
|
|
|
- positions[i] = (float)width;
|
|
|
-
|
|
|
- if (glyphRun.GlyphAdvances == null)
|
|
|
- {
|
|
|
- width += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- width += glyphRun.GlyphAdvances[i];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var glyphs = buffer.GetGlyphSpan();
|
|
|
+ Subpixel = true,
|
|
|
+ Edging = SKFontEdging.SubpixelAntialias,
|
|
|
+ Hinting = SKFontHinting.Full,
|
|
|
+ LinearMetrics = true,
|
|
|
+ Size = fontRenderingEmSize,
|
|
|
+ Typeface = glyphTypefaceImpl.Typeface,
|
|
|
+ Embolden = glyphTypefaceImpl.IsFakeBold,
|
|
|
+ SkewX = glyphTypefaceImpl.IsFakeItalic ? -0.2f : 0
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < glyphs.Length; i++)
|
|
|
- {
|
|
|
- glyphs[i] = glyphRun.GlyphIndices[i];
|
|
|
- }
|
|
|
+ public abstract Span<ushort> GlyphIndices { get; }
|
|
|
|
|
|
- textBlob = textBlobBuilder.Build();
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
+ public IGlyphRunImpl Build()
|
|
|
{
|
|
|
- var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count);
|
|
|
-
|
|
|
- var glyphPositions = buffer.GetPositionSpan();
|
|
|
+ return new GlyphRunImpl(_builder.Build());
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var currentX = 0.0;
|
|
|
+ private sealed class SKGlyphRunBuffer : SKGlyphRunBufferBase
|
|
|
+ {
|
|
|
+ private readonly SKRunBuffer _buffer;
|
|
|
|
|
|
- for (var i = 0; i < count; i++)
|
|
|
- {
|
|
|
- var glyphOffset = glyphRun.GlyphOffsets[i];
|
|
|
-
|
|
|
- glyphPositions[i] = new SKPoint((float)(currentX + glyphOffset.X), (float)glyphOffset.Y);
|
|
|
-
|
|
|
- if (glyphRun.GlyphAdvances == null)
|
|
|
- {
|
|
|
- currentX += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- currentX += glyphRun.GlyphAdvances[i];
|
|
|
- }
|
|
|
- }
|
|
|
+ public SKGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
|
|
|
+ {
|
|
|
+ _buffer = _builder.AllocateRun(_font, length, 0, 0);
|
|
|
+ }
|
|
|
|
|
|
- var glyphs = buffer.GetGlyphSpan();
|
|
|
+ public override Span<ushort> GlyphIndices => _buffer.GetGlyphSpan();
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < glyphs.Length; i++)
|
|
|
- {
|
|
|
- glyphs[i] = glyphRun.GlyphIndices[i];
|
|
|
- }
|
|
|
+ private sealed class SKHorizontalGlyphRunBuffer : SKGlyphRunBufferBase, IHorizontalGlyphRunBuffer
|
|
|
+ {
|
|
|
+ private readonly SKHorizontalRunBuffer _buffer;
|
|
|
|
|
|
- textBlob = textBlobBuilder.Build();
|
|
|
+ public SKHorizontalGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
|
|
|
+ {
|
|
|
+ _buffer = _builder.AllocateHorizontalRun(_font, length, 0);
|
|
|
}
|
|
|
|
|
|
- return new GlyphRunImpl(textBlob);
|
|
|
+ public override Span<ushort> GlyphIndices => _buffer.GetGlyphSpan();
|
|
|
+
|
|
|
+ public Span<float> GlyphPositions => _buffer.GetPositionSpan();
|
|
|
}
|
|
|
|
|
|
- public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
|
|
|
+ private sealed class SKPositionedGlyphRunBuffer : SKGlyphRunBufferBase, IPositionedGlyphRunBuffer
|
|
|
{
|
|
|
- if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
|
|
|
- return glAware.CreateOpenGlBitmap(size, dpi);
|
|
|
- if (_skiaGpu == null)
|
|
|
- throw new PlatformNotSupportedException("GPU acceleration is not available");
|
|
|
- throw new PlatformNotSupportedException(
|
|
|
- "Current GPU acceleration backend does not support OpenGL integration");
|
|
|
- }
|
|
|
+ private readonly SKPositionedRunBuffer _buffer;
|
|
|
|
|
|
- public bool SupportsIndividualRoundRects => true;
|
|
|
+ public SKPositionedGlyphRunBuffer(GlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
|
|
|
+ {
|
|
|
+ _buffer = _builder.AllocatePositionedRun(_font, length);
|
|
|
+ }
|
|
|
|
|
|
- public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
|
|
|
+ public override Span<ushort> GlyphIndices => _buffer.GetGlyphSpan();
|
|
|
|
|
|
- public PixelFormat DefaultPixelFormat { get; }
|
|
|
+ public Span<PointF> GlyphPositions => MemoryMarshal.Cast<SKPoint, PointF>(_buffer.GetPositionSpan());
|
|
|
+ }
|
|
|
}
|
|
|
}
|