diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 95211ccda8..96cd094cd8 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using SixLabors.ImageSharp; @@ -17,13 +16,14 @@ internal static partial class Guard /// The type of the value. /// is not a value type. [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeValueType(TValue value, string parameterName) + public static void MustBeValueType(TValue value, [CallerArgumentExpression("value")] string? parameterName = null) + where TValue : notnull { if (value.GetType().IsValueType) { return; } - ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName); + ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName!); } } diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 04930a2689..cf970b3166 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Text; @@ -180,6 +179,6 @@ public static partial class ImageExtensions // Always available. stream.TryGetBuffer(out ArraySegment buffer); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer.Array, 0, (int)stream.Length)}"; + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer.Array ?? Array.Empty(), 0, (int)stream.Length)}"; } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index fb4ee6ae5f..3734402d30 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -145,7 +144,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource } /// - public Buffer2D PixelBuffer { get; private set; } + public Buffer2D PixelBuffer { get; } /// /// Gets or sets the pixel at the specified position. @@ -183,7 +182,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor = new PixelAccessor(this.PixelBuffer); + PixelAccessor accessor = new(this.PixelBuffer); processPixels(accessor); } finally @@ -211,8 +210,8 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor1 = new PixelAccessor(this.PixelBuffer); - var accessor2 = new PixelAccessor(frame2.PixelBuffer); + PixelAccessor accessor1 = new(this.PixelBuffer); + PixelAccessor accessor2 = new(frame2.PixelBuffer); processPixels(accessor1, accessor2); } finally @@ -247,9 +246,9 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor1 = new PixelAccessor(this.PixelBuffer); - var accessor2 = new PixelAccessor(frame2.PixelBuffer); - var accessor3 = new PixelAccessor(frame3.PixelBuffer); + PixelAccessor accessor1 = new(this.PixelBuffer); + PixelAccessor accessor2 = new(frame2.PixelBuffer); + PixelAccessor accessor3 = new(frame3.PixelBuffer); processPixels(accessor1, accessor2, accessor3); } finally @@ -310,6 +309,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource /// Copies the pixels to a of the same size. /// /// The target pixel buffer accessor. + /// ImageFrame{TPixel}.CopyTo(): target must be of the same size! internal void CopyTo(Buffer2D target) { if (this.Size() != target.Size()) @@ -342,8 +342,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource if (disposing) { - this.PixelBuffer?.Dispose(); - this.PixelBuffer = null; + this.PixelBuffer.Dispose(); } this.isDisposed = true; @@ -379,14 +378,14 @@ public sealed class ImageFrame : ImageFrame, IPixelSource /// /// The configuration providing initialization code which allows extending the library. /// The - internal ImageFrame Clone(Configuration configuration) => new ImageFrame(configuration, this); + internal ImageFrame Clone(Configuration configuration) => new(configuration, this); /// /// Returns a copy of the image frame in the given pixel format. /// /// The pixel format. /// The - internal ImageFrame CloneAs() + internal ImageFrame? CloneAs() where TPixel2 : unmanaged, IPixel => this.CloneAs(this.GetConfiguration()); /// @@ -400,11 +399,11 @@ public sealed class ImageFrame : ImageFrame, IPixelSource { if (typeof(TPixel2) == typeof(TPixel)) { - return this.Clone(configuration) as ImageFrame; + return (this.Clone(configuration) as ImageFrame)!; } - var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); - var operation = new RowIntervalOperation(this.PixelBuffer, target.PixelBuffer, configuration); + ImageFrame target = new(configuration, this.Width, this.Height, this.Metadata.DeepClone()); + RowIntervalOperation operation = new(this.PixelBuffer, target.PixelBuffer, configuration); ParallelRowIterator.IterateRowIntervals( configuration, @@ -447,14 +446,12 @@ public sealed class ImageFrame : ImageFrame, IPixelSource } [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowArgumentOutOfRangeException(string paramName) - { - throw new ArgumentOutOfRangeException(paramName); - } + private static void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); /// /// A implementing the clone logic for . /// + /// The type of the target pixel format. private readonly struct RowIntervalOperation : IRowIntervalOperation where TPixel2 : unmanaged, IPixel { diff --git a/src/ImageSharp/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/IndexedImageFrame{TPixel}.cs index 741b3219e0..6807e77ad2 100644 --- a/src/ImageSharp/IndexedImageFrame{TPixel}.cs +++ b/src/ImageSharp/IndexedImageFrame{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -18,8 +17,8 @@ namespace SixLabors.ImageSharp; public sealed class IndexedImageFrame : IPixelSource, IDisposable where TPixel : unmanaged, IPixel { - private Buffer2D pixelBuffer; - private IMemoryOwner paletteOwner; + private readonly Buffer2D pixelBuffer; + private readonly IMemoryOwner paletteOwner; private bool isDisposed; /// @@ -109,8 +108,6 @@ public sealed class IndexedImageFrame : IPixelSource, IDisposable this.isDisposed = true; this.pixelBuffer.Dispose(); this.paletteOwner.Dispose(); - this.pixelBuffer = null; - this.paletteOwner = null; } } } diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 8d6465389f..1173e02e17 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -55,6 +55,8 @@ public sealed class Buffer2D : IDisposable /// internal MemoryGroup FastMemoryGroup { get; private set; } + internal bool IsDisposed { get; private set; } + /// /// Gets a reference to the element at the specified position. /// @@ -79,7 +81,11 @@ public sealed class Buffer2D : IDisposable /// /// Disposes the instance /// - public void Dispose() => this.FastMemoryGroup.Dispose(); + public void Dispose() + { + this.FastMemoryGroup.Dispose(); + this.IsDisposed = true; + } /// /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index 575ce293ca..f830ddfd09 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -157,9 +156,9 @@ public static partial class ProcessingExtensions Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - var visitor = new ProcessingVisitor(configuration, operation, false); + ProcessingVisitor visitor = new(configuration, operation, false); source.AcceptVisitor(visitor); - return visitor.ResultImage; + return visitor.GetResultImage(); } /// @@ -275,6 +274,8 @@ public static partial class ProcessingExtensions private readonly bool mutate; + private Image? resultImage; + public ProcessingVisitor(Configuration configuration, Action operation, bool mutate) { this.configuration = configuration; @@ -282,7 +283,7 @@ public static partial class ProcessingExtensions this.mutate = mutate; } - public Image ResultImage { get; private set; } + public Image GetResultImage() => this.resultImage!; public void Visit(Image image) where TPixel : unmanaged, IPixel @@ -291,7 +292,7 @@ public static partial class ProcessingExtensions this.configuration.ImageOperationsProvider.CreateImageProcessingContext(this.configuration, image, this.mutate); this.operation(operationsRunner); - this.ResultImage = operationsRunner.GetResultImage(); + this.resultImage = operationsRunner.GetResultImage(); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs index abb8beccdf..30c279e593 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs @@ -19,7 +19,7 @@ public class AffineTransformProcessor : CloningImageProcessor public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, nameof(sampler)); - Guard.MustBeValueType(sampler, nameof(sampler)); + Guard.MustBeValueType(sampler); if (TransformUtils.IsDegenerate(matrix)) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs index 9b0b979e69..9e9507b737 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs @@ -19,7 +19,7 @@ public sealed class ProjectiveTransformProcessor : CloningImageProcessor public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, nameof(sampler)); - Guard.MustBeValueType(sampler, nameof(sampler)); + Guard.MustBeValueType(sampler); if (TransformUtils.IsDegenerate(matrix)) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index b7651c03f3..719622a28f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -17,7 +17,7 @@ public class ResizeProcessor : CloningImageProcessor { Guard.NotNull(options, nameof(options)); Guard.NotNull(options.Sampler, nameof(options.Sampler)); - Guard.MustBeValueType(options.Sampler, nameof(options.Sampler)); + Guard.MustBeValueType(options.Sampler); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index 44d6d8e720..ad0888ad77 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing; /// public class ProjectiveTransformBuilder { - private readonly List> matrixFactories = new List>(); + private readonly List> matrixFactories = new(); /// /// Prepends a matrix that performs a tapering projective transform. @@ -313,7 +313,7 @@ public class ProjectiveTransformBuilder Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle)); // Translate the origin matrix to cater for source rectangle offsets. - var matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); + Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); Size size = sourceRectangle.Size; diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index fba19065b0..62d1ef4db9 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -182,12 +182,12 @@ public abstract partial class ImageFrameCollectionTests new[] { imageFrame1, imageFrame2 }); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + + Assert.All(framesSnapShot, f => Assert.False(f.PixelBuffer.IsDisposed)); + collection.Dispose(); - Assert.All( - framesSnapShot, - f => // The pixel source of the frame is null after its been disposed. - Assert.Null(f.PixelBuffer)); + Assert.All(framesSnapShot, f => Assert.True(f.PixelBuffer.IsDisposed)); } [Theory]