diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs index 732f1957d0..07fd2d56fa 100644 --- a/src/Avalonia.Base/Media/IImageBrush.cs +++ b/src/Avalonia.Base/Media/IImageBrush.cs @@ -12,6 +12,6 @@ namespace Avalonia.Media /// /// Gets the image to draw. /// - IBitmap Source { get; } + IBitmap? Source { get; } } } diff --git a/src/Avalonia.Base/Media/ImageBrush.cs b/src/Avalonia.Base/Media/ImageBrush.cs index 2f2a0fb627..718ebf1686 100644 --- a/src/Avalonia.Base/Media/ImageBrush.cs +++ b/src/Avalonia.Base/Media/ImageBrush.cs @@ -11,8 +11,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty SourceProperty = - AvaloniaProperty.Register(nameof(Source)); + public static readonly StyledProperty SourceProperty = + AvaloniaProperty.Register(nameof(Source)); static ImageBrush() { @@ -30,7 +30,7 @@ namespace Avalonia.Media /// Initializes a new instance of the class. /// /// The image to draw. - public ImageBrush(IBitmap source) + public ImageBrush(IBitmap? source) { Source = source; } @@ -38,7 +38,7 @@ namespace Avalonia.Media /// /// Gets or sets the image to draw. /// - public IBitmap Source + public IBitmap? Source { get { return GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index f9892bf60c..668a907fdf 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -24,13 +24,13 @@ namespace Avalonia.Media.Immutable /// The tile mode. /// The bitmap interpolation mode. 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 } /// - public IBitmap Source { get; } + public IBitmap? Source { get; } } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 0b625080e3..e9086eee37 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media.Immutable /// The tile mode. /// Controls the quality of interpolation. public ImmutableVisualBrush( - Visual visual, + Visual? visual, AlignmentX alignmentX = AlignmentX.Center, AlignmentY alignmentY = AlignmentY.Center, RelativeRect? destinationRect = null, diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs index 5826cfb2ff..d1964bf07e 100644 --- a/src/Avalonia.Base/Platform/IGeometryImpl.cs +++ b/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 /// The stroke to use. /// The point. /// true if the geometry contains the point; otherwise, false. - bool StrokeContains(IPen pen, Point point); + bool StrokeContains(IPen? pen, Point point); /// /// Makes a clone of the geometry with the specified transform. @@ -87,6 +88,7 @@ namespace Avalonia.Platform /// If ture, the resulting snipped path will start with a BeginFigure call. /// The resulting snipped path. /// If the snipping operation is successful. - bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry); + bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, + [NotNullWhen(true)] out IGeometryImpl? segmentGeometry); } } diff --git a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs index 3c04935f0d..a169966188 100644 --- a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs +++ b/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; } diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index 4c3cfe2ef4..ab9f9ea413 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs index 40d7e10ae3..170cc9d420 100644 --- a/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs +++ b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; using Avalonia.Media; using SkiaSharp; -#nullable enable - namespace Avalonia.Skia { /// @@ -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; } } } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index eededb2836..82d902cbd0 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -19,27 +19,27 @@ namespace Avalonia.Skia /// internal class DrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport { - private IDisposable[] _disposables; + private IDisposable?[]? _disposables; private readonly Vector _dpi; - private readonly Stack _maskStack = new Stack(); - private readonly Stack _opacityStack = new Stack(); - private readonly Stack _blendingModeStack = new Stack(); + private readonly Stack _maskStack = new(); + private readonly Stack _opacityStack = new(); + private readonly Stack _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; /// /// Context create info. @@ -49,12 +49,12 @@ namespace Avalonia.Skia /// /// Canvas to draw to. /// - public SKCanvas Canvas; + public SKCanvas? Canvas; /// /// Surface to draw to. /// - public SKSurface Surface; + public SKSurface? Surface; /// /// Dpi of drawings. @@ -64,7 +64,7 @@ namespace Avalonia.Skia /// /// Visual brush renderer. /// - public IVisualBrushRenderer VisualBrushRenderer; + public IVisualBrushRenderer? VisualBrushRenderer; /// /// Render text without Lcd rendering. @@ -74,17 +74,17 @@ namespace Avalonia.Skia /// /// GPU-accelerated context (optional) /// - public GRContext GrContext; + public GRContext? GrContext; /// /// Skia GPU provider context (optional) /// - 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 /// /// Create info. /// Array of elements to dispose after drawing has finished. - 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. /// public SKCanvas Canvas { get; } - public SKSurface Surface { get; } + public SKSurface? Surface { get; } private void CheckLease() { @@ -205,87 +206,89 @@ namespace Avalonia.Skia } /// - 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); } } } /// - 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 } /// - 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 } /// - 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); } /// - 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); } } } /// - public void DrawGlyphRun(IBrush foreground, IRef glyphRun) + public void DrawGlyphRun(IBrush? foreground, IRef 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 /// /// Configure paint wrapper for using gradient brush. @@ -957,9 +952,10 @@ namespace Avalonia.Skia /// Visual brush. /// Visual brush renderer. /// Tile brush image. - 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 /// Source pen. /// Target size. /// - 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) { diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 90ff9652d8..d53dcd2df3 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/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; diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index 05fad25f1b..f1216100bc 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/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; /// /// Create new framebuffer render target using a target surface. @@ -35,7 +36,7 @@ namespace Avalonia.Skia } /// - 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 /// /// Desired image info. /// Backing framebuffer. + [MemberNotNull(nameof(_framebufferSurface))] private void CreateSurface(SKImageInfo desiredImageInfo, ILockedFramebuffer framebuffer) { if (_framebufferSurface != null && AreImageInfosCompatible(_currentImageInfo, desiredImageInfo) && _currentFramebufferAddress == framebuffer.Address) diff --git a/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs b/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs index d6f19612c1..2828f9a9c1 100644 --- a/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs +++ b/src/Skia/Avalonia.Skia/GeometryGroupImpl.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using Avalonia.Media; using SkiaSharp; -#nullable enable - namespace Avalonia.Skia { /// @@ -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; diff --git a/src/Skia/Avalonia.Skia/GeometryImpl.cs b/src/Skia/Avalonia.Skia/GeometryImpl.cs index 51386d2a45..34270c2078 100644 --- a/src/Skia/Avalonia.Skia/GeometryImpl.cs +++ b/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!); /// 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; } /// public bool FillContains(Point point) @@ -50,7 +40,7 @@ namespace Avalonia.Skia } /// - 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 /// Path to check. /// Point. /// True, if point is contained in a path. - 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); } /// - 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; } /// - 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 /// /// Cached contour path. /// - public SKPath CachedStrokePath { get; private set; } + public SKPath? CachedStrokePath { get; private set; } /// /// Cached geometry render bounds. @@ -244,6 +239,7 @@ namespace Avalonia.Skia public void Invalidate() { CachedStrokePath?.Dispose(); + CachedStrokePath = null; CachedGeometryRenderBounds = default; _cachedStrokeWidth = default; } diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index cfd6fc12f8..079eea7bef 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/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; diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index a8dd289a13..3093455bec 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/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() diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs index a5782037f3..e6e30a1203 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs @@ -15,14 +15,14 @@ namespace Avalonia.Skia /// /// Surfaces. /// Created render target or if it fails. - ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable surfaces); + ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable surfaces); /// /// Creates an offscreen render target surface /// /// size in pixels. /// An optional custom render session. - ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session); + ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session); } public interface ISkiaSurface : IDisposable diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs index e19379df09..4a3031d9ad 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs +++ b/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 diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs index 4bf43634ef..2b6caf34dc 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs +++ b/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()) diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index bf3e950e81..d403855094 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/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 _postDisposeCallbacks = new(); + private readonly List _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 surfaces) + public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable surfaces) { var customRenderTargetFactory = _glContext.TryGetFeature(); 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 preferredVersions = null) => + public IGlContext? CreateSharedContext(IEnumerable? 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; diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index 6b4a7a3409..7f9108481d 100644 --- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs +++ b/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(); diff --git a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs index ec33770356..4d8afe9830 100644 --- a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs +++ b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs @@ -15,7 +15,7 @@ namespace Avalonia.Skia.Helpers /// /// /// DrawingContext - 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 { diff --git a/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs b/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs index b3966c0324..66abd818e6 100644 --- a/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs +++ b/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; } -} \ No newline at end of file +} diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs index 4ab873fd8d..0627407509 100644 --- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs +++ b/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); } } } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 8e9a19239b..ab1c6b8816 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/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(); diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index 73f58e66bc..9ee17a09d6 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/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 _typefaces = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _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; diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index b49efd59cd..d064f49ae4 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -23,7 +23,7 @@ namespace Avalonia.Skia /// public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily) { - return s_cachedCollections.GetOrAdd(fontFamily, x => CreateCustomFontCollection(fontFamily)); + return s_cachedCollections.GetOrAdd(fontFamily, CreateCustomFontCollection); } /// @@ -33,10 +33,15 @@ namespace Avalonia.Skia /// 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(); foreach (var asset in fontAssets) diff --git a/src/Skia/Avalonia.Skia/SkiaBackendContext.cs b/src/Skia/Avalonia.Skia/SkiaBackendContext.cs index 0cf66767cb..51e182f7e3 100644 --- a/src/Skia/Avalonia.Skia/SkiaBackendContext.cs +++ b/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 /// public IRenderTarget CreateRenderTarget(IEnumerable 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); } diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 20dde27e9a..c66b53284a 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/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; } diff --git a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs index df847d2224..0c3289767e 100644 --- a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs +++ b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs @@ -47,7 +47,7 @@ namespace Avalonia.Skia /// public IStreamGeometryImpl Clone() { - return new StreamGeometryImpl(_effectivePath?.Clone(), Bounds); + return new StreamGeometryImpl(_effectivePath.Clone(), Bounds); } /// diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index e5fb182a3b..f88e74d738 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/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; } /// @@ -73,7 +81,7 @@ namespace Avalonia.Skia /// Height. /// Format. /// - 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 } /// - public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer) { _canvas.RestoreToCount(-1); _canvas.ResetMatrix(); @@ -218,11 +226,11 @@ namespace Avalonia.Skia /// /// GPU-accelerated context (optional) /// - public GRContext GrContext; + public GRContext? GrContext; - public ISkiaGpu Gpu; + public ISkiaGpu? Gpu; - public ISkiaGpuRenderSession Session; + public ISkiaGpuRenderSession? Session; public bool DisableManualFbo; } diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index a21038839c..e7dd4fb6da 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/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 memoryManager, out start, out length)) + if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager? memoryManager, out start, out length)) { return memoryManager.Memory; } diff --git a/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs b/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs index 64d5b58970..fb3c2e403f 100644 --- a/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs +++ b/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; } /// - public override SKPath EffectivePath { get; } + public override SKPath? EffectivePath { get; } /// public IGeometryImpl SourceGeometry { get; } diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 56e627f2d8..8ea7434c23 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/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(); /// /// 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!; } ///