diff --git a/src/ImageSharp/Advanced/IRowIntervalAction.cs b/src/ImageSharp/Advanced/IRowIntervalAction.cs index 9a67eea715..a24cda3201 100644 --- a/src/ImageSharp/Advanced/IRowIntervalAction.cs +++ b/src/ImageSharp/Advanced/IRowIntervalAction.cs @@ -95,7 +95,8 @@ namespace SixLabors.ImageSharp.Advanced int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY); var rows = new RowInterval(yMin, yMax); - this.action.Invoke(in rows); + // Skip the safety copy when invoking a potentially impure method on a readonly field + Unsafe.AsRef(this.action).Invoke(in rows); } } } diff --git a/src/ImageSharp/Advanced/IRowIntervalAction{TBuffer}.cs b/src/ImageSharp/Advanced/IRowIntervalAction{TBuffer}.cs index 1fd088e09f..89ef2ab444 100644 --- a/src/ImageSharp/Advanced/IRowIntervalAction{TBuffer}.cs +++ b/src/ImageSharp/Advanced/IRowIntervalAction{TBuffer}.cs @@ -92,7 +92,8 @@ namespace SixLabors.ImageSharp.Advanced var rows = new RowInterval(yMin, yMax); using IMemoryOwner buffer = this.allocator.Allocate(this.info.MaxX); - this.action.Invoke(in rows, buffer.Memory); + + Unsafe.AsRef(this.action).Invoke(in rows, buffer.Memory); } } } diff --git a/src/ImageSharp/Advanced/ParallelRowIterator.cs b/src/ImageSharp/Advanced/ParallelRowIterator.cs index 5c1b4110e2..5c8a30f68b 100644 --- a/src/ImageSharp/Advanced/ParallelRowIterator.cs +++ b/src/ImageSharp/Advanced/ParallelRowIterator.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Advanced if (numOfSteps == 1) { var rows = new RowInterval(top, bottom); - body.Invoke(in rows); + Unsafe.AsRef(body).Invoke(in rows); return; } @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Advanced var rows = new RowInterval(top, bottom); using (IMemoryOwner buffer = allocator.Allocate(width)) { - body.Invoke(rows, buffer.Memory); + Unsafe.AsRef(body).Invoke(rows, buffer.Memory); } return; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 69a80e024e..bcbfda2ba2 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -554,8 +554,14 @@ namespace SixLabors.ImageSharp.Formats.Png return; } - // Grab the palette and write it to the stream. - ReadOnlySpan palette = quantized.Palette.Span; + /* Grab the palette and write it to the stream. + * Here the palette is reinterpreted as a mutable Memory value, + * which is possible because the two memory types have the same layout. + * This is done so that the Span we're working on is mutable, + * so that we can skip the safety copies done by the compiler when we + * invoke the IPixel.ToRgba32 method below, which is not marked as readonly. */ + ReadOnlyMemory paletteMemory = quantized.Palette; + Span palette = Unsafe.As, Memory>(ref paletteMemory).Span; int paletteLength = Math.Min(palette.Length, 256); int colorTableLength = paletteLength * 3; bool anyAlpha = false; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 56bc6f4559..d71f7bca43 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -218,7 +218,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; /// [MethodImpl(InliningOptions.ShortMethod)] @@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . [MethodImpl(InliningOptions.ShortMethod)] - public Rgba32 ToRgba32() + public readonly Rgba32 ToRgba32() { byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); @@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . [MethodImpl(InliningOptions.ShortMethod)] - public Bgra32 ToBgra32() + public readonly Bgra32 ToBgra32() { byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); @@ -371,7 +371,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . [MethodImpl(InliningOptions.ShortMethod)] - public Argb32 ToArgb32() + public readonly Argb32 ToArgb32() { byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); @@ -385,7 +385,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . [MethodImpl(InliningOptions.ShortMethod)] - public Rgb24 ToRgb24() + public readonly Rgb24 ToRgb24() { byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); @@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . [MethodImpl(InliningOptions.ShortMethod)] - public Bgr24 ToBgr24() + public readonly Bgr24 ToBgr24() { byte r = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); byte g = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs index 0350c669ab..79574e4426 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -66,7 +66,8 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils MemoryMarshal.Cast(lastQuarterOfDestBuffer), MemoryMarshal.Cast(destVectors.Slice(0, countWithoutLastItem))); - destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); + // Reinterpret as a mutable reference to skip the safety copy of the readonly value + destVectors[countWithoutLastItem] = Unsafe.AsRef(sourcePixels[countWithoutLastItem]).ToVector4(); // TODO: Investigate optimized 1-pass approach! ApplyForwardConversionModifiers(destVectors, modifiers); @@ -126,4 +127,4 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs index eea52dd71b..fd725d3ba0 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelRowDelegateProcessor{TPixel,TDelegate}.cs @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects PixelOperations.Instance.ToVector4(this.configuration, rowSpan, vectorSpan, this.modifiers); // Run the user defined pixel shader to the current row of pixels - this.rowProcessor.Invoke(vectorSpan, new Point(this.startX, y)); + Unsafe.AsRef(this.rowProcessor).Invoke(vectorSpan, new Point(this.startX, y)); PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorSpan, rowSpan, this.modifiers); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index 1b653a92c3..d3d59a5940 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public void Dispose() { - this.pinHandle.Dispose(); + Unsafe.AsRef(this.pinHandle).Dispose(); this.data.Dispose(); } @@ -248,4 +248,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return new ResizeKernel(left, rowPtr, length); } } -} \ No newline at end of file +}