|
|
|
@ -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()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|