Browse Source

Merge pull request #10390 from MrJul/skia-nullable

Nullable annotations for Avalonia.Skia
fixes/6684-two-way-style-bindings-with-localvalue
Max Katz 3 years ago
committed by GitHub
parent
commit
bda1a2fc1c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.Base/Media/IImageBrush.cs
  2. 8
      src/Avalonia.Base/Media/ImageBrush.cs
  3. 6
      src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs
  4. 2
      src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
  5. 6
      src/Avalonia.Base/Platform/IGeometryImpl.cs
  6. 2
      src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs
  7. 1
      src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
  8. 16
      src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs
  9. 220
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  10. 21
      src/Skia/Avalonia.Skia/FontManagerImpl.cs
  11. 10
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  12. 8
      src/Skia/Avalonia.Skia/GeometryGroupImpl.cs
  13. 44
      src/Skia/Avalonia.Skia/GeometryImpl.cs
  14. 3
      src/Skia/Avalonia.Skia/GlyphRunImpl.cs
  15. 6
      src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
  16. 4
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  17. 18
      src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
  18. 11
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs
  19. 20
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  20. 2
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  21. 2
      src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
  22. 6
      src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs
  23. 15
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  24. 4
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  25. 14
      src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
  26. 11
      src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
  27. 14
      src/Skia/Avalonia.Skia/SkiaBackendContext.cs
  28. 14
      src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
  29. 2
      src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
  30. 50
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  31. 4
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  32. 6
      src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
  33. 6
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

2
src/Avalonia.Base/Media/IImageBrush.cs

@ -12,6 +12,6 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets the image to draw. /// Gets the image to draw.
/// </summary> /// </summary>
IBitmap Source { get; } IBitmap? Source { get; }
} }
} }

8
src/Avalonia.Base/Media/ImageBrush.cs

@ -11,8 +11,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Visual"/> property. /// Defines the <see cref="Visual"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<IBitmap> SourceProperty = public static readonly StyledProperty<IBitmap?> SourceProperty =
AvaloniaProperty.Register<ImageBrush, IBitmap>(nameof(Source)); AvaloniaProperty.Register<ImageBrush, IBitmap?>(nameof(Source));
static ImageBrush() static ImageBrush()
{ {
@ -30,7 +30,7 @@ namespace Avalonia.Media
/// Initializes a new instance of the <see cref="ImageBrush"/> class. /// Initializes a new instance of the <see cref="ImageBrush"/> class.
/// </summary> /// </summary>
/// <param name="source">The image to draw.</param> /// <param name="source">The image to draw.</param>
public ImageBrush(IBitmap source) public ImageBrush(IBitmap? source)
{ {
Source = source; Source = source;
} }
@ -38,7 +38,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the image to draw. /// Gets or sets the image to draw.
/// </summary> /// </summary>
public IBitmap Source public IBitmap? Source
{ {
get { return GetValue(SourceProperty); } get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); } set { SetValue(SourceProperty, value); }

6
src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs

@ -24,13 +24,13 @@ namespace Avalonia.Media.Immutable
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param> /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public ImmutableImageBrush( public ImmutableImageBrush(
IBitmap source, IBitmap? source,
AlignmentX alignmentX = AlignmentX.Center, AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center, AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null, RelativeRect? destinationRect = null,
double opacity = 1, double opacity = 1,
ImmutableTransform? transform = null, ImmutableTransform? transform = null,
RelativePoint transformOrigin = new RelativePoint(), RelativePoint transformOrigin = default,
RelativeRect? sourceRect = null, RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform, Stretch stretch = Stretch.Uniform,
TileMode tileMode = TileMode.None, TileMode tileMode = TileMode.None,
@ -61,6 +61,6 @@ namespace Avalonia.Media.Immutable
} }
/// <inheritdoc/> /// <inheritdoc/>
public IBitmap Source { get; } public IBitmap? Source { get; }
} }
} }

2
src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs

@ -24,7 +24,7 @@ namespace Avalonia.Media.Immutable
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">Controls the quality of interpolation.</param> /// <param name="bitmapInterpolationMode">Controls the quality of interpolation.</param>
public ImmutableVisualBrush( public ImmutableVisualBrush(
Visual visual, Visual? visual,
AlignmentX alignmentX = AlignmentX.Center, AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center, AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null, RelativeRect? destinationRect = null,

6
src/Avalonia.Base/Platform/IGeometryImpl.cs

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -47,7 +48,7 @@ namespace Avalonia.Platform
/// <param name="pen">The stroke to use.</param> /// <param name="pen">The stroke to use.</param>
/// <param name="point">The point.</param> /// <param name="point">The point.</param>
/// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the geometry contains the point; otherwise, <c>false</c>.</returns>
bool StrokeContains(IPen pen, Point point); bool StrokeContains(IPen? pen, Point point);
/// <summary> /// <summary>
/// Makes a clone of the geometry with the specified transform. /// Makes a clone of the geometry with the specified transform.
@ -87,6 +88,7 @@ namespace Avalonia.Platform
/// <param name="startOnBeginFigure">If ture, the resulting snipped path will start with a BeginFigure call.</param> /// <param name="startOnBeginFigure">If ture, the resulting snipped path will start with a BeginFigure call.</param>
/// <param name="segmentGeometry">The resulting snipped path.</param> /// <param name="segmentGeometry">The resulting snipped path.</param>
/// <returns>If the snipping operation is successful.</returns> /// <returns>If the snipping operation is successful.</returns>
bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry); bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure,
[NotNullWhen(true)] out IGeometryImpl? segmentGeometry);
} }
} }

2
src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs

@ -21,7 +21,7 @@ namespace Avalonia.Browser.Skia
return null; return null;
} }
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session) public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session)
{ {
return null; return null;
} }

1
src/Skia/Avalonia.Skia/Avalonia.Skia.csproj

@ -19,6 +19,7 @@
<Import Project="..\..\..\build\HarfBuzzSharp.props" /> <Import Project="..\..\..\build\HarfBuzzSharp.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" /> <Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" /> <Import Project="..\..\..\build\TrimmingEnable.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
<ItemGroup Label="InternalsVisibleTo"> <ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />

16
src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs

@ -1,9 +1,6 @@
using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using SkiaSharp; using SkiaSharp;
#nullable enable
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
/// <summary> /// <summary>
@ -13,23 +10,24 @@ namespace Avalonia.Skia
{ {
public CombinedGeometryImpl(GeometryCombineMode combineMode, Geometry g1, Geometry g2) public CombinedGeometryImpl(GeometryCombineMode combineMode, Geometry g1, Geometry g2)
{ {
var path1 = ((GeometryImpl)g1.PlatformImpl).EffectivePath; var path1 = (g1.PlatformImpl as GeometryImpl)?.EffectivePath;
var path2 = ((GeometryImpl)g2.PlatformImpl).EffectivePath; var path2 = (g2.PlatformImpl as GeometryImpl)?.EffectivePath;
var op = combineMode switch var op = combineMode switch
{ {
GeometryCombineMode.Intersect => SKPathOp.Intersect, GeometryCombineMode.Intersect => SKPathOp.Intersect,
GeometryCombineMode.Xor => SKPathOp.Xor, GeometryCombineMode.Xor => SKPathOp.Xor,
GeometryCombineMode.Exclude => SKPathOp.Difference, GeometryCombineMode.Exclude => SKPathOp.Difference,
_ => SKPathOp.Union, _ => SKPathOp.Union
}; };
var path = path1.Op(path2, op); var path = path1?.Op(path2, op);
EffectivePath = path; EffectivePath = path;
Bounds = path.Bounds.ToAvaloniaRect(); Bounds = path?.Bounds.ToAvaloniaRect() ?? default;
} }
public override Rect Bounds { get; } public override Rect Bounds { get; }
public override SKPath EffectivePath { get; } public override SKPath? EffectivePath { get; }
} }
} }

220
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -19,27 +19,27 @@ namespace Avalonia.Skia
/// </summary> /// </summary>
internal class DrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport internal class DrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport
{ {
private IDisposable[] _disposables; private IDisposable?[]? _disposables;
private readonly Vector _dpi; private readonly Vector _dpi;
private readonly Stack<PaintWrapper> _maskStack = new Stack<PaintWrapper>(); private readonly Stack<PaintWrapper> _maskStack = new();
private readonly Stack<double> _opacityStack = new Stack<double>(); private readonly Stack<double> _opacityStack = new();
private readonly Stack<BitmapBlendingMode> _blendingModeStack = new Stack<BitmapBlendingMode>(); private readonly Stack<BitmapBlendingMode> _blendingModeStack = new();
private readonly Matrix? _postTransform; private readonly Matrix? _postTransform;
private readonly IVisualBrushRenderer _visualBrushRenderer; private readonly IVisualBrushRenderer? _visualBrushRenderer;
private double _currentOpacity = 1.0f; private double _currentOpacity = 1.0f;
private BitmapBlendingMode _currentBlendingMode = BitmapBlendingMode.SourceOver; private BitmapBlendingMode _currentBlendingMode = BitmapBlendingMode.SourceOver;
private readonly bool _canTextUseLcdRendering; private readonly bool _canTextUseLcdRendering;
private Matrix _currentTransform; private Matrix _currentTransform;
private bool _disposed; private bool _disposed;
private GRContext _grContext; private GRContext? _grContext;
public GRContext GrContext => _grContext; public GRContext? GrContext => _grContext;
private ISkiaGpu _gpu; private readonly ISkiaGpu? _gpu;
private readonly SKPaint _strokePaint = SKPaintCache.Shared.Get(); private readonly SKPaint _strokePaint = SKPaintCache.Shared.Get();
private readonly SKPaint _fillPaint = SKPaintCache.Shared.Get(); private readonly SKPaint _fillPaint = SKPaintCache.Shared.Get();
private readonly SKPaint _boxShadowPaint = SKPaintCache.Shared.Get(); private readonly SKPaint _boxShadowPaint = SKPaintCache.Shared.Get();
private static SKShader s_acrylicNoiseShader; private static SKShader? s_acrylicNoiseShader;
private readonly ISkiaGpuRenderSession _session; private readonly ISkiaGpuRenderSession? _session;
private bool _leased = false; private bool _leased;
/// <summary> /// <summary>
/// Context create info. /// Context create info.
@ -49,12 +49,12 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// Canvas to draw to. /// Canvas to draw to.
/// </summary> /// </summary>
public SKCanvas Canvas; public SKCanvas? Canvas;
/// <summary> /// <summary>
/// Surface to draw to. /// Surface to draw to.
/// </summary> /// </summary>
public SKSurface Surface; public SKSurface? Surface;
/// <summary> /// <summary>
/// Dpi of drawings. /// Dpi of drawings.
@ -64,7 +64,7 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// Visual brush renderer. /// Visual brush renderer.
/// </summary> /// </summary>
public IVisualBrushRenderer VisualBrushRenderer; public IVisualBrushRenderer? VisualBrushRenderer;
/// <summary> /// <summary>
/// Render text without Lcd rendering. /// Render text without Lcd rendering.
@ -74,17 +74,17 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// GPU-accelerated context (optional) /// GPU-accelerated context (optional)
/// </summary> /// </summary>
public GRContext GrContext; public GRContext? GrContext;
/// <summary> /// <summary>
/// Skia GPU provider context (optional) /// Skia GPU provider context (optional)
/// </summary> /// </summary>
public ISkiaGpu Gpu; public ISkiaGpu? Gpu;
public ISkiaGpuRenderSession CurrentSession; public ISkiaGpuRenderSession? CurrentSession;
} }
class SkiaLeaseFeature : ISkiaSharpApiLeaseFeature private class SkiaLeaseFeature : ISkiaSharpApiLeaseFeature
{ {
private readonly DrawingContextImpl _context; private readonly DrawingContextImpl _context;
@ -99,10 +99,11 @@ namespace Avalonia.Skia
return new ApiLease(_context); return new ApiLease(_context);
} }
class ApiLease : ISkiaSharpApiLease private class ApiLease : ISkiaSharpApiLease
{ {
private DrawingContextImpl _context; private readonly DrawingContextImpl _context;
private readonly SKMatrix _revertTransform; private readonly SKMatrix _revertTransform;
private bool _isDisposed;
public ApiLease(DrawingContextImpl context) public ApiLease(DrawingContextImpl context)
{ {
@ -112,15 +113,18 @@ namespace Avalonia.Skia
} }
public SKCanvas SkCanvas => _context.Canvas; public SKCanvas SkCanvas => _context.Canvas;
public GRContext GrContext => _context.GrContext; public GRContext? GrContext => _context.GrContext;
public SKSurface SkSurface => _context.Surface; public SKSurface? SkSurface => _context.Surface;
public double CurrentOpacity => _context._currentOpacity; public double CurrentOpacity => _context._currentOpacity;
public void Dispose() public void Dispose()
{ {
_context.Canvas.SetMatrix(_revertTransform); if (!_isDisposed)
_context._leased = false; {
_context = null; _context.Canvas.SetMatrix(_revertTransform);
_context._leased = false;
_isDisposed = true;
}
} }
} }
} }
@ -130,8 +134,11 @@ namespace Avalonia.Skia
/// </summary> /// </summary>
/// <param name="createInfo">Create info.</param> /// <param name="createInfo">Create info.</param>
/// <param name="disposables">Array of elements to dispose after drawing has finished.</param> /// <param name="disposables">Array of elements to dispose after drawing has finished.</param>
public DrawingContextImpl(CreateInfo createInfo, params IDisposable[] disposables) public DrawingContextImpl(CreateInfo createInfo, params IDisposable?[]? disposables)
{ {
Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas
?? throw new ArgumentException("Invalid create info - no Canvas provided", nameof(createInfo));
_dpi = createInfo.Dpi; _dpi = createInfo.Dpi;
_visualBrushRenderer = createInfo.VisualBrushRenderer; _visualBrushRenderer = createInfo.VisualBrushRenderer;
_disposables = disposables; _disposables = disposables;
@ -141,15 +148,9 @@ namespace Avalonia.Skia
if (_grContext != null) if (_grContext != null)
Monitor.Enter(_grContext); Monitor.Enter(_grContext);
Surface = createInfo.Surface; Surface = createInfo.Surface;
Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas;
_session = createInfo.CurrentSession; _session = createInfo.CurrentSession;
if (Canvas == null)
{
throw new ArgumentException("Invalid create info - no Canvas provided", nameof(createInfo));
}
if (!_dpi.NearlyEquals(SkiaPlatform.DefaultDpi)) if (!_dpi.NearlyEquals(SkiaPlatform.DefaultDpi))
{ {
_postTransform = _postTransform =
@ -163,7 +164,7 @@ namespace Avalonia.Skia
/// Skia canvas. /// Skia canvas.
/// </summary> /// </summary>
public SKCanvas Canvas { get; } public SKCanvas Canvas { get; }
public SKSurface Surface { get; } public SKSurface? Surface { get; }
private void CheckLease() private void CheckLease()
{ {
@ -205,87 +206,89 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawLine(IPen pen, Point p1, Point p2) public void DrawLine(IPen? pen, Point p1, Point p2)
{ {
CheckLease(); CheckLease();
if (pen is null) if (pen is not null
{ && TryCreatePaint(_strokePaint, pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))) is { } stroke)
return;
}
using (var paint = CreatePaint(_strokePaint, pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
{ {
if (paint.Paint is object) using (stroke)
{ {
Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, paint.Paint); Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, stroke.Paint);
} }
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
{ {
CheckLease(); CheckLease();
var impl = (GeometryImpl) geometry; var impl = (GeometryImpl) geometry;
var size = geometry.Bounds.Size; var size = geometry.Bounds.Size;
using (var fill = brush != null ? CreatePaint(_fillPaint, brush, size) : default) if (brush is not null)
using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen,
size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0))) : default)
{ {
if (fill.Paint != null) using (var fill = CreatePaint(_fillPaint, brush, size))
{ {
Canvas.DrawPath(impl.EffectivePath, fill.Paint); Canvas.DrawPath(impl.EffectivePath, fill.Paint);
} }
}
if (stroke.Paint != null) if (pen is not null
&& TryCreatePaint(_strokePaint, pen, size.Inflate(new Thickness(pen.Thickness / 2))) is { } stroke)
{
using (stroke)
{ {
Canvas.DrawPath(impl.EffectivePath, stroke.Paint); Canvas.DrawPath(impl.EffectivePath, stroke.Paint);
} }
} }
} }
struct BoxShadowFilter : IDisposable private struct BoxShadowFilter : IDisposable
{ {
public SKPaint Paint; public readonly SKPaint Paint;
private SKImageFilter _filter; private readonly SKImageFilter? _filter;
public SKClipOperation ClipOperation; public readonly SKClipOperation ClipOperation;
private BoxShadowFilter(SKPaint paint, SKImageFilter? filter, SKClipOperation clipOperation)
{
Paint = paint;
_filter = filter;
ClipOperation = clipOperation;
}
static float SkBlurRadiusToSigma(double radius) { private static float SkBlurRadiusToSigma(double radius) {
if (radius <= 0) if (radius <= 0)
return 0.0f; return 0.0f;
return 0.288675f * (float)radius + 0.5f; return 0.288675f * (float)radius + 0.5f;
} }
public static BoxShadowFilter Create(SKPaint paint, BoxShadow shadow, double opacity) public static BoxShadowFilter Create(SKPaint paint, BoxShadow shadow, double opacity)
{ {
var ac = shadow.Color; var ac = shadow.Color;
SKImageFilter filter = null; var filter = SKImageFilter.CreateBlur(SkBlurRadiusToSigma(shadow.Blur), SkBlurRadiusToSigma(shadow.Blur));
filter = SKImageFilter.CreateBlur(SkBlurRadiusToSigma(shadow.Blur), SkBlurRadiusToSigma(shadow.Blur));
var color = new SKColor(ac.R, ac.G, ac.B, (byte)(ac.A * opacity)); var color = new SKColor(ac.R, ac.G, ac.B, (byte)(ac.A * opacity));
paint.Reset(); paint.Reset();
paint.IsAntialias = true; paint.IsAntialias = true;
paint.Color = color; paint.Color = color;
paint.ImageFilter = filter; paint.ImageFilter = filter;
return new BoxShadowFilter var clipOperation = shadow.IsInset ? SKClipOperation.Intersect : SKClipOperation.Difference;
{
Paint = paint, _filter = filter, return new BoxShadowFilter(paint, filter, clipOperation);
ClipOperation = shadow.IsInset ? SKClipOperation.Intersect : SKClipOperation.Difference
};
} }
public void Dispose() public void Dispose()
{ {
Paint.Reset(); Paint?.Reset();
Paint = null;
_filter?.Dispose(); _filter?.Dispose();
} }
} }
static SKRect AreaCastingShadowInHole( private static SKRect AreaCastingShadowInHole(
SKRect hole_rect, SKRect hole_rect,
float shadow_blur, float shadow_blur,
float shadow_spread, float shadow_spread,
@ -306,18 +309,16 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawRectangle(IExperimentalAcrylicMaterial material, RoundedRect rect) public void DrawRectangle(IExperimentalAcrylicMaterial? material, RoundedRect rect)
{ {
if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0) if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0)
return; return;
CheckLease(); CheckLease();
var rc = rect.Rect.ToSKRect(); var rc = rect.Rect.ToSKRect();
var isRounded = rect.IsRounded; SKRoundRect? skRoundRect = null;
var needRoundRect = rect.IsRounded;
SKRoundRect skRoundRect = null;
if (needRoundRect) if (rect.IsRounded)
{ {
skRoundRect = SKRoundRectCache.Shared.Get(); skRoundRect = SKRoundRectCache.Shared.Get();
skRoundRect.SetRectRadii(rc, skRoundRect.SetRectRadii(rc,
@ -334,7 +335,7 @@ namespace Avalonia.Skia
{ {
using (var paint = CreateAcrylicPaint(_fillPaint, material)) using (var paint = CreateAcrylicPaint(_fillPaint, material))
{ {
if (isRounded) if (skRoundRect is not null)
{ {
Canvas.DrawRoundRect(skRoundRect, paint.Paint); Canvas.DrawRoundRect(skRoundRect, paint.Paint);
SKRoundRectCache.Shared.Return(skRoundRect); SKRoundRectCache.Shared.Return(skRoundRect);
@ -349,7 +350,7 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadows = default) public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default)
{ {
if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0) if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0)
return; return;
@ -362,7 +363,7 @@ namespace Avalonia.Skia
var rc = rect.Rect.ToSKRect(); var rc = rect.Rect.ToSKRect();
var isRounded = rect.IsRounded; var isRounded = rect.IsRounded;
var needRoundRect = rect.IsRounded || (boxShadows.HasInsetShadows); var needRoundRect = rect.IsRounded || (boxShadows.HasInsetShadows);
SKRoundRect skRoundRect = null; SKRoundRect? skRoundRect = null;
if (needRoundRect) if (needRoundRect)
{ {
skRoundRect = SKRoundRectCache.Shared.GetAndSetRadii(rc, rect); skRoundRect = SKRoundRectCache.Shared.GetAndSetRadii(rc, rect);
@ -412,15 +413,15 @@ namespace Avalonia.Skia
if (brush != null) if (brush != null)
{ {
using (var paint = CreatePaint(_fillPaint, brush, rect.Rect.Size)) using (var fill = CreatePaint(_fillPaint, brush, rect.Rect.Size))
{ {
if (isRounded) if (isRounded)
{ {
Canvas.DrawRoundRect(skRoundRect, paint.Paint); Canvas.DrawRoundRect(skRoundRect, fill.Paint);
} }
else else
{ {
Canvas.DrawRect(rc, paint.Paint); Canvas.DrawRect(rc, fill.Paint);
} }
} }
} }
@ -454,30 +455,28 @@ namespace Avalonia.Skia
} }
} }
if (pen?.Brush != null) if (pen is not null
&& TryCreatePaint(_strokePaint, pen, rect.Rect.Size.Inflate(new Thickness(pen.Thickness / 2))) is { } stroke)
{ {
using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) using (stroke)
{ {
if (paint.Paint is object) if (isRounded)
{ {
if (isRounded) Canvas.DrawRoundRect(skRoundRect, stroke.Paint);
{ }
Canvas.DrawRoundRect(skRoundRect, paint.Paint); else
} {
else Canvas.DrawRect(rc, stroke.Paint);
{
Canvas.DrawRect(rc, paint.Paint);
}
} }
} }
} }
if(isRounded) if (skRoundRect is not null)
SKRoundRectCache.Shared.Return(skRoundRect); SKRoundRectCache.Shared.Return(skRoundRect);
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawEllipse(IBrush brush, IPen pen, Rect rect) public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect)
{ {
if (rect.Height <= 0 || rect.Width <= 0) if (rect.Height <= 0 || rect.Width <= 0)
return; return;
@ -487,26 +486,24 @@ namespace Avalonia.Skia
if (brush != null) if (brush != null)
{ {
using (var paint = CreatePaint(_fillPaint, brush, rect.Size)) using (var fill = CreatePaint(_fillPaint, brush, rect.Size))
{ {
Canvas.DrawOval(rc, paint.Paint); Canvas.DrawOval(rc, fill.Paint);
} }
} }
if (pen?.Brush != null) if (pen is not null
&& TryCreatePaint(_strokePaint, pen, rect.Size.Inflate(new Thickness(pen.Thickness / 2))) is { } stroke)
{ {
using (var paint = CreatePaint(_strokePaint, pen, rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) using (stroke)
{ {
if (paint.Paint is object) Canvas.DrawOval(rc, stroke.Paint);
{
Canvas.DrawOval(rc, paint.Paint);
}
} }
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun) public void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun)
{ {
CheckLease(); CheckLease();
@ -711,14 +708,12 @@ namespace Avalonia.Skia
} }
} }
#nullable enable
public object? GetFeature(Type t) public object? GetFeature(Type t)
{ {
if (t == typeof(ISkiaSharpApiLeaseFeature)) if (t == typeof(ISkiaSharpApiLeaseFeature))
return new SkiaLeaseFeature(this); return new SkiaLeaseFeature(this);
return null; return null;
} }
#nullable restore
/// <summary> /// <summary>
/// Configure paint wrapper for using gradient brush. /// Configure paint wrapper for using gradient brush.
@ -957,9 +952,10 @@ namespace Avalonia.Skia
/// <param name="visualBrush">Visual brush.</param> /// <param name="visualBrush">Visual brush.</param>
/// <param name="visualBrushRenderer">Visual brush renderer.</param> /// <param name="visualBrushRenderer">Visual brush renderer.</param>
/// <param name="tileBrushImage">Tile brush image.</param> /// <param name="tileBrushImage">Tile brush image.</param>
private void ConfigureVisualBrush(ref PaintWrapper paintWrapper, IVisualBrush visualBrush, IVisualBrushRenderer visualBrushRenderer, ref IDrawableBitmapImpl tileBrushImage) private void ConfigureVisualBrush(ref PaintWrapper paintWrapper, IVisualBrush visualBrush,
IVisualBrushRenderer? visualBrushRenderer, ref IDrawableBitmapImpl? tileBrushImage)
{ {
if (_visualBrushRenderer == null) if (visualBrushRenderer == null)
{ {
throw new NotSupportedException("No IVisualBrushRenderer was supplied to DrawingContextImpl."); throw new NotSupportedException("No IVisualBrushRenderer was supplied to DrawingContextImpl.");
} }
@ -982,7 +978,7 @@ namespace Avalonia.Skia
} }
} }
static SKColorFilter CreateAlphaColorFilter(double opacity) private static SKColorFilter CreateAlphaColorFilter(double opacity)
{ {
if (opacity > 1) if (opacity > 1)
opacity = 1; opacity = 1;
@ -997,7 +993,7 @@ namespace Avalonia.Skia
return SKColorFilter.CreateTable(a, c, c, c); return SKColorFilter.CreateTable(a, c, c, c);
} }
static byte Blend(byte leftColor, byte leftAlpha, byte rightColor, byte rightAlpha) private static byte Blend(byte leftColor, byte leftAlpha, byte rightColor, byte rightAlpha)
{ {
var ca = leftColor / 255d; var ca = leftColor / 255d;
var aa = leftAlpha / 255d; var aa = leftAlpha / 255d;
@ -1007,7 +1003,7 @@ namespace Avalonia.Skia
return (byte)(r * 255); return (byte)(r * 255);
} }
static Color Blend(Color left, Color right) private static Color Blend(Color left, Color right)
{ {
var aa = left.A / 255d; var aa = left.A / 255d;
var ab = right.A / 255d; var ab = right.A / 255d;
@ -1103,7 +1099,7 @@ namespace Avalonia.Skia
} }
else else
{ {
tileBrushImage = (IDrawableBitmapImpl)(tileBrush as IImageBrush)?.Source?.PlatformImpl.Item; tileBrushImage = (tileBrush as IImageBrush)?.Source?.PlatformImpl.Item as IDrawableBitmapImpl;
} }
if (tileBrush != null && tileBrushImage != null) if (tileBrush != null && tileBrushImage != null)
@ -1125,16 +1121,16 @@ namespace Avalonia.Skia
/// <param name="pen">Source pen.</param> /// <param name="pen">Source pen.</param>
/// <param name="targetSize">Target size.</param> /// <param name="targetSize">Target size.</param>
/// <returns></returns> /// <returns></returns>
private PaintWrapper CreatePaint(SKPaint paint, IPen pen, Size targetSize) private PaintWrapper? TryCreatePaint(SKPaint paint, IPen pen, Size targetSize)
{ {
// In Skia 0 thickness means - use hairline rendering // In Skia 0 thickness means - use hairline rendering
// and for us it means - there is nothing rendered. // and for us it means - there is nothing rendered.
if (pen.Thickness == 0d) if (pen.Brush is not { } brush || pen.Thickness == 0d)
{ {
return default; return null;
} }
var rv = CreatePaint(paint, pen.Brush, targetSize); var rv = CreatePaint(paint, brush, targetSize);
paint.IsStroke = true; paint.IsStroke = true;
paint.StrokeWidth = (float) pen.Thickness; paint.StrokeWidth = (float) pen.Thickness;
@ -1253,9 +1249,9 @@ namespace Avalonia.Skia
//We are saving memory allocations there //We are saving memory allocations there
public readonly SKPaint Paint; public readonly SKPaint Paint;
private IDisposable _disposable1; private IDisposable? _disposable1;
private IDisposable _disposable2; private IDisposable? _disposable2;
private IDisposable _disposable3; private IDisposable? _disposable3;
public PaintWrapper(SKPaint paint) public PaintWrapper(SKPaint paint)
{ {

21
src/Skia/Avalonia.Skia/FontManagerImpl.cs

@ -26,11 +26,11 @@ namespace Avalonia.Skia
return _skFontManager.FontFamilies; return _skFontManager.FontFamilies;
} }
[ThreadStatic] private static string[] t_languageTagBuffer; [ThreadStatic] private static string[]? t_languageTagBuffer;
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontStretch fontStretch, FontWeight fontWeight, FontStretch fontStretch,
FontFamily fontFamily, CultureInfo culture, out Typeface fontKey) FontFamily? fontFamily, CultureInfo? culture, out Typeface fontKey)
{ {
SKFontStyle skFontStyle; SKFontStyle skFontStyle;
@ -53,20 +53,13 @@ namespace Avalonia.Skia
break; break;
} }
if (culture == null) culture ??= CultureInfo.CurrentUICulture;
{
culture = CultureInfo.CurrentUICulture;
}
if (t_languageTagBuffer == null)
{
t_languageTagBuffer = new string[2];
}
t_languageTagBuffer ??= new string[2];
t_languageTagBuffer[0] = culture.TwoLetterISOLanguageName; t_languageTagBuffer[0] = culture.TwoLetterISOLanguageName;
t_languageTagBuffer[1] = culture.ThreeLetterISOLanguageName; t_languageTagBuffer[1] = culture.ThreeLetterISOLanguageName;
if (fontFamily != null && fontFamily.FamilyNames.HasFallbacks) if (fontFamily is not null && fontFamily.FamilyNames.HasFallbacks)
{ {
var familyNames = fontFamily.FamilyNames; var familyNames = fontFamily.FamilyNames;
@ -104,9 +97,9 @@ namespace Avalonia.Skia
public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{ {
SKTypeface skTypeface = null; SKTypeface? skTypeface = null;
if (typeface.FontFamily.Key == null) if (typeface.FontFamily.Key is null)
{ {
var defaultName = SKTypeface.Default.FamilyName; var defaultName = SKTypeface.Default.FamilyName;

10
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
@ -15,9 +16,9 @@ namespace Avalonia.Skia
private readonly IFramebufferPlatformSurface _platformSurface; private readonly IFramebufferPlatformSurface _platformSurface;
private SKImageInfo _currentImageInfo; private SKImageInfo _currentImageInfo;
private IntPtr _currentFramebufferAddress; private IntPtr _currentFramebufferAddress;
private SKSurface _framebufferSurface; private SKSurface? _framebufferSurface;
private PixelFormatConversionShim _conversionShim; private PixelFormatConversionShim? _conversionShim;
private IDisposable _preFramebufferCopyHandler; private IDisposable? _preFramebufferCopyHandler;
/// <summary> /// <summary>
/// Create new framebuffer render target using a target surface. /// Create new framebuffer render target using a target surface.
@ -35,7 +36,7 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{ {
var framebuffer = _platformSurface.Lock(); var framebuffer = _platformSurface.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height, var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
@ -81,6 +82,7 @@ namespace Avalonia.Skia
/// </summary> /// </summary>
/// <param name="desiredImageInfo">Desired image info.</param> /// <param name="desiredImageInfo">Desired image info.</param>
/// <param name="framebuffer">Backing framebuffer.</param> /// <param name="framebuffer">Backing framebuffer.</param>
[MemberNotNull(nameof(_framebufferSurface))]
private void CreateSurface(SKImageInfo desiredImageInfo, ILockedFramebuffer framebuffer) private void CreateSurface(SKImageInfo desiredImageInfo, ILockedFramebuffer framebuffer)
{ {
if (_framebufferSurface != null && AreImageInfosCompatible(_currentImageInfo, desiredImageInfo) && _currentFramebufferAddress == framebuffer.Address) if (_framebufferSurface != null && AreImageInfosCompatible(_currentImageInfo, desiredImageInfo) && _currentFramebufferAddress == framebuffer.Address)

8
src/Skia/Avalonia.Skia/GeometryGroupImpl.cs

@ -2,8 +2,6 @@ using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using SkiaSharp; using SkiaSharp;
#nullable enable
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
/// <summary> /// <summary>
@ -22,8 +20,10 @@ namespace Avalonia.Skia
for (var i = 0; i < count; ++i) for (var i = 0; i < count; ++i)
{ {
if (children[i]?.PlatformImpl is GeometryImpl child) if (children[i].PlatformImpl is GeometryImpl { EffectivePath: { } effectivePath })
path.AddPath(child.EffectivePath); {
path.AddPath(effectivePath);
}
} }
EffectivePath = path; EffectivePath = path;

44
src/Skia/Avalonia.Skia/GeometryImpl.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using SkiaSharp; using SkiaSharp;
@ -11,20 +12,9 @@ namespace Avalonia.Skia
internal abstract class GeometryImpl : IGeometryImpl internal abstract class GeometryImpl : IGeometryImpl
{ {
private PathCache _pathCache; private PathCache _pathCache;
private SKPathMeasure _pathMeasureCache; private SKPathMeasure? _cachedPathMeasure;
private SKPathMeasure CachedPathMeasure private SKPathMeasure CachedPathMeasure => _cachedPathMeasure ??= new SKPathMeasure(EffectivePath!);
{
get
{
if (_pathMeasureCache is null)
{
_pathMeasureCache = new SKPathMeasure(EffectivePath);
}
return _pathMeasureCache;
}
}
/// <inheritdoc /> /// <inheritdoc />
public abstract Rect Bounds { get; } public abstract Rect Bounds { get; }
@ -37,11 +27,11 @@ namespace Avalonia.Skia
if (EffectivePath is null) if (EffectivePath is null)
return 0; return 0;
return (double)CachedPathMeasure?.Length; return CachedPathMeasure.Length;
} }
} }
public abstract SKPath EffectivePath { get; } public abstract SKPath? EffectivePath { get; }
/// <inheritdoc /> /// <inheritdoc />
public bool FillContains(Point point) public bool FillContains(Point point)
@ -50,7 +40,7 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public bool StrokeContains(IPen pen, Point point) public bool StrokeContains(IPen? pen, Point point)
{ {
// Skia requires to compute stroke path to check for point containment. // Skia requires to compute stroke path to check for point containment.
// Due to that we are caching using stroke width. // Due to that we are caching using stroke width.
@ -98,21 +88,26 @@ namespace Avalonia.Skia
/// <param name="path">Path to check.</param> /// <param name="path">Path to check.</param>
/// <param name="point">Point.</param> /// <param name="point">Point.</param>
/// <returns>True, if point is contained in a path.</returns> /// <returns>True, if point is contained in a path.</returns>
private static bool PathContainsCore(SKPath path, Point point) private static bool PathContainsCore(SKPath? path, Point point)
{ {
return path.Contains((float)point.X, (float)point.Y); return path is not null && path.Contains((float)point.X, (float)point.Y);
} }
/// <inheritdoc /> /// <inheritdoc />
public IGeometryImpl Intersect(IGeometryImpl geometry) public IGeometryImpl? Intersect(IGeometryImpl geometry)
{ {
var result = EffectivePath.Op(((GeometryImpl)geometry).EffectivePath, SKPathOp.Intersect); if (EffectivePath is { } path
&& (geometry as GeometryImpl)?.EffectivePath is { } otherPath
&& path.Op(otherPath, SKPathOp.Intersect) is { } result)
{
return new StreamGeometryImpl(result);
}
return result == null ? null : new StreamGeometryImpl(result); return null;
} }
/// <inheritdoc /> /// <inheritdoc />
public Rect GetRenderBounds(IPen pen) public Rect GetRenderBounds(IPen? pen)
{ {
var strokeWidth = (float)(pen?.Thickness ?? 0); var strokeWidth = (float)(pen?.Thickness ?? 0);
@ -161,7 +156,7 @@ namespace Avalonia.Skia
} }
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure,
out IGeometryImpl segmentGeometry) [NotNullWhen(true)] out IGeometryImpl? segmentGeometry)
{ {
if (EffectivePath is null) if (EffectivePath is null)
{ {
@ -203,7 +198,7 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// Cached contour path. /// Cached contour path.
/// </summary> /// </summary>
public SKPath CachedStrokePath { get; private set; } public SKPath? CachedStrokePath { get; private set; }
/// <summary> /// <summary>
/// Cached geometry render bounds. /// Cached geometry render bounds.
@ -244,6 +239,7 @@ namespace Avalonia.Skia
public void Invalidate() public void Invalidate()
{ {
CachedStrokePath?.Dispose(); CachedStrokePath?.Dispose();
CachedStrokePath = null;
CachedGeometryRenderBounds = default; CachedGeometryRenderBounds = default;
_cachedStrokeWidth = default; _cachedStrokeWidth = default;
} }

3
src/Skia/Avalonia.Skia/GlyphRunImpl.cs

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Platform; using Avalonia.Platform;
using SkiaSharp; using SkiaSharp;
#nullable enable
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
@ -10,7 +9,7 @@ namespace Avalonia.Skia
{ {
public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin) public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin)
{ {
TextBlob = textBlob ?? throw new ArgumentNullException (nameof (textBlob)); TextBlob = textBlob ?? throw new ArgumentNullException(nameof(textBlob));
Size = size; Size = size;

6
src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs

@ -140,7 +140,7 @@ namespace Avalonia.Skia
return Font.GetHorizontalGlyphAdvances(glyphIndices); return Font.GetHorizontalGlyphAdvances(glyphIndices);
} }
private Blob GetTable(Face face, Tag tag) private Blob? GetTable(Face face, Tag tag)
{ {
var size = Typeface.GetTableSize(tag); var size = Typeface.GetTableSize(tag);
@ -166,8 +166,8 @@ namespace Avalonia.Skia
return; return;
} }
Font?.Dispose(); Font.Dispose();
Face?.Dispose(); Face.Dispose();
} }
public void Dispose() public void Dispose()

4
src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs

@ -15,14 +15,14 @@ namespace Avalonia.Skia
/// </summary> /// </summary>
/// <param name="surfaces">Surfaces.</param> /// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns> /// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces); ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces);
/// <summary> /// <summary>
/// Creates an offscreen render target surface /// Creates an offscreen render target surface
/// </summary> /// </summary>
/// <param name="size">size in pixels.</param> /// <param name="size">size in pixels.</param>
/// <param name="session">An optional custom render session.</param> /// <param name="session">An optional custom render session.</param>
ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session); ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session);
} }
public interface ISkiaSurface : IDisposable public interface ISkiaSurface : IDisposable

18
src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs

@ -3,6 +3,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform; using Avalonia.Platform;
using SkiaSharp; using SkiaSharp;
using static Avalonia.OpenGL.GlConsts; using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
internal class FboSkiaSurface : ISkiaSurface internal class FboSkiaSurface : ISkiaSurface
@ -14,6 +15,7 @@ namespace Avalonia.Skia
private int _fbo; private int _fbo;
private int _depthStencil; private int _depthStencil;
private int _texture; private int _texture;
private SKSurface? _surface;
private static readonly bool[] TrueFalse = new[] { true, false }; private static readonly bool[] TrueFalse = new[] { true, false };
public FboSkiaSurface(GlSkiaGpu gpu, GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin) public FboSkiaSurface(GlSkiaGpu gpu, GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin)
@ -89,7 +91,7 @@ namespace Avalonia.Skia
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8, var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat())); new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target, _surface = SKSurface.Create(_grContext, target,
surfaceOrigin, SKColorType.Rgba8888, new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal)); surfaceOrigin, SKColorType.Rgba8888, new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal));
CanBlit = gl.IsBlitFramebufferAvailable; CanBlit = gl.IsBlitFramebufferAvailable;
} }
@ -100,8 +102,8 @@ namespace Avalonia.Skia
{ {
using (_glContext.EnsureCurrent()) using (_glContext.EnsureCurrent())
{ {
Surface?.Dispose(); _surface?.Dispose();
Surface = null; _surface = null;
var gl = _glContext.GlInterface; var gl = _glContext.GlInterface;
if (_fbo != 0) if (_fbo != 0)
{ {
@ -113,11 +115,11 @@ namespace Avalonia.Skia
} }
catch (PlatformGraphicsContextLostException) catch (PlatformGraphicsContextLostException)
{ {
if (Surface != null) if (_surface != null)
// We need to dispose SKSurface _after_ GRContext.Abandon was called, // We need to dispose SKSurface _after_ GRContext.Abandon was called,
// otherwise it will try to do OpenGL calls without a proper context // otherwise it will try to do OpenGL calls without a proper context
_gpu.AddPostDispose(Surface.Dispose); _gpu.AddPostDispose(_surface.Dispose);
Surface = null; _surface = null;
} }
finally finally
{ {
@ -125,8 +127,10 @@ namespace Avalonia.Skia
} }
} }
public SKSurface Surface { get; private set; } public SKSurface Surface => _surface ?? throw new ObjectDisposedException(nameof(FboSkiaSurface));
public bool CanBlit { get; } public bool CanBlit { get; }
public void Blit(SKCanvas canvas) public void Blit(SKCanvas canvas)
{ {
// This should set the render target as the current FBO // This should set the render target as the current FBO

11
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs

@ -1,4 +1,3 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.OpenGL; using Avalonia.OpenGL;
@ -150,6 +149,11 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex) public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex)
{ {
if (_image is null)
{
throw new NotSupportedException("Only supported with an external image");
}
using (_gpu.EnsureCurrent()) using (_gpu.EnsureCurrent())
{ {
_image.AcquireKeyedMutex(acquireIndex); _image.AcquireKeyedMutex(acquireIndex);
@ -167,6 +171,11 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
IPlatformRenderInterfaceImportedSemaphore signalSemaphore) IPlatformRenderInterfaceImportedSemaphore signalSemaphore)
{ {
if (_image is null)
{
throw new NotSupportedException("Only supported with an external image");
}
var wait = (GlSkiaImportedSemaphore)waitForSemaphore; var wait = (GlSkiaImportedSemaphore)waitForSemaphore;
var signal = (GlSkiaImportedSemaphore)signalSemaphore; var signal = (GlSkiaImportedSemaphore)signalSemaphore;
using (_gpu.EnsureCurrent()) using (_gpu.EnsureCurrent())

20
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@ -10,15 +10,15 @@ using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
class GlSkiaGpu : ISkiaGpu, IOpenGlTextureSharingRenderInterfaceContextFeature internal class GlSkiaGpu : ISkiaGpu, IOpenGlTextureSharingRenderInterfaceContextFeature
{ {
private GRContext _grContext; private readonly GRContext _grContext;
private IGlContext _glContext; private readonly IGlContext _glContext;
public GRContext GrContext => _grContext; public GRContext GrContext => _grContext;
public IGlContext GlContext => _glContext; public IGlContext GlContext => _glContext;
private List<Action> _postDisposeCallbacks = new(); private readonly List<Action> _postDisposeCallbacks = new();
private bool? _canCreateSurfaces; private bool? _canCreateSurfaces;
private IExternalObjectsRenderInterfaceContextFeature? _externalObjectsFeature; private readonly IExternalObjectsRenderInterfaceContextFeature? _externalObjectsFeature;
public GlSkiaGpu(IGlContext context, long? maxResourceBytes) public GlSkiaGpu(IGlContext context, long? maxResourceBytes)
{ {
@ -41,7 +41,7 @@ namespace Avalonia.Skia
} }
} }
class SurfaceWrapper : IGlPlatformSurface private class SurfaceWrapper : IGlPlatformSurface
{ {
private readonly object _surface; private readonly object _surface;
@ -57,7 +57,7 @@ namespace Avalonia.Skia
} }
} }
public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces) public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces)
{ {
var customRenderTargetFactory = _glContext.TryGetFeature<IGlPlatformSurfaceRenderTargetFactory>(); var customRenderTargetFactory = _glContext.TryGetFeature<IGlPlatformSurfaceRenderTargetFactory>();
foreach (var surface in surfaces) foreach (var surface in surfaces)
@ -75,7 +75,7 @@ namespace Avalonia.Skia
return null; return null;
} }
public ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session) public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session)
{ {
// Only windows platform needs our FBO trickery // Only windows platform needs our FBO trickery
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -106,7 +106,7 @@ namespace Avalonia.Skia
public bool CanCreateSharedContext => _glContext.CanCreateSharedContext; public bool CanCreateSharedContext => _glContext.CanCreateSharedContext;
public IGlContext CreateSharedContext(IEnumerable<GlVersion> preferredVersions = null) => public IGlContext? CreateSharedContext(IEnumerable<GlVersion>? preferredVersions = null) =>
_glContext.CreateSharedContext(preferredVersions); _glContext.CreateSharedContext(preferredVersions);
public ICompositionImportableOpenGlSharedTexture CreateSharedTextureForComposition(IGlContext context, PixelSize size) public ICompositionImportableOpenGlSharedTexture CreateSharedTextureForComposition(IGlContext context, PixelSize size)
@ -153,7 +153,7 @@ namespace Avalonia.Skia
public bool IsLost => _glContext.IsLost; public bool IsLost => _glContext.IsLost;
public IDisposable EnsureCurrent() => _glContext.EnsureCurrent(); public IDisposable EnsureCurrent() => _glContext.EnsureCurrent();
public object TryGetFeature(Type featureType) public object? TryGetFeature(Type featureType)
{ {
if (featureType == typeof(IOpenGlTextureSharingRenderInterfaceContextFeature)) if (featureType == typeof(IOpenGlTextureSharingRenderInterfaceContextFeature))
return this; return this;

2
src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs

@ -22,7 +22,7 @@ namespace Avalonia.Skia
_renderTarget.Dispose(); _renderTarget.Dispose();
} }
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{ {
var session = _renderTarget.BeginRenderingSession(); var session = _renderTarget.BeginRenderingSession();

2
src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs

@ -15,7 +15,7 @@ namespace Avalonia.Skia.Helpers
/// <param name="dpi"></param> /// <param name="dpi"></param>
/// <param name="visualBrushRenderer"></param> /// <param name="visualBrushRenderer"></param>
/// <returns>DrawingContext</returns> /// <returns>DrawingContext</returns>
public static IDrawingContextImpl WrapSkiaCanvas(SKCanvas canvas, Vector dpi, IVisualBrushRenderer visualBrushRenderer = null) public static IDrawingContextImpl WrapSkiaCanvas(SKCanvas canvas, Vector dpi, IVisualBrushRenderer? visualBrushRenderer = null)
{ {
var createInfo = new DrawingContextImpl.CreateInfo var createInfo = new DrawingContextImpl.CreateInfo
{ {

6
src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs

@ -14,7 +14,7 @@ public interface ISkiaSharpApiLeaseFeature
public interface ISkiaSharpApiLease : IDisposable public interface ISkiaSharpApiLease : IDisposable
{ {
SKCanvas SkCanvas { get; } SKCanvas SkCanvas { get; }
GRContext GrContext { get; } GRContext? GrContext { get; }
SKSurface SkSurface { get; } SKSurface? SkSurface { get; }
double CurrentOpacity { get; } double CurrentOpacity { get; }
} }

15
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -100,7 +100,7 @@ namespace Avalonia.Skia
_bitmap = scaledBmp; _bitmap = scaledBmp;
} }
_bitmap!.SetImmutable(); _bitmap.SetImmutable();
_image = SKImage.FromBitmap(_bitmap); _image = SKImage.FromBitmap(_bitmap);
@ -134,7 +134,7 @@ namespace Avalonia.Skia
data); data);
_bitmap = tmp.Copy(); _bitmap = tmp.Copy();
} }
_bitmap!.SetImmutable(); _bitmap.SetImmutable();
_image = SKImage.FromBitmap(_bitmap); _image = SKImage.FromBitmap(_bitmap);
if (_image == null) if (_image == null)
@ -179,10 +179,13 @@ namespace Avalonia.Skia
public PixelFormat? Format => _bitmap?.ColorType.ToAvalonia(); public PixelFormat? Format => _bitmap?.ColorType.ToAvalonia();
public ILockedFramebuffer Lock() public ILockedFramebuffer Lock()
{ {
if (_bitmap == null) if (_bitmap is null)
throw new NotSupportedException(); throw new NotSupportedException("A bitmap is needed for locking");
return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi,
_bitmap.ColorType.ToAvalonia().Value, null); if (_bitmap.ColorType.ToAvalonia() is not { } format)
throw new NotSupportedException($"Unsupported format {_bitmap.ColorType}");
return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi, format, null);
} }
} }
} }

4
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -24,7 +24,7 @@ namespace Avalonia.Skia
} }
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext? graphicsContext)
{ {
if (graphicsContext == null) if (graphicsContext == null)
return new SkiaContext(null); return new SkiaContext(null);
@ -225,7 +225,7 @@ namespace Avalonia.Skia
throw new ArgumentNullException(nameof(glyphInfos)); throw new ArgumentNullException(nameof(glyphInfos));
} }
var glyphTypefaceImpl = glyphTypeface as GlyphTypefaceImpl; var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
var font = SKFontCache.Shared.Get(); var font = SKFontCache.Shared.Get();

14
src/Skia/Avalonia.Skia/SKTypefaceCollection.cs

@ -1,4 +1,5 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media; using Avalonia.Media;
using SkiaSharp; using SkiaSharp;
@ -6,20 +7,19 @@ namespace Avalonia.Skia
{ {
internal class SKTypefaceCollection internal class SKTypefaceCollection
{ {
private readonly ConcurrentDictionary<Typeface, SKTypeface> _typefaces = private readonly ConcurrentDictionary<Typeface, SKTypeface> _typefaces = new();
new ConcurrentDictionary<Typeface, SKTypeface>();
public void AddTypeface(Typeface key, SKTypeface typeface) public void AddTypeface(Typeface key, SKTypeface typeface)
{ {
_typefaces.TryAdd(key, typeface); _typefaces.TryAdd(key, typeface);
} }
public SKTypeface Get(Typeface typeface) public SKTypeface? Get(Typeface typeface)
{ {
return GetNearestMatch(typeface); return GetNearestMatch(typeface);
} }
private SKTypeface GetNearestMatch(Typeface key) private SKTypeface? GetNearestMatch(Typeface key)
{ {
if (_typefaces.Count == 0) if (_typefaces.Count == 0)
{ {
@ -70,7 +70,7 @@ namespace Avalonia.Skia
return typeface; return typeface;
} }
SKTypeface skTypeface = null; SKTypeface? skTypeface = null;
foreach(var pair in _typefaces) foreach(var pair in _typefaces)
{ {
@ -85,7 +85,7 @@ namespace Avalonia.Skia
return skTypeface; return skTypeface;
} }
private bool TryFindStretchFallback(Typeface key, out SKTypeface typeface) private bool TryFindStretchFallback(Typeface key, [NotNullWhen(true)] out SKTypeface? typeface)
{ {
typeface = null; typeface = null;
var stretch = (int)key.Stretch; var stretch = (int)key.Stretch;
@ -114,7 +114,7 @@ namespace Avalonia.Skia
return false; return false;
} }
private bool TryFindWeightFallback(Typeface key, out SKTypeface typeface) private bool TryFindWeightFallback(Typeface key, [NotNullWhen(true)] out SKTypeface? typeface)
{ {
typeface = null; typeface = null;
var weight = (int)key.Weight; var weight = (int)key.Weight;

11
src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs

@ -23,7 +23,7 @@ namespace Avalonia.Skia
/// <returns></returns> /// <returns></returns>
public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily) public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily)
{ {
return s_cachedCollections.GetOrAdd(fontFamily, x => CreateCustomFontCollection(fontFamily)); return s_cachedCollections.GetOrAdd(fontFamily, CreateCustomFontCollection);
} }
/// <summary> /// <summary>
@ -33,10 +33,15 @@ namespace Avalonia.Skia
/// <returns></returns> /// <returns></returns>
private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily) private static SKTypefaceCollection CreateCustomFontCollection(FontFamily fontFamily)
{ {
var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamily.Key);
var typeFaceCollection = new SKTypefaceCollection(); var typeFaceCollection = new SKTypefaceCollection();
if (fontFamily.Key is not { } fontFamilyKey)
{
return typeFaceCollection;
}
var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamilyKey);
var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>(); var assetLoader = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
foreach (var asset in fontAssets) foreach (var asset in fontAssets)

14
src/Skia/Avalonia.Skia/SkiaBackendContext.cs

@ -9,9 +9,9 @@ namespace Avalonia.Skia;
internal class SkiaContext : IPlatformRenderInterfaceContext internal class SkiaContext : IPlatformRenderInterfaceContext
{ {
private ISkiaGpu _gpu; private ISkiaGpu? _gpu;
public SkiaContext(ISkiaGpu gpu) public SkiaContext(ISkiaGpu? gpu)
{ {
_gpu = gpu; _gpu = gpu;
} }
@ -25,10 +25,10 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
/// <inheritdoc /> /// <inheritdoc />
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{ {
if (!(surfaces is IList)) if (surfaces is not IList)
surfaces = surfaces.ToList(); surfaces = surfaces.ToList();
var gpuRenderTarget = _gpu?.TryCreateRenderTarget(surfaces);
if (gpuRenderTarget != null) if (_gpu?.TryCreateRenderTarget(surfaces) is { } gpuRenderTarget)
{ {
return new SkiaGpuRenderTarget(_gpu, gpuRenderTarget); return new SkiaGpuRenderTarget(_gpu, gpuRenderTarget);
} }
@ -43,7 +43,7 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
"Don't know how to create a Skia render target from any of provided surfaces"); "Don't know how to create a Skia render target from any of provided surfaces");
} }
public bool IsLost => _gpu.IsLost; public bool IsLost => _gpu?.IsLost ?? false;
public object TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType); public object? TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType);
} }

14
src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@ -111,7 +112,7 @@ namespace Avalonia.Skia
return sm; return sm;
} }
public static SKColor ToSKColor(this Media.Color c) public static SKColor ToSKColor(this Color c)
{ {
return new SKColor(c.R, c.G, c.B, c.A); return new SKColor(c.R, c.G, c.B, c.A);
} }
@ -171,14 +172,14 @@ namespace Avalonia.Skia
}; };
} }
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m) public static SKShaderTileMode ToSKShaderTileMode(this GradientSpreadMethod m)
{ {
switch (m) switch (m)
{ {
default: default:
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp; case GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror; case GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat; case GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
} }
} }
@ -215,7 +216,8 @@ namespace Avalonia.Skia
}; };
} }
public static SKPath Clone(this SKPath src) [return: NotNullIfNotNull(nameof(src))]
public static SKPath? Clone(this SKPath? src)
{ {
return src != null ? new SKPath(src) : null; return src != null ? new SKPath(src) : null;
} }

2
src/Skia/Avalonia.Skia/StreamGeometryImpl.cs

@ -47,7 +47,7 @@ namespace Avalonia.Skia
/// <inheritdoc /> /// <inheritdoc />
public IStreamGeometryImpl Clone() public IStreamGeometryImpl Clone()
{ {
return new StreamGeometryImpl(_effectivePath?.Clone(), Bounds); return new StreamGeometryImpl(_effectivePath.Clone(), Bounds);
} }
/// <inheritdoc /> /// <inheritdoc />

50
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -1,7 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Skia.Helpers; using Avalonia.Skia.Helpers;
@ -17,24 +16,26 @@ namespace Avalonia.Skia
private readonly ISkiaSurface _surface; private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas; private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering; private readonly bool _disableLcdRendering;
private readonly GRContext _grContext; private readonly GRContext? _grContext;
private readonly ISkiaGpu _gpu; private readonly ISkiaGpu? _gpu;
class SkiaSurfaceWrapper : ISkiaSurface private class SkiaSurfaceWrapper : ISkiaSurface
{ {
public SKSurface Surface { get; private set; } private SKSurface? _surface;
public SKSurface Surface => _surface ?? throw new ObjectDisposedException(nameof(SkiaSurfaceWrapper));
public bool CanBlit => false; public bool CanBlit => false;
public void Blit(SKCanvas canvas) => throw new NotSupportedException(); public void Blit(SKCanvas canvas) => throw new NotSupportedException();
public SkiaSurfaceWrapper(SKSurface surface) public SkiaSurfaceWrapper(SKSurface surface)
{ {
Surface = surface; _surface = surface;
} }
public void Dispose() public void Dispose()
{ {
Surface?.Dispose(); _surface?.Dispose();
Surface = null; _surface = null;
} }
} }
@ -51,18 +52,25 @@ namespace Avalonia.Skia
_grContext = createInfo.GrContext; _grContext = createInfo.GrContext;
_gpu = createInfo.Gpu; _gpu = createInfo.Gpu;
if (!createInfo.DisableManualFbo) ISkiaSurface? surface = null;
_surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
_canvas = _surface?.Surface.Canvas; if (!createInfo.DisableManualFbo)
surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null || _canvas == null) if (surface is null)
{ {
throw new InvalidOperationException("Failed to create Skia render target surface"); if (CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format)
is { } skSurface)
{
surface = new SkiaSurfaceWrapper(skSurface);
}
} }
if (surface?.Surface.Canvas is not { } canvas)
throw new InvalidOperationException("Failed to create Skia render target surface");
_surface = surface;
_canvas = canvas;
} }
/// <summary> /// <summary>
@ -73,7 +81,7 @@ namespace Avalonia.Skia
/// <param name="height">Height.</param> /// <param name="height">Height.</param>
/// <param name="format">Format.</param> /// <param name="format">Format.</param>
/// <returns></returns> /// <returns></returns>
private static SKSurface CreateSurface(GRContext gpu, int width, int height, PixelFormat? format) private static SKSurface? CreateSurface(GRContext? gpu, int width, int height, PixelFormat? format)
{ {
var imageInfo = MakeImageInfo(width, height, format); var imageInfo = MakeImageInfo(width, height, format);
if (gpu != null) if (gpu != null)
@ -89,7 +97,7 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{ {
_canvas.RestoreToCount(-1); _canvas.RestoreToCount(-1);
_canvas.ResetMatrix(); _canvas.ResetMatrix();
@ -218,11 +226,11 @@ namespace Avalonia.Skia
/// <summary> /// <summary>
/// GPU-accelerated context (optional) /// GPU-accelerated context (optional)
/// </summary> /// </summary>
public GRContext GrContext; public GRContext? GrContext;
public ISkiaGpu Gpu; public ISkiaGpu? Gpu;
public ISkiaGpuRenderSession Session; public ISkiaGpuRenderSession? Session;
public bool DisableManualFbo; public bool DisableManualFbo;
} }

4
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -37,7 +37,7 @@ namespace Avalonia.Skia
var usedCulture = culture ?? CultureInfo.CurrentCulture; var usedCulture = culture ?? CultureInfo.CurrentCulture;
buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, i => new Language(usedCulture)); buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture));
var font = ((GlyphTypefaceImpl)typeface).Font; var font = ((GlyphTypefaceImpl)typeface).Font;
@ -170,7 +170,7 @@ namespace Avalonia.Skia
return segment.Array.AsMemory(); return segment.Array.AsMemory();
} }
if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<char> memoryManager, out start, out length)) if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<char>? memoryManager, out start, out length))
{ {
return memoryManager.Memory; return memoryManager.Memory;
} }

6
src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs

@ -19,14 +19,14 @@ namespace Avalonia.Skia
Transform = transform; Transform = transform;
var transformedPath = source.EffectivePath.Clone(); var transformedPath = source.EffectivePath.Clone();
transformedPath.Transform(transform.ToSKMatrix()); transformedPath?.Transform(transform.ToSKMatrix());
EffectivePath = transformedPath; EffectivePath = transformedPath;
Bounds = transformedPath.TightBounds.ToAvaloniaRect(); Bounds = transformedPath?.TightBounds.ToAvaloniaRect() ?? default;
} }
/// <inheritdoc /> /// <inheritdoc />
public override SKPath EffectivePath { get; } public override SKPath? EffectivePath { get; }
/// <inheritdoc /> /// <inheritdoc />
public IGeometryImpl SourceGeometry { get; } public IGeometryImpl SourceGeometry { get; }

6
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -15,7 +15,7 @@ namespace Avalonia.Skia
{ {
private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc; private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc;
private readonly SKBitmap _bitmap; private readonly SKBitmap _bitmap;
private readonly object _lock = new object(); private readonly object _lock = new();
/// <summary> /// <summary>
/// Create a WriteableBitmap from given stream. /// Create a WriteableBitmap from given stream.
@ -205,8 +205,8 @@ namespace Avalonia.Skia
_bitmap.NotifyPixelsChanged(); _bitmap.NotifyPixelsChanged();
_parent.Version++; _parent.Version++;
Monitor.Exit(_parent._lock); Monitor.Exit(_parent._lock);
_bitmap = null; _bitmap = null!;
_parent = null; _parent = null!;
} }
/// <inheritdoc /> /// <inheritdoc />

Loading…
Cancel
Save