Browse Source

Nullable annotations for Avalonia.Skia

pull/10390/head
Julien Lebosquain 3 years ago
parent
commit
dc53c3ee15
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  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>
/// Gets the image to draw.
/// </summary>
IBitmap Source { get; }
IBitmap? Source { get; }
}
}

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

@ -11,8 +11,8 @@ namespace Avalonia.Media
/// <summary>
/// Defines the <see cref="Visual"/> property.
/// </summary>
public static readonly StyledProperty<IBitmap> SourceProperty =
AvaloniaProperty.Register<ImageBrush, IBitmap>(nameof(Source));
public static readonly StyledProperty<IBitmap?> SourceProperty =
AvaloniaProperty.Register<ImageBrush, IBitmap?>(nameof(Source));
static ImageBrush()
{
@ -30,7 +30,7 @@ namespace Avalonia.Media
/// Initializes a new instance of the <see cref="ImageBrush"/> class.
/// </summary>
/// <param name="source">The image to draw.</param>
public ImageBrush(IBitmap source)
public ImageBrush(IBitmap? source)
{
Source = source;
}
@ -38,7 +38,7 @@ namespace Avalonia.Media
/// <summary>
/// Gets or sets the image to draw.
/// </summary>
public IBitmap Source
public IBitmap? Source
{
get { return GetValue(SourceProperty); }
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="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public ImmutableImageBrush(
IBitmap source,
IBitmap? source,
AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null,
double opacity = 1,
ImmutableTransform? transform = null,
RelativePoint transformOrigin = new RelativePoint(),
RelativePoint transformOrigin = default,
RelativeRect? sourceRect = null,
Stretch stretch = Stretch.Uniform,
TileMode tileMode = TileMode.None,
@ -61,6 +61,6 @@ namespace Avalonia.Media.Immutable
}
/// <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="bitmapInterpolationMode">Controls the quality of interpolation.</param>
public ImmutableVisualBrush(
Visual visual,
Visual? visual,
AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null,

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

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media;
using Avalonia.Metadata;
@ -47,7 +48,7 @@ namespace Avalonia.Platform
/// <param name="pen">The stroke to use.</param>
/// <param name="point">The point.</param>
/// <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>
/// 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="segmentGeometry">The resulting snipped path.</param>
/// <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;
}
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session)
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session)
{
return null;
}

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

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

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

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

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

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Reactive;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
@ -15,9 +16,9 @@ namespace Avalonia.Skia
private readonly IFramebufferPlatformSurface _platformSurface;
private SKImageInfo _currentImageInfo;
private IntPtr _currentFramebufferAddress;
private SKSurface _framebufferSurface;
private PixelFormatConversionShim _conversionShim;
private IDisposable _preFramebufferCopyHandler;
private SKSurface? _framebufferSurface;
private PixelFormatConversionShim? _conversionShim;
private IDisposable? _preFramebufferCopyHandler;
/// <summary>
/// Create new framebuffer render target using a target surface.
@ -35,7 +36,7 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{
var framebuffer = _platformSurface.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
@ -81,6 +82,7 @@ namespace Avalonia.Skia
/// </summary>
/// <param name="desiredImageInfo">Desired image info.</param>
/// <param name="framebuffer">Backing framebuffer.</param>
[MemberNotNull(nameof(_framebufferSurface))]
private void CreateSurface(SKImageInfo desiredImageInfo, ILockedFramebuffer framebuffer)
{
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 SkiaSharp;
#nullable enable
namespace Avalonia.Skia
{
/// <summary>
@ -22,8 +20,10 @@ namespace Avalonia.Skia
for (var i = 0; i < count; ++i)
{
if (children[i]?.PlatformImpl is GeometryImpl child)
path.AddPath(child.EffectivePath);
if (children[i].PlatformImpl is GeometryImpl { EffectivePath: { } effectivePath })
{
path.AddPath(effectivePath);
}
}
EffectivePath = path;

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

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media;
using Avalonia.Platform;
using SkiaSharp;
@ -11,20 +12,9 @@ namespace Avalonia.Skia
internal abstract class GeometryImpl : IGeometryImpl
{
private PathCache _pathCache;
private SKPathMeasure _pathMeasureCache;
private SKPathMeasure? _cachedPathMeasure;
private SKPathMeasure CachedPathMeasure
{
get
{
if (_pathMeasureCache is null)
{
_pathMeasureCache = new SKPathMeasure(EffectivePath);
}
return _pathMeasureCache;
}
}
private SKPathMeasure CachedPathMeasure => _cachedPathMeasure ??= new SKPathMeasure(EffectivePath!);
/// <inheritdoc />
public abstract Rect Bounds { get; }
@ -37,11 +27,11 @@ namespace Avalonia.Skia
if (EffectivePath is null)
return 0;
return (double)CachedPathMeasure?.Length;
return CachedPathMeasure.Length;
}
}
public abstract SKPath EffectivePath { get; }
public abstract SKPath? EffectivePath { get; }
/// <inheritdoc />
public bool FillContains(Point point)
@ -50,7 +40,7 @@ namespace Avalonia.Skia
}
/// <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.
// 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="point">Point.</param>
/// <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 />
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 />
public Rect GetRenderBounds(IPen pen)
public Rect GetRenderBounds(IPen? pen)
{
var strokeWidth = (float)(pen?.Thickness ?? 0);
@ -161,7 +156,7 @@ namespace Avalonia.Skia
}
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure,
out IGeometryImpl segmentGeometry)
[NotNullWhen(true)] out IGeometryImpl? segmentGeometry)
{
if (EffectivePath is null)
{
@ -203,7 +198,7 @@ namespace Avalonia.Skia
/// <summary>
/// Cached contour path.
/// </summary>
public SKPath CachedStrokePath { get; private set; }
public SKPath? CachedStrokePath { get; private set; }
/// <summary>
/// Cached geometry render bounds.
@ -244,6 +239,7 @@ namespace Avalonia.Skia
public void Invalidate()
{
CachedStrokePath?.Dispose();
CachedStrokePath = null;
CachedGeometryRenderBounds = default;
_cachedStrokeWidth = default;
}

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

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

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

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

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

@ -15,14 +15,14 @@ namespace Avalonia.Skia
/// </summary>
/// <param name="surfaces">Surfaces.</param>
/// <returns>Created render target or <see langword="null"/> if it fails.</returns>
ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces);
ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces);
/// <summary>
/// Creates an offscreen render target surface
/// </summary>
/// <param name="size">size in pixels.</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

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

@ -3,6 +3,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform;
using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
internal class FboSkiaSurface : ISkiaSurface
@ -14,6 +15,7 @@ namespace Avalonia.Skia
private int _fbo;
private int _depthStencil;
private int _texture;
private SKSurface? _surface;
private static readonly bool[] TrueFalse = new[] { true, false };
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,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target,
_surface = SKSurface.Create(_grContext, target,
surfaceOrigin, SKColorType.Rgba8888, new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal));
CanBlit = gl.IsBlitFramebufferAvailable;
}
@ -100,8 +102,8 @@ namespace Avalonia.Skia
{
using (_glContext.EnsureCurrent())
{
Surface?.Dispose();
Surface = null;
_surface?.Dispose();
_surface = null;
var gl = _glContext.GlInterface;
if (_fbo != 0)
{
@ -113,11 +115,11 @@ namespace Avalonia.Skia
}
catch (PlatformGraphicsContextLostException)
{
if (Surface != null)
if (_surface != null)
// We need to dispose SKSurface _after_ GRContext.Abandon was called,
// otherwise it will try to do OpenGL calls without a proper context
_gpu.AddPostDispose(Surface.Dispose);
Surface = null;
_gpu.AddPostDispose(_surface.Dispose);
_surface = null;
}
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 void Blit(SKCanvas canvas)
{
// 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.Collections.Generic;
using Avalonia.OpenGL;
@ -150,6 +149,11 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex)
{
if (_image is null)
{
throw new NotSupportedException("Only supported with an external image");
}
using (_gpu.EnsureCurrent())
{
_image.AcquireKeyedMutex(acquireIndex);
@ -167,6 +171,11 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore,
IPlatformRenderInterfaceImportedSemaphore signalSemaphore)
{
if (_image is null)
{
throw new NotSupportedException("Only supported with an external image");
}
var wait = (GlSkiaImportedSemaphore)waitForSemaphore;
var signal = (GlSkiaImportedSemaphore)signalSemaphore;
using (_gpu.EnsureCurrent())

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

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

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

@ -22,7 +22,7 @@ namespace Avalonia.Skia
_renderTarget.Dispose();
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{
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="visualBrushRenderer"></param>
/// <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
{

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

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

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

@ -100,7 +100,7 @@ namespace Avalonia.Skia
_bitmap = scaledBmp;
}
_bitmap!.SetImmutable();
_bitmap.SetImmutable();
_image = SKImage.FromBitmap(_bitmap);
@ -134,7 +134,7 @@ namespace Avalonia.Skia
data);
_bitmap = tmp.Copy();
}
_bitmap!.SetImmutable();
_bitmap.SetImmutable();
_image = SKImage.FromBitmap(_bitmap);
if (_image == null)
@ -179,10 +179,13 @@ namespace Avalonia.Skia
public PixelFormat? Format => _bitmap?.ColorType.ToAvalonia();
public ILockedFramebuffer Lock()
{
if (_bitmap == null)
throw new NotSupportedException();
return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi,
_bitmap.ColorType.ToAvalonia().Value, null);
if (_bitmap is null)
throw new NotSupportedException("A bitmap is needed for locking");
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)
return new SkiaContext(null);
@ -225,7 +225,7 @@ namespace Avalonia.Skia
throw new ArgumentNullException(nameof(glyphInfos));
}
var glyphTypefaceImpl = glyphTypeface as GlyphTypefaceImpl;
var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
var font = SKFontCache.Shared.Get();

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

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

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

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

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

@ -9,9 +9,9 @@ namespace Avalonia.Skia;
internal class SkiaContext : IPlatformRenderInterfaceContext
{
private ISkiaGpu _gpu;
private ISkiaGpu? _gpu;
public SkiaContext(ISkiaGpu gpu)
public SkiaContext(ISkiaGpu? gpu)
{
_gpu = gpu;
}
@ -25,10 +25,10 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
/// <inheritdoc />
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
if (!(surfaces is IList))
if (surfaces is not IList)
surfaces = surfaces.ToList();
var gpuRenderTarget = _gpu?.TryCreateRenderTarget(surfaces);
if (gpuRenderTarget != null)
if (_gpu?.TryCreateRenderTarget(surfaces) is { } 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");
}
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.Diagnostics.CodeAnalysis;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Media.Imaging;
@ -111,7 +112,7 @@ namespace Avalonia.Skia
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);
}
@ -171,14 +172,14 @@ namespace Avalonia.Skia
};
}
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m)
public static SKShaderTileMode ToSKShaderTileMode(this GradientSpreadMethod m)
{
switch (m)
{
default:
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
case GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
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;
}

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

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

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

@ -1,7 +1,6 @@
using System;
using System.IO;
using Avalonia.Reactive;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Skia.Helpers;
@ -17,24 +16,26 @@ namespace Avalonia.Skia
private readonly ISkiaSurface _surface;
private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering;
private readonly GRContext _grContext;
private readonly ISkiaGpu _gpu;
private readonly GRContext? _grContext;
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 void Blit(SKCanvas canvas) => throw new NotSupportedException();
public SkiaSurfaceWrapper(SKSurface surface)
{
Surface = surface;
_surface = surface;
}
public void Dispose()
{
Surface?.Dispose();
Surface = null;
_surface?.Dispose();
_surface = null;
}
}
@ -51,18 +52,25 @@ namespace Avalonia.Skia
_grContext = createInfo.GrContext;
_gpu = createInfo.Gpu;
if (!createInfo.DisableManualFbo)
_surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
ISkiaSurface? surface = null;
_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>
@ -73,7 +81,7 @@ namespace Avalonia.Skia
/// <param name="height">Height.</param>
/// <param name="format">Format.</param>
/// <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);
if (gpu != null)
@ -89,7 +97,7 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer)
{
_canvas.RestoreToCount(-1);
_canvas.ResetMatrix();
@ -218,11 +226,11 @@ namespace Avalonia.Skia
/// <summary>
/// GPU-accelerated context (optional)
/// </summary>
public GRContext GrContext;
public GRContext? GrContext;
public ISkiaGpu Gpu;
public ISkiaGpu? Gpu;
public ISkiaGpuRenderSession Session;
public ISkiaGpuRenderSession? Session;
public bool DisableManualFbo;
}

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

@ -37,7 +37,7 @@ namespace Avalonia.Skia
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;
@ -170,7 +170,7 @@ namespace Avalonia.Skia
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;
}

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

@ -19,14 +19,14 @@ namespace Avalonia.Skia
Transform = transform;
var transformedPath = source.EffectivePath.Clone();
transformedPath.Transform(transform.ToSKMatrix());
transformedPath?.Transform(transform.ToSKMatrix());
EffectivePath = transformedPath;
Bounds = transformedPath.TightBounds.ToAvaloniaRect();
Bounds = transformedPath?.TightBounds.ToAvaloniaRect() ?? default;
}
/// <inheritdoc />
public override SKPath EffectivePath { get; }
public override SKPath? EffectivePath { get; }
/// <inheritdoc />
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 readonly SKBitmap _bitmap;
private readonly object _lock = new object();
private readonly object _lock = new();
/// <summary>
/// Create a WriteableBitmap from given stream.
@ -205,8 +205,8 @@ namespace Avalonia.Skia
_bitmap.NotifyPixelsChanged();
_parent.Version++;
Monitor.Exit(_parent._lock);
_bitmap = null;
_parent = null;
_bitmap = null!;
_parent = null!;
}
/// <inheritdoc />

Loading…
Cancel
Save