@ -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 ) ;
@ -64,7 +70,7 @@ namespace Avalonia.Skia
public IGeometryImpl BuildGlyphRunGeometry ( GlyphRun glyphRun )
{
if ( glyphRun . GlyphTypeface . PlatformImpl is not GlyphTypefaceImpl glyphTypeface )
if ( glyphRun . GlyphTypeface is not GlyphTypefaceImpl glyphTypeface )
{
throw new InvalidOperationException ( "PlatformImpl can't be null." ) ;
}
@ -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 ( IGlyphTypeface 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 ( IGlyphTypeface glyphTypeface , float fontRenderingEmSize , int length )
= > new SKHorizontalGlyphRunBuffer ( glyphTypeface , fontRenderingEmSize , length ) ;
SKTextBlob textBlob ;
public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun ( IGlyphTypeface 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 _f ont ;
if ( glyphRun . GlyphOffsets = = null )
public SKGlyphRunBufferBase ( IGlyphTypeface 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 ;
textBlob = textBlobBuilder . Build ( ) ;
}
else
_f ont = 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 ( IGlyphTypeface glyphTypeface , float fontRenderingEmSize , int length ) : base ( glyphTypeface , fontRenderingEmSize , length )
{
_ buffer = _ builder . AllocateRun ( _f ont , 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 ( IGlyphTypeface glyphTypeface , float fontRenderingEmSize , int length ) : base ( glyphTypeface , fontRenderingEmSize , length )
{
_ buffer = _ builder . AllocateHorizontalRun ( _f ont , 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 ( IGlyphTypeface glyphTypeface , float fontRenderingEmSize , int length ) : base ( glyphTypeface , fontRenderingEmSize , length )
{
_ buffer = _ builder . AllocatePositionedRun ( _f ont , 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 ( ) ) ;
}
}
}