diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index a450aebf4..435c629bc 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -19,6 +19,31 @@ jobs:
isARM:
- ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }}
options:
+ - os: ubuntu-latest
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+ - os: buildjet-4vcpu-ubuntu-2204-arm
+ framework: net9.0
+ sdk: 9.0.x
+ sdk-preview: true
+ runtime: -x64
+ codecov: false
+
- os: ubuntu-latest
framework: net8.0
sdk: 8.0.x
@@ -100,7 +125,7 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 8.0.x
+ 9.0.x
- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
diff --git a/src/ImageSharp.ruleset b/src/ImageSharp.ruleset
index b60989020..dee0393cd 100644
--- a/src/ImageSharp.ruleset
+++ b/src/ImageSharp.ruleset
@@ -1,4 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/ImageEncoder.cs b/src/ImageSharp/Formats/ImageEncoder.cs
index fdaa5c35d..a37a32717 100644
--- a/src/ImageSharp/Formats/ImageEncoder.cs
+++ b/src/ImageSharp/Formats/ImageEncoder.cs
@@ -51,7 +51,7 @@ public abstract class ImageEncoder : IImageEncoder
else
{
using ChunkedMemoryStream ms = new(configuration.MemoryAllocator);
- this.Encode(image, stream, cancellationToken);
+ this.Encode(image, ms, cancellationToken);
ms.Position = 0;
ms.CopyTo(stream, configuration.StreamProcessingBufferSize);
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 2320fe179..707baa1a8 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -16,7 +16,6 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
-using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg;
@@ -1473,7 +1472,7 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData
this.Frame.ComponentOrder[i / 2] = (byte)componentIndex;
- IJpegComponent component = this.Frame.Components[componentIndex];
+ JpegComponent component = this.Frame.Components[componentIndex];
// 1 byte: Huffman table selectors.
// 4 bits - dc
diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
index eccd9ede8..a9e63a3d0 100644
--- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
@@ -183,7 +183,7 @@ internal class AlphaDecoder : IDisposable
else
{
this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span);
- this.ExtractAlphaRows(this.Vp8LDec);
+ this.ExtractAlphaRows(this.Vp8LDec, this.Width);
}
}
@@ -257,14 +257,15 @@ internal class AlphaDecoder : IDisposable
/// Once the image-stream is decoded into ARGB color values, the transparency information will be extracted from the green channel of the ARGB quadruplet.
///
/// The VP8L decoder.
- private void ExtractAlphaRows(Vp8LDecoder dec)
+ /// The image width.
+ private void ExtractAlphaRows(Vp8LDecoder dec, int width)
{
int numRowsToProcess = dec.Height;
- int width = dec.Width;
Span input = dec.Pixels.Memory.Span;
Span output = this.Alpha.Memory.Span;
// Extract alpha (which is stored in the green plane).
+ // the final width (!= dec->width_)
int pixelCount = width * numRowsToProcess;
WebpLosslessDecoder.ApplyInverseTransforms(dec, input, this.memoryAllocator);
ExtractGreen(input, output, pixelCount);
diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
index 024adb7c2..5287f0b75 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
@@ -269,7 +269,11 @@ internal static unsafe class LosslessUtils
///
/// The transform data contains color table size and the entries in the color table.
/// The pixel data to apply the reverse transform on.
- public static void ColorIndexInverseTransform(Vp8LTransform transform, Span pixelData)
+ /// The resulting pixel data with the reversed transformation data.
+ public static void ColorIndexInverseTransform(
+ Vp8LTransform transform,
+ Span pixelData,
+ Span outputSpan)
{
int bitsPerPixel = 8 >> transform.Bits;
int width = transform.XSize;
@@ -282,7 +286,6 @@ internal static unsafe class LosslessUtils
int countMask = pixelsPerByte - 1;
int bitMask = (1 << bitsPerPixel) - 1;
- uint[] decodedPixelData = new uint[width * height];
int pixelDataPos = 0;
for (int y = 0; y < height; y++)
{
@@ -298,12 +301,12 @@ internal static unsafe class LosslessUtils
packedPixels = GetArgbIndex(pixelData[pixelDataPos++]);
}
- decodedPixelData[decodedPixels++] = colorMap[(int)(packedPixels & bitMask)];
+ outputSpan[decodedPixels++] = colorMap[(int)(packedPixels & bitMask)];
packedPixels >>= bitsPerPixel;
}
}
- decodedPixelData.AsSpan().CopyTo(pixelData);
+ outputSpan.CopyTo(pixelData);
}
else
{
diff --git a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
index e4c2a7ddf..6de3ae749 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
@@ -684,6 +684,7 @@ internal sealed class WebpLosslessDecoder
List transforms = decoder.Transforms;
for (int i = transforms.Count - 1; i >= 0; i--)
{
+ // TODO: Review these 1D allocations. They could conceivably exceed limits.
Vp8LTransform transform = transforms[i];
switch (transform.TransformType)
{
@@ -701,7 +702,11 @@ internal sealed class WebpLosslessDecoder
LosslessUtils.ColorSpaceInverseTransform(transform, pixelData);
break;
case Vp8LTransformType.ColorIndexingTransform:
- LosslessUtils.ColorIndexInverseTransform(transform, pixelData);
+ using (IMemoryOwner output = memoryAllocator.Allocate(transform.XSize * transform.YSize, AllocationOptions.Clean))
+ {
+ LosslessUtils.ColorIndexInverseTransform(transform, pixelData, output.GetSpan());
+ }
+
break;
}
}
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs
index 82f00e876..c645816d4 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs
@@ -667,12 +667,12 @@ internal static unsafe class Vp8Encoding
// V block.
dst = dst[8..];
- if (top != default)
+ if (!top.IsEmpty)
{
top = top[8..];
}
- if (left != default)
+ if (!left.IsEmpty)
{
left = left[16..];
}
@@ -701,7 +701,7 @@ internal static unsafe class Vp8Encoding
private static void VerticalPred(Span dst, Span top, int size)
{
- if (top != default)
+ if (!top.IsEmpty)
{
for (int j = 0; j < size; j++)
{
@@ -716,7 +716,7 @@ internal static unsafe class Vp8Encoding
public static void HorizontalPred(Span dst, Span left, int size)
{
- if (left != default)
+ if (!left.IsEmpty)
{
left = left[1..]; // in the reference implementation, left starts at - 1.
for (int j = 0; j < size; j++)
@@ -732,9 +732,9 @@ internal static unsafe class Vp8Encoding
public static void TrueMotion(Span dst, Span left, Span top, int size)
{
- if (left != default)
+ if (!left.IsEmpty)
{
- if (top != default)
+ if (!top.IsEmpty)
{
Span clip = Clip1.AsSpan(255 - left[0]); // left [0] instead of left[-1], original left starts at -1
for (int y = 0; y < size; y++)
@@ -759,7 +759,7 @@ internal static unsafe class Vp8Encoding
// is equivalent to VE prediction where you just copy the top samples.
// Note that if top samples are not available, the default value is
// then 129, and not 127 as in the VerticalPred case.
- if (top != default)
+ if (!top.IsEmpty)
{
VerticalPred(dst, top, size);
}
@@ -774,14 +774,14 @@ internal static unsafe class Vp8Encoding
{
int dc = 0;
int j;
- if (top != default)
+ if (!top.IsEmpty)
{
for (j = 0; j < size; j++)
{
dc += top[j];
}
- if (left != default)
+ if (!left.IsEmpty)
{
// top and left present.
left = left[1..]; // in the reference implementation, left starts at -1.
@@ -798,7 +798,7 @@ internal static unsafe class Vp8Encoding
dc = (dc + round) >> shift;
}
- else if (left != default)
+ else if (!left.IsEmpty)
{
// left but no top.
left = left[1..]; // in the reference implementation, left starts at -1.
diff --git a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
index f8e664ed0..40146c6af 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
@@ -48,7 +48,7 @@ internal static class YuvConversion
uint uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
YuvToBgr(topY[0], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2;
YuvToBgr(bottomY[0], (int)uv0 & 0xff, (int)(uv0 >> 16), bottomDst);
@@ -69,7 +69,7 @@ internal static class YuvConversion
YuvToBgr(topY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst[((xMul2 - 1) * xStep)..]);
YuvToBgr(topY[xMul2 - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst[((xMul2 - 0) * xStep)..]);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
uv0 = (diag03 + luv) >> 1;
uv1 = (diag12 + uv) >> 1;
@@ -85,7 +85,7 @@ internal static class YuvConversion
{
uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst[((len - 1) * xStep)..]);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2;
YuvToBgr(bottomY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst[((len - 1) * xStep)..]);
@@ -120,7 +120,7 @@ internal static class YuvConversion
int u0t = (topU[0] + uDiag) >> 1;
int v0t = (topV[0] + vDiag) >> 1;
YuvToBgr(topY[0], u0t, v0t, topDst);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
int u0b = (curU[0] + uDiag) >> 1;
int v0b = (curV[0] + vDiag) >> 1;
@@ -134,7 +134,7 @@ internal static class YuvConversion
ref byte topVRef = ref MemoryMarshal.GetReference(topV);
ref byte curURef = ref MemoryMarshal.GetReference(curU);
ref byte curVRef = ref MemoryMarshal.GetReference(curV);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
for (pos = 1, uvPos = 0; pos + 32 + 1 <= len; pos += 32, uvPos += 16)
{
@@ -160,12 +160,12 @@ internal static class YuvConversion
Span tmpTopDst = ru[(4 * 32)..];
Span tmpBottomDst = tmpTopDst[(4 * 32)..];
Span tmpTop = tmpBottomDst[(4 * 32)..];
- Span tmpBottom = (bottomY == default) ? null : tmpTop[32..];
+ Span tmpBottom = bottomY.IsEmpty ? null : tmpTop[32..];
UpSampleLastBlock(topU[uvPos..], curU[uvPos..], leftOver, ru);
UpSampleLastBlock(topV[uvPos..], curV[uvPos..], leftOver, rv);
topY[pos..len].CopyTo(tmpTop);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
bottomY[pos..len].CopyTo(tmpBottom);
ConvertYuvToBgrWithBottomYSse41(tmpTop, tmpBottom, tmpTopDst, tmpBottomDst, ru, rv, 0, xStep);
@@ -176,7 +176,7 @@ internal static class YuvConversion
}
tmpTopDst[..((len - pos) * xStep)].CopyTo(topDst[(pos * xStep)..]);
- if (bottomY != default)
+ if (!bottomY.IsEmpty)
{
tmpBottomDst[..((len - pos) * xStep)].CopyTo(bottomDst[(pos * xStep)..]);
}
diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs
index 253454814..760d1d334 100644
--- a/src/ImageSharp/IO/ChunkedMemoryStream.cs
+++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs
@@ -3,6 +3,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.IO;
@@ -14,42 +15,19 @@ namespace SixLabors.ImageSharp.IO;
///
internal sealed class ChunkedMemoryStream : Stream
{
- // The memory allocator.
- private readonly MemoryAllocator allocator;
-
- // Data
- private MemoryChunk? memoryChunk;
-
- // The total number of allocated chunks
- private int chunkCount;
-
- // The length of the largest contiguous buffer that can be handled by the allocator.
- private readonly int allocatorCapacity;
-
- // Has the stream been disposed.
+ private readonly MemoryChunkBuffer memoryChunkBuffer;
+ private long length;
+ private long position;
+ private int bufferIndex;
+ private int chunkIndex;
private bool isDisposed;
- // Current chunk to write to
- private MemoryChunk? writeChunk;
-
- // Offset into chunk to write to
- private int writeOffset;
-
- // Current chunk to read from
- private MemoryChunk? readChunk;
-
- // Offset into chunk to read from
- private int readOffset;
-
///
/// Initializes a new instance of the class.
///
/// The memory allocator.
public ChunkedMemoryStream(MemoryAllocator allocator)
- {
- this.allocatorCapacity = allocator.GetBufferCapacityInBytes();
- this.allocator = allocator;
- }
+ => this.memoryChunkBuffer = new(allocator);
///
public override bool CanRead => !this.isDisposed;
@@ -66,25 +44,7 @@ internal sealed class ChunkedMemoryStream : Stream
get
{
this.EnsureNotDisposed();
-
- int length = 0;
- MemoryChunk? chunk = this.memoryChunk;
- while (chunk != null)
- {
- MemoryChunk? next = chunk.Next;
- if (next != null)
- {
- length += chunk.Length;
- }
- else
- {
- length += this.writeOffset;
- }
-
- chunk = next;
- }
-
- return length;
+ return this.length;
}
}
@@ -94,93 +54,35 @@ internal sealed class ChunkedMemoryStream : Stream
get
{
this.EnsureNotDisposed();
-
- if (this.readChunk is null)
- {
- return 0;
- }
-
- int pos = 0;
- MemoryChunk? chunk = this.memoryChunk;
- while (chunk != this.readChunk && chunk is not null)
- {
- pos += chunk.Length;
- chunk = chunk.Next;
- }
-
- pos += this.readOffset;
-
- return pos;
+ return this.position;
}
set
{
this.EnsureNotDisposed();
-
- if (value < 0)
- {
- ThrowArgumentOutOfRange(nameof(value));
- }
-
- // Back up current position in case new position is out of range
- MemoryChunk? backupReadChunk = this.readChunk;
- int backupReadOffset = this.readOffset;
-
- this.readChunk = null;
- this.readOffset = 0;
-
- int leftUntilAtPos = (int)value;
- MemoryChunk? chunk = this.memoryChunk;
- while (chunk != null)
- {
- if ((leftUntilAtPos < chunk.Length)
- || ((leftUntilAtPos == chunk.Length)
- && (chunk.Next is null)))
- {
- // The desired position is in this chunk
- this.readChunk = chunk;
- this.readOffset = leftUntilAtPos;
- break;
- }
-
- leftUntilAtPos -= chunk.Length;
- chunk = chunk.Next;
- }
-
- if (this.readChunk is null)
- {
- // Position is out of range
- this.readChunk = backupReadChunk;
- this.readOffset = backupReadOffset;
- }
+ this.SetPosition(value);
}
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Flush()
+ {
+ }
+
+ ///
public override long Seek(long offset, SeekOrigin origin)
{
this.EnsureNotDisposed();
- switch (origin)
+ this.Position = origin switch
{
- case SeekOrigin.Begin:
- this.Position = offset;
- break;
-
- case SeekOrigin.Current:
- this.Position += offset;
- break;
-
- case SeekOrigin.End:
- this.Position = this.Length + offset;
- break;
- default:
- ThrowInvalidSeek();
- break;
- }
+ SeekOrigin.Begin => (int)offset,
+ SeekOrigin.Current => (int)(this.Position + offset),
+ SeekOrigin.End => (int)(this.Length + offset),
+ _ => throw new ArgumentOutOfRangeException(nameof(offset)),
+ };
- return this.Position;
+ return this.position;
}
///
@@ -188,39 +90,13 @@ internal sealed class ChunkedMemoryStream : Stream
=> throw new NotSupportedException();
///
- protected override void Dispose(bool disposing)
- {
- if (this.isDisposed)
- {
- return;
- }
-
- try
- {
- this.isDisposed = true;
- if (disposing)
- {
- ReleaseMemoryChunks(this.memoryChunk);
- }
-
- this.memoryChunk = null;
- this.writeChunk = null;
- this.readChunk = null;
- this.chunkCount = 0;
- }
- finally
- {
- base.Dispose(disposing);
- }
- }
-
- ///
- public override void Flush()
+ public override int ReadByte()
{
+ Unsafe.SkipInit(out byte b);
+ return this.Read(MemoryMarshal.CreateSpan(ref b, 1)) == 1 ? b : -1;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(byte[] buffer, int offset, int count)
{
Guard.NotNull(buffer, nameof(buffer));
@@ -230,111 +106,70 @@ internal sealed class ChunkedMemoryStream : Stream
const string bufferMessage = "Offset subtracted from the buffer length is less than count.";
Guard.IsFalse(buffer.Length - offset < count, nameof(buffer), bufferMessage);
- return this.ReadImpl(buffer.AsSpan(offset, count));
+ return this.Read(buffer.AsSpan(offset, count));
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override int Read(Span buffer) => this.ReadImpl(buffer);
-
- private int ReadImpl(Span buffer)
+ public override int Read(Span buffer)
{
this.EnsureNotDisposed();
- if (this.readChunk is null)
- {
- if (this.memoryChunk is null)
- {
- return 0;
- }
+ int offset = 0;
+ int count = buffer.Length;
- this.readChunk = this.memoryChunk;
- this.readOffset = 0;
+ long remaining = this.length - this.position;
+ if (remaining <= 0)
+ {
+ // Already at the end of the stream, nothing to read
+ return 0;
}
- IMemoryOwner chunkBuffer = this.readChunk.Buffer;
- int chunkSize = this.readChunk.Length;
- if (this.readChunk.Next is null)
+ if (remaining > count)
{
- chunkSize = this.writeOffset;
+ remaining = count;
}
+ // 'remaining' can be less than the provided buffer length.
+ int bytesToRead = (int)remaining;
int bytesRead = 0;
- int offset = 0;
- int count = buffer.Length;
- while (count > 0)
+ while (bytesToRead > 0 && this.bufferIndex != this.memoryChunkBuffer.Length)
{
- if (this.readOffset == chunkSize)
+ bool moveToNextChunk = false;
+ MemoryChunk chunk = this.memoryChunkBuffer[this.bufferIndex];
+ int n = bytesToRead;
+ int remainingBytesInCurrentChunk = chunk.Length - this.chunkIndex;
+ if (n >= remainingBytesInCurrentChunk)
{
- // Exit if no more chunks are currently available
- if (this.readChunk.Next is null)
- {
- break;
- }
-
- this.readChunk = this.readChunk.Next;
- this.readOffset = 0;
- chunkBuffer = this.readChunk.Buffer;
- chunkSize = this.readChunk.Length;
- if (this.readChunk.Next is null)
- {
- chunkSize = this.writeOffset;
- }
+ n = remainingBytesInCurrentChunk;
+ moveToNextChunk = true;
}
- int readCount = Math.Min(count, chunkSize - this.readOffset);
- chunkBuffer.Slice(this.readOffset, readCount).CopyTo(buffer[offset..]);
- offset += readCount;
- count -= readCount;
- this.readOffset += readCount;
- bytesRead += readCount;
- }
-
- return bytesRead;
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override int ReadByte()
- {
- this.EnsureNotDisposed();
+ // Read n bytes from the current chunk
+ chunk.Buffer.Memory.Span.Slice(this.chunkIndex, n).CopyTo(buffer.Slice(offset, n));
+ bytesToRead -= n;
+ offset += n;
+ bytesRead += n;
- if (this.readChunk is null)
- {
- if (this.memoryChunk is null)
+ if (moveToNextChunk)
{
- return 0;
+ this.chunkIndex = 0;
+ this.bufferIndex++;
}
-
- this.readChunk = this.memoryChunk;
- this.readOffset = 0;
- }
-
- IMemoryOwner chunkBuffer = this.readChunk.Buffer;
- int chunkSize = this.readChunk.Length;
- if (this.readChunk.Next is null)
- {
- chunkSize = this.writeOffset;
- }
-
- if (this.readOffset == chunkSize)
- {
- // Exit if no more chunks are currently available
- if (this.readChunk.Next is null)
+ else
{
- return -1;
+ this.chunkIndex += n;
}
-
- this.readChunk = this.readChunk.Next;
- this.readOffset = 0;
- chunkBuffer = this.readChunk.Buffer;
}
- return chunkBuffer.GetSpan()[this.readOffset++];
+ this.position += bytesRead;
+ return bytesRead;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void WriteByte(byte value)
+ => this.Write(MemoryMarshal.CreateSpan(ref value, 1));
+
+ ///
public override void Write(byte[] buffer, int offset, int count)
{
Guard.NotNull(buffer, nameof(buffer));
@@ -344,157 +179,198 @@ internal sealed class ChunkedMemoryStream : Stream
const string bufferMessage = "Offset subtracted from the buffer length is less than count.";
Guard.IsFalse(buffer.Length - offset < count, nameof(buffer), bufferMessage);
- this.WriteImpl(buffer.AsSpan(offset, count));
+ this.Write(buffer.AsSpan(offset, count));
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public override void Write(ReadOnlySpan buffer) => this.WriteImpl(buffer);
-
- private void WriteImpl(ReadOnlySpan buffer)
+ public override void Write(ReadOnlySpan buffer)
{
this.EnsureNotDisposed();
- if (this.memoryChunk is null)
+ int offset = 0;
+ int count = buffer.Length;
+
+ long remaining = this.memoryChunkBuffer.Length - this.position;
+
+ // Ensure we have enough capacity to write the data.
+ while (remaining < count)
{
- this.memoryChunk = this.AllocateMemoryChunk();
- this.writeChunk = this.memoryChunk;
- this.writeOffset = 0;
+ this.memoryChunkBuffer.Expand();
+ remaining = this.memoryChunkBuffer.Length - this.position;
}
- Guard.NotNull(this.writeChunk);
-
- Span chunkBuffer = this.writeChunk.Buffer.GetSpan();
- int chunkSize = this.writeChunk.Length;
- int count = buffer.Length;
- int offset = 0;
- while (count > 0)
+ int bytesToWrite = count;
+ int bytesWritten = 0;
+ while (bytesToWrite > 0 && this.bufferIndex != this.memoryChunkBuffer.Length)
{
- if (this.writeOffset == chunkSize)
+ bool moveToNextChunk = false;
+ MemoryChunk chunk = this.memoryChunkBuffer[this.bufferIndex];
+ int n = bytesToWrite;
+ int remainingBytesInCurrentChunk = chunk.Length - this.chunkIndex;
+ if (n >= remainingBytesInCurrentChunk)
{
- // Allocate a new chunk if the current one is full
- this.writeChunk.Next = this.AllocateMemoryChunk();
- this.writeChunk = this.writeChunk.Next;
- this.writeOffset = 0;
- chunkBuffer = this.writeChunk.Buffer.GetSpan();
- chunkSize = this.writeChunk.Length;
+ n = remainingBytesInCurrentChunk;
+ moveToNextChunk = true;
}
- int copyCount = Math.Min(count, chunkSize - this.writeOffset);
- buffer.Slice(offset, copyCount).CopyTo(chunkBuffer[this.writeOffset..]);
+ // Write n bytes to the current chunk
+ buffer.Slice(offset, n).CopyTo(chunk.Buffer.Slice(this.chunkIndex, n));
+ bytesToWrite -= n;
+ offset += n;
+ bytesWritten += n;
- offset += copyCount;
- count -= copyCount;
- this.writeOffset += copyCount;
+ if (moveToNextChunk)
+ {
+ this.chunkIndex = 0;
+ this.bufferIndex++;
+ }
+ else
+ {
+ this.chunkIndex += n;
+ }
}
+
+ this.position += bytesWritten;
+ this.length += bytesWritten;
}
- ///
- public override void WriteByte(byte value)
+ ///
+ /// Writes the entire contents of this memory stream to another stream.
+ ///
+ /// The stream to write this memory stream to.
+ /// is .
+ /// The current or target stream is closed.
+ public void WriteTo(Stream stream)
{
+ Guard.NotNull(stream, nameof(stream));
this.EnsureNotDisposed();
- if (this.memoryChunk is null)
+ this.Position = 0;
+
+ long remaining = this.length - this.position;
+ if (remaining <= 0)
{
- this.memoryChunk = this.AllocateMemoryChunk();
- this.writeChunk = this.memoryChunk;
- this.writeOffset = 0;
+ // Already at the end of the stream, nothing to read
+ return;
}
- Guard.NotNull(this.writeChunk);
+ int bytesToRead = (int)remaining;
+ int bytesRead = 0;
+ while (bytesToRead > 0 && this.bufferIndex != this.memoryChunkBuffer.Length)
+ {
+ bool moveToNextChunk = false;
+ MemoryChunk chunk = this.memoryChunkBuffer[this.bufferIndex];
+ int n = bytesToRead;
+ int remainingBytesInCurrentChunk = chunk.Length - this.chunkIndex;
+ if (n >= remainingBytesInCurrentChunk)
+ {
+ n = remainingBytesInCurrentChunk;
+ moveToNextChunk = true;
+ }
- IMemoryOwner chunkBuffer = this.writeChunk.Buffer;
- int chunkSize = this.writeChunk.Length;
+ // Read n bytes from the current chunk
+ stream.Write(chunk.Buffer.Memory.Span.Slice(this.chunkIndex, n));
+ bytesToRead -= n;
+ bytesRead += n;
- if (this.writeOffset == chunkSize)
- {
- // Allocate a new chunk if the current one is full
- this.writeChunk.Next = this.AllocateMemoryChunk();
- this.writeChunk = this.writeChunk.Next;
- this.writeOffset = 0;
- chunkBuffer = this.writeChunk.Buffer;
+ if (moveToNextChunk)
+ {
+ this.chunkIndex = 0;
+ this.bufferIndex++;
+ }
+ else
+ {
+ this.chunkIndex += n;
+ }
}
- chunkBuffer.GetSpan()[this.writeOffset++] = value;
+ this.position += bytesRead;
}
///
- /// Copy entire buffer into an array.
+ /// Writes the stream contents to a byte array, regardless of the property.
///
- /// The .
+ /// A new .
public byte[] ToArray()
{
- int length = (int)this.Length; // This will throw if stream is closed
- byte[] copy = new byte[this.Length];
-
- MemoryChunk? backupReadChunk = this.readChunk;
- int backupReadOffset = this.readOffset;
-
- this.readChunk = this.memoryChunk;
- this.readOffset = 0;
- this.Read(copy, 0, length);
-
- this.readChunk = backupReadChunk;
- this.readOffset = backupReadOffset;
+ this.EnsureNotDisposed();
+ long position = this.position;
+ byte[] copy = new byte[this.length];
+ this.Position = 0;
+ _ = this.Read(copy, 0, copy.Length);
+ this.Position = position;
return copy;
}
- ///
- /// Write remainder of this stream to another stream.
- ///
- /// The stream to write to.
- public void WriteTo(Stream stream)
+ ///
+ protected override void Dispose(bool disposing)
{
- this.EnsureNotDisposed();
-
- Guard.NotNull(stream, nameof(stream));
+ if (this.isDisposed)
+ {
+ return;
+ }
- if (this.readChunk is null)
+ try
{
- if (this.memoryChunk is null)
+ this.isDisposed = true;
+ if (disposing)
{
- return;
+ this.memoryChunkBuffer.Dispose();
}
- this.readChunk = this.memoryChunk;
- this.readOffset = 0;
+ this.bufferIndex = 0;
+ this.chunkIndex = 0;
+ this.position = 0;
+ this.length = 0;
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ private void SetPosition(long value)
+ {
+ long newPosition = value;
+ if (newPosition < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value));
}
- IMemoryOwner chunkBuffer = this.readChunk.Buffer;
- int chunkSize = this.readChunk.Length;
- if (this.readChunk.Next is null)
+ this.position = newPosition;
+
+ // Find the current chunk & current chunk index
+ int currentChunkIndex = 0;
+ long offset = newPosition;
+
+ // If the new position is greater than the length of the stream, set the position to the end of the stream
+ if (offset > 0 && offset >= this.memoryChunkBuffer.Length)
{
- chunkSize = this.writeOffset;
+ this.bufferIndex = this.memoryChunkBuffer.ChunkCount - 1;
+ this.chunkIndex = this.memoryChunkBuffer[this.bufferIndex].Length - 1;
+ return;
}
- // Following code mirrors Read() logic (readChunk/readOffset should
- // point just past last byte of last chunk when done)
- // loop until end of chunks is found
- while (true)
+ // Loop through the current chunks, as we increment the chunk index, we subtract the length of the chunk
+ // from the offset. Once the offset is less than the length of the chunk, we have found the correct chunk.
+ while (offset != 0)
{
- if (this.readOffset == chunkSize)
+ int chunkLength = this.memoryChunkBuffer[currentChunkIndex].Length;
+ if (offset < chunkLength)
{
- // Exit if no more chunks are currently available
- if (this.readChunk.Next is null)
- {
- break;
- }
-
- this.readChunk = this.readChunk.Next;
- this.readOffset = 0;
- chunkBuffer = this.readChunk.Buffer;
- chunkSize = this.readChunk.Length;
- if (this.readChunk.Next is null)
- {
- chunkSize = this.writeOffset;
- }
+ // Found the correct chunk and the corresponding index
+ break;
}
- int writeCount = chunkSize - this.readOffset;
- stream.Write(chunkBuffer.GetSpan(), this.readOffset, writeCount);
- this.readOffset = chunkSize;
+ offset -= chunkLength;
+ currentChunkIndex++;
}
+
+ this.bufferIndex = currentChunkIndex;
+
+ // Safe to cast here as we know the offset is less than the chunk length.
+ this.chunkIndex = (int)offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -507,48 +383,66 @@ internal sealed class ChunkedMemoryStream : Stream
}
[MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowDisposed() => throw new ObjectDisposedException(null, "The stream is closed.");
+ private static void ThrowDisposed() => throw new ObjectDisposedException(nameof(ChunkedMemoryStream), "The stream is closed.");
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowArgumentOutOfRange(string value) => throw new ArgumentOutOfRangeException(value);
+ private sealed class MemoryChunkBuffer : IDisposable
+ {
+ private readonly List memoryChunks = new();
+ private readonly MemoryAllocator allocator;
+ private readonly int allocatorCapacity;
+ private bool isDisposed;
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void ThrowInvalidSeek() => throw new ArgumentException("Invalid seek origin.");
+ public MemoryChunkBuffer(MemoryAllocator allocator)
+ {
+ this.allocatorCapacity = allocator.GetBufferCapacityInBytes();
+ this.allocator = allocator;
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private MemoryChunk AllocateMemoryChunk()
- {
- // Tweak our buffer sizes to take the minimum of the provided buffer sizes
- // or the allocator buffer capacity which provides us with the largest
- // available contiguous buffer size.
- IMemoryOwner buffer = this.allocator.Allocate(Math.Min(this.allocatorCapacity, GetChunkSize(this.chunkCount++)));
+ public int ChunkCount => this.memoryChunks.Count;
- return new MemoryChunk(buffer)
+ public long Length { get; private set; }
+
+ public MemoryChunk this[int index] => this.memoryChunks[index];
+
+ public void Expand()
{
- Next = null,
- Length = buffer.Length()
- };
- }
+ IMemoryOwner buffer =
+ this.allocator.Allocate(Math.Min(this.allocatorCapacity, GetChunkSize(this.ChunkCount)));
- private static void ReleaseMemoryChunks(MemoryChunk? chunk)
- {
- while (chunk != null)
+ MemoryChunk chunk = new(buffer)
+ {
+ Length = buffer.Length()
+ };
+
+ this.memoryChunks.Add(chunk);
+ this.Length += chunk.Length;
+ }
+
+ public void Dispose()
{
- chunk.Dispose();
- chunk = chunk.Next;
+ if (!this.isDisposed)
+ {
+ foreach (MemoryChunk chunk in this.memoryChunks)
+ {
+ chunk.Dispose();
+ }
+
+ this.memoryChunks.Clear();
+ this.Length = 0;
+ this.isDisposed = true;
+ }
}
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetChunkSize(int i)
- {
- // Increment chunks sizes with moderate speed, but without using too many buffers from the same ArrayPool bucket of the default MemoryAllocator.
- // https://github.com/SixLabors/ImageSharp/pull/2006#issuecomment-1066244720
-#pragma warning disable IDE1006 // Naming Styles
- const int _128K = 1 << 17;
- const int _4M = 1 << 22;
- return i < 16 ? _128K * (1 << (int)((uint)i / 4)) : _4M;
-#pragma warning restore IDE1006 // Naming Styles
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int GetChunkSize(int i)
+ {
+ // Increment chunks sizes with moderate speed, but without using too many buffers from the
+ // same ArrayPool bucket of the default MemoryAllocator.
+ // https://github.com/SixLabors/ImageSharp/pull/2006#issuecomment-1066244720
+ const int b128K = 1 << 17;
+ const int b4M = 1 << 22;
+ return i < 16 ? b128K * (1 << (int)((uint)i / 4)) : b4M;
+ }
}
private sealed class MemoryChunk : IDisposable
@@ -559,27 +453,15 @@ internal sealed class ChunkedMemoryStream : Stream
public IMemoryOwner Buffer { get; }
- public MemoryChunk? Next { get; set; }
-
public int Length { get; init; }
- private void Dispose(bool disposing)
+ public void Dispose()
{
if (!this.isDisposed)
{
- if (disposing)
- {
- this.Buffer.Dispose();
- }
-
+ this.Buffer.Dispose();
this.isDisposed = true;
}
}
-
- public void Dispose()
- {
- this.Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
}
}
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index 7f58c6ecd..d2ee0f906 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -128,21 +128,18 @@ public abstract partial class Image
// Does the given stream contain enough data to fit in the header for the format
// and does that data match the format specification?
// Individual formats should still check since they are public.
- IImageFormat? format = null;
foreach (IImageFormatDetector formatDetector in configuration.ImageFormatsManager.FormatDetectors)
{
if (formatDetector.HeaderSize <= headersBuffer.Length && formatDetector.TryDetectFormat(headersBuffer, out IImageFormat? attemptFormat))
{
- format = attemptFormat;
+ return attemptFormat;
}
}
- if (format is null)
- {
- ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
- }
+ ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager);
- return format;
+ // Need to write this otherwise compiler is not happy
+ return null;
}
///
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index d3c403471..0d36340bf 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -13,6 +13,7 @@
Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga Tiff WebP NetCore
A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET
Debug;Release
+ true
@@ -29,14 +30,12 @@
- net8.0
- true
+ net8.0;net9.0
net8.0
- true
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 02403923d..7ec791838 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -160,7 +160,7 @@ public sealed class Image : Image
///
/// Gets the root frame.
///
- private IPixelSource PixelSourceUnsafe => this.frames.RootFrameUnsafe;
+ private ImageFrame PixelSourceUnsafe => this.frames.RootFrameUnsafe;
///
/// Gets or sets the pixel at the specified position.
@@ -324,7 +324,7 @@ public sealed class Image : Image
}
///
- /// Clones the current image
+ /// Clones the current image.
///
/// Returns a new image with all the same metadata as the original.
public Image Clone() => this.Clone(this.Configuration);
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index 1d2dca870..cf4a421b4 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -241,7 +241,7 @@ internal sealed class ExifWriter
return true;
}
- private static uint GetLength(IList values)
+ private static uint GetLength(List values)
{
if (values.Count == 0)
{
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
index 0d50b9809..ddfc62515 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
@@ -144,7 +144,7 @@ internal sealed partial class IccDataReader
ushort channelCount = this.ReadUInt16();
var colorant = (IccColorantEncoding)this.ReadUInt16();
- if (Enum.IsDefined(typeof(IccColorantEncoding), colorant) && colorant != IccColorantEncoding.Unknown)
+ if (Enum.IsDefined(colorant) && colorant != IccColorantEncoding.Unknown)
{
// The type is known and so are the values (they are constant)
// channelCount should always be 3 but it doesn't really matter if it's not
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
index be7350bc4..ac78318f2 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
@@ -155,9 +155,9 @@ public sealed class IccProfile : IDeepCloneable
}
return arrayValid &&
- Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
- Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) &&
- Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) &&
+ Enum.IsDefined(this.Header.DataColorSpace) &&
+ Enum.IsDefined(this.Header.ProfileConnectionSpace) &&
+ Enum.IsDefined(this.Header.RenderingIntent) &&
this.Header.Size is >= minSize and < maxSize;
}
diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index 4ac9546f3..6d1e8aaa5 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing;
///
public class AffineTransformBuilder
{
- private readonly List> transformMatrixFactories = new();
+ private readonly List> transformMatrixFactories = [];
///
/// Initializes a new instance of the class.
@@ -301,7 +301,8 @@ public class AffineTransformBuilder
///
/// The source image size.
/// The .
- public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
+ public Matrix3x2 BuildMatrix(Size sourceSize)
+ => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
///
/// Returns the combined transform matrix for a given source rectangle.
@@ -345,18 +346,8 @@ public class AffineTransformBuilder
/// The .
public Size GetTransformedSize(Rectangle sourceRectangle)
{
- Size size = sourceRectangle.Size;
-
- // Translate the origin matrix to cater for source rectangle offsets.
- Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location);
-
- foreach (Func factory in this.transformMatrixFactories)
- {
- matrix *= factory(size);
- CheckDegenerate(matrix);
- }
-
- return TransformUtils.GetTransformedSize(matrix, size, this.TransformSpace);
+ Matrix3x2 matrix = this.BuildMatrix(sourceRectangle);
+ return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix3x2 matrix)
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
index a680393c8..565a5746d 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs
@@ -21,12 +21,12 @@ internal static class BokehBlurKernelDataProvider
///
/// Gets the kernel scales to adjust the component values in each kernel
///
- private static IReadOnlyList KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f };
+ private static float[] KernelScales { get; } = new[] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f };
///
/// Gets the available bokeh blur kernel parameters
///
- private static IReadOnlyList KernelComponents { get; } = new[]
+ private static Vector4[][] KernelComponents { get; } = new[]
{
// 1 component
new[] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) },
@@ -112,7 +112,7 @@ internal static class BokehBlurKernelDataProvider
private static (Vector4[] Parameters, float Scale) GetParameters(int componentsCount)
{
// Prepare the kernel components
- int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Count));
+ int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Length));
return (KernelComponents[index], KernelScales[index]);
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
index c5c2a778e..888d51320 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
@@ -61,12 +61,12 @@ internal class AffineTransformProcessor : TransformProcessor, IR
if (matrix.Equals(Matrix3x2.Identity))
{
// The clone will be blank here copy all the pixel data over
- var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest);
- Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest);
+ Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest);
for (int y = 0; y < sourceBuffer.Height; y++)
{
- sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y));
+ sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y));
}
return;
@@ -77,7 +77,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
if (sampler is NearestNeighborResampler)
{
- var nnOperation = new NNAffineOperation(
+ NNAffineOperation nnOperation = new(
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
destination.PixelBuffer,
@@ -91,7 +91,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
return;
}
- var operation = new AffineOperation(
+ AffineOperation operation = new(
configuration,
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
@@ -128,17 +128,17 @@ internal class AffineTransformProcessor : TransformProcessor, IR
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
- Span destRow = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
- for (int x = 0; x < destRow.Length; x++)
+ for (int x = 0; x < destinationRowSpan.Length; x++)
{
- var point = Vector2.Transform(new Vector2(x, y), this.matrix);
+ Vector2 point = Vector2.Transform(new Vector2(x, y), this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
- destRow[x] = this.source.GetElementUnsafe(px, py);
+ destinationRowSpan[x] = this.source.GetElementUnsafe(px, py);
}
}
}
@@ -195,16 +195,16 @@ internal class AffineTransformProcessor : TransformProcessor, IR
for (int y = rows.Min; y < rows.Max; y++)
{
- Span rowSpan = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations.Instance.ToVector4(
this.configuration,
- rowSpan,
+ destinationRowSpan,
span,
PixelConversionModifiers.Scale);
for (int x = 0; x < span.Length; x++)
{
- var point = Vector2.Transform(new Vector2(x, y), matrix);
+ Vector2 point = Vector2.Transform(new Vector2(x, y), matrix);
float pY = point.Y;
float pX = point.X;
@@ -221,13 +221,14 @@ internal class AffineTransformProcessor : TransformProcessor, IR
Vector4 sum = Vector4.Zero;
for (int yK = top; yK <= bottom; yK++)
{
+ Span sourceRowSpan = this.source.DangerousGetRowSpan(yK);
float yWeight = sampler.GetValue(yK - pY);
for (int xK = left; xK <= right; xK++)
{
float xWeight = sampler.GetValue(xK - pX);
- Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
+ Vector4 current = sourceRowSpan[xK].ToScaledVector4();
Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
@@ -240,7 +241,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR
PixelOperations.Instance.FromVector4Destructive(
this.configuration,
span,
- rowSpan,
+ destinationRowSpan,
PixelConversionModifiers.Scale);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs
new file mode 100644
index 000000000..1190de435
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/GaussianEliminationSolver.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
+
+///
+/// Represents a solver for systems of linear equations using the Gaussian Elimination method.
+/// This class applies Gaussian Elimination to transform the matrix into row echelon form and then performs back substitution to find the solution vector.
+/// This implementation is based on:
+///
+internal static class GaussianEliminationSolver
+{
+ ///
+ /// Solves the system of linear equations represented by the given matrix and result vector using Gaussian Elimination.
+ ///
+ /// The square matrix representing the coefficients of the linear equations.
+ /// The vector representing the constants on the right-hand side of the linear equations.
+ /// Thrown if the matrix is singular and cannot be solved.
+ ///
+ /// The matrix passed to this method must be a square matrix.
+ /// If the matrix is singular (i.e., has no unique solution), an will be thrown.
+ ///
+ public static void Solve(double[][] matrix, double[] result)
+ {
+ TransformToRowEchelonForm(matrix, result);
+ ApplyBackSubstitution(matrix, result);
+ }
+
+ private static void TransformToRowEchelonForm(double[][] matrix, double[] result)
+ {
+ int colCount = matrix.Length;
+ int rowCount = matrix[0].Length;
+ int pivotRow = 0;
+ for (int pivotCol = 0; pivotCol < colCount; pivotCol++)
+ {
+ double maxValue = double.Abs(matrix[pivotRow][pivotCol]);
+ int maxIndex = pivotRow;
+ for (int r = pivotRow + 1; r < rowCount; r++)
+ {
+ double value = double.Abs(matrix[r][pivotCol]);
+ if (value > maxValue)
+ {
+ maxIndex = r;
+ maxValue = value;
+ }
+ }
+
+ if (matrix[maxIndex][pivotCol] == 0)
+ {
+ throw new NotSupportedException("Matrix is singular and cannot be solve");
+ }
+
+ (matrix[pivotRow], matrix[maxIndex]) = (matrix[maxIndex], matrix[pivotRow]);
+ (result[pivotRow], result[maxIndex]) = (result[maxIndex], result[pivotRow]);
+
+ for (int r = pivotRow + 1; r < rowCount; r++)
+ {
+ double fraction = matrix[r][pivotCol] / matrix[pivotRow][pivotCol];
+ for (int c = pivotCol + 1; c < colCount; c++)
+ {
+ matrix[r][c] -= matrix[pivotRow][c] * fraction;
+ }
+
+ result[r] -= result[pivotRow] * fraction;
+ matrix[r][pivotCol] = 0;
+ }
+
+ pivotRow++;
+ }
+ }
+
+ private static void ApplyBackSubstitution(double[][] matrix, double[] result)
+ {
+ int rowCount = matrix[0].Length;
+
+ for (int row = rowCount - 1; row >= 0; row--)
+ {
+ result[row] /= matrix[row][row];
+
+ for (int r = 0; r < row; r++)
+ {
+ result[r] -= result[row] * matrix[r][row];
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
index b741dc4ee..068f69ceb 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
@@ -61,12 +61,12 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (matrix.Equals(Matrix4x4.Identity))
{
// The clone will be blank here copy all the pixel data over
- var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
+ Rectangle interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds());
Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest);
- Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest);
+ Buffer2DRegion destinationBuffer = destination.PixelBuffer.GetRegion(interest);
for (int y = 0; y < sourceBuffer.Height; y++)
{
- sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y));
+ sourceBuffer.DangerousGetRowSpan(y).CopyTo(destinationBuffer.DangerousGetRowSpan(y));
}
return;
@@ -77,7 +77,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (sampler is NearestNeighborResampler)
{
- var nnOperation = new NNProjectiveOperation(
+ NNProjectiveOperation nnOperation = new(
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
destination.PixelBuffer,
@@ -91,7 +91,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
return;
}
- var operation = new ProjectiveOperation(
+ ProjectiveOperation operation = new(
configuration,
source.PixelBuffer,
Rectangle.Intersect(this.SourceRectangle, source.Bounds()),
@@ -128,9 +128,9 @@ internal class ProjectiveTransformProcessor : TransformProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
- Span destRow = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
- for (int x = 0; x < destRow.Length; x++)
+ for (int x = 0; x < destinationRowSpan.Length; x++)
{
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
@@ -138,7 +138,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
if (this.bounds.Contains(px, py))
{
- destRow[x] = this.source.GetElementUnsafe(px, py);
+ destinationRowSpan[x] = this.source.GetElementUnsafe(px, py);
}
}
}
@@ -195,10 +195,10 @@ internal class ProjectiveTransformProcessor : TransformProcessor
for (int y = rows.Min; y < rows.Max; y++)
{
- Span rowSpan = this.destination.DangerousGetRowSpan(y);
+ Span destinationRowSpan = this.destination.DangerousGetRowSpan(y);
PixelOperations.Instance.ToVector4(
this.configuration,
- rowSpan,
+ destinationRowSpan,
span,
PixelConversionModifiers.Scale);
@@ -221,13 +221,14 @@ internal class ProjectiveTransformProcessor : TransformProcessor
Vector4 sum = Vector4.Zero;
for (int yK = top; yK <= bottom; yK++)
{
+ Span sourceRowSpan = this.source.DangerousGetRowSpan(yK);
float yWeight = sampler.GetValue(yK - pY);
for (int xK = left; xK <= right; xK++)
{
float xWeight = sampler.GetValue(xK - pX);
- Vector4 current = this.source.GetElementUnsafe(xK, yK).ToScaledVector4();
+ Vector4 current = sourceRowSpan[xK].ToScaledVector4();
Numerics.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
@@ -240,7 +241,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor
PixelOperations.Instance.FromVector4Destructive(
this.configuration,
span,
- rowSpan,
+ destinationRowSpan,
PixelConversionModifiers.Scale);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
index 62ea5e830..47b3250b8 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs
@@ -3,6 +3,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms;
@@ -278,6 +279,91 @@ internal static class TransformUtils
return matrix;
}
+ ///
+ /// Computes the projection matrix for a quad distortion transformation.
+ ///
+ /// The source rectangle.
+ /// The top-left point of the distorted quad.
+ /// The top-right point of the distorted quad.
+ /// The bottom-right point of the distorted quad.
+ /// The bottom-left point of the distorted quad.
+ /// The to use when creating the matrix.
+ /// The computed projection matrix for the quad distortion.
+ ///
+ /// This method is based on the algorithm described in the following article:
+ ///
+ ///
+ public static Matrix4x4 CreateQuadDistortionMatrix(
+ Rectangle rectangle,
+ PointF topLeft,
+ PointF topRight,
+ PointF bottomRight,
+ PointF bottomLeft,
+ TransformSpace transformSpace)
+ {
+ PointF p1 = new(rectangle.X, rectangle.Y);
+ PointF p2 = new(rectangle.X + rectangle.Width, rectangle.Y);
+ PointF p3 = new(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height);
+ PointF p4 = new(rectangle.X, rectangle.Y + rectangle.Height);
+
+ PointF q1 = topLeft;
+ PointF q2 = topRight;
+ PointF q3 = bottomRight;
+ PointF q4 = bottomLeft;
+
+ double[][] matrixData =
+ [
+ [p1.X, p1.Y, 1, 0, 0, 0, -p1.X * q1.X, -p1.Y * q1.X],
+ [0, 0, 0, p1.X, p1.Y, 1, -p1.X * q1.Y, -p1.Y * q1.Y],
+ [p2.X, p2.Y, 1, 0, 0, 0, -p2.X * q2.X, -p2.Y * q2.X],
+ [0, 0, 0, p2.X, p2.Y, 1, -p2.X * q2.Y, -p2.Y * q2.Y],
+ [p3.X, p3.Y, 1, 0, 0, 0, -p3.X * q3.X, -p3.Y * q3.X],
+ [0, 0, 0, p3.X, p3.Y, 1, -p3.X * q3.Y, -p3.Y * q3.Y],
+ [p4.X, p4.Y, 1, 0, 0, 0, -p4.X * q4.X, -p4.Y * q4.X],
+ [0, 0, 0, p4.X, p4.Y, 1, -p4.X * q4.Y, -p4.Y * q4.Y],
+ ];
+
+ double[] b =
+ [
+ q1.X,
+ q1.Y,
+ q2.X,
+ q2.Y,
+ q3.X,
+ q3.Y,
+ q4.X,
+ q4.Y,
+ ];
+
+ GaussianEliminationSolver.Solve(matrixData, b);
+
+#pragma warning disable SA1117
+ Matrix4x4 projectionMatrix = new(
+ (float)b[0], (float)b[3], 0, (float)b[6],
+ (float)b[1], (float)b[4], 0, (float)b[7],
+ 0, 0, 1, 0,
+ (float)b[2], (float)b[5], 0, 1);
+#pragma warning restore SA1117
+
+ // Check if the matrix involves only affine transformations by inspecting the relevant components.
+ // We want to use pixel space for calculations only if the transformation is purely 2D and does not include
+ // any perspective effects, non-standard scaling, or unusual translations that could distort the image.
+ if (transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(projectionMatrix))
+ {
+ if (projectionMatrix.M41 != 0)
+ {
+ projectionMatrix.M41--;
+ }
+
+ if (projectionMatrix.M42 != 0)
+ {
+ projectionMatrix.M42--;
+ }
+ }
+
+ return projectionMatrix;
+ }
+
///
/// Returns the size relative to the source for the given transformation matrix.
///
@@ -293,15 +379,16 @@ internal static class TransformUtils
///
/// The transformation matrix.
/// The source size.
+ /// The used when generating the matrix.
///
/// The .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Size GetTransformedSize(Matrix4x4 matrix, Size size)
+ public static Size GetTransformedSize(Matrix4x4 matrix, Size size, TransformSpace transformSpace)
{
Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!");
- if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
+ if (matrix.IsIdentity || matrix.Equals(default))
{
return size;
}
@@ -309,27 +396,7 @@ internal static class TransformUtils
// Check if the matrix involves only affine transformations by inspecting the relevant components.
// We want to use pixel space for calculations only if the transformation is purely 2D and does not include
// any perspective effects, non-standard scaling, or unusual translations that could distort the image.
- // The conditions are as follows:
- bool usePixelSpace =
-
- // 1. Ensure there's no perspective distortion:
- // M34 corresponds to the perspective component. For a purely 2D affine transformation, this should be 0.
- (matrix.M34 == 0) &&
-
- // 2. Ensure standard affine transformation without any unusual depth or perspective scaling:
- // M44 should be 1 for a standard affine transformation. If M44 is not 1, it indicates non-standard depth
- // scaling or perspective, which suggests a more complex transformation.
- (matrix.M44 == 1) &&
-
- // 3. Ensure no unusual translation in the x-direction:
- // M14 represents translation in the x-direction that might be part of a more complex transformation.
- // For standard affine transformations, M14 should be 0.
- (matrix.M14 == 0) &&
-
- // 4. Ensure no unusual translation in the y-direction:
- // M24 represents translation in the y-direction that might be part of a more complex transformation.
- // For standard affine transformations, M24 should be 0.
- (matrix.M24 == 0);
+ bool usePixelSpace = transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(matrix);
// Define an offset size to translate between pixel space and coordinate space.
// When using pixel space, apply a scaling sensitive offset to translate to discrete pixel coordinates.
@@ -376,7 +443,7 @@ internal static class TransformUtils
{
Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!");
- if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
+ if (matrix.IsIdentity || matrix.Equals(default))
{
return size;
}
@@ -412,7 +479,7 @@ internal static class TransformUtils
///
private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix3x2 matrix, out Rectangle bounds)
{
- if (rectangle.Equals(default) || Matrix3x2.Identity.Equals(matrix))
+ if (matrix.IsIdentity || rectangle.Equals(default))
{
bounds = default;
return false;
@@ -439,7 +506,7 @@ internal static class TransformUtils
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix4x4 matrix, out Rectangle bounds)
{
- if (rectangle.Equals(default) || Matrix4x4.Identity.Equals(matrix))
+ if (matrix.IsIdentity || rectangle.Equals(default))
{
bounds = default;
return false;
@@ -492,4 +559,44 @@ internal static class TransformUtils
(int)Math.Ceiling(right),
(int)Math.Ceiling(bottom));
}
+
+ private static bool IsAffineRotationOrSkew(Matrix4x4 matrix)
+ {
+ const float epsilon = 1e-6f;
+
+ // Check if the matrix is affine (last column should be [0, 0, 0, 1])
+ if (Math.Abs(matrix.M14) > epsilon ||
+ Math.Abs(matrix.M24) > epsilon ||
+ Math.Abs(matrix.M34) > epsilon ||
+ Math.Abs(matrix.M44 - 1f) > epsilon)
+ {
+ return false;
+ }
+
+ // Translation component (M41, m42) are allowed, others are not.
+ if (Math.Abs(matrix.M43) > epsilon)
+ {
+ return false;
+ }
+
+ // Extract the linear (rotation and skew) part of the matrix
+ // Upper-left 3x3 matrix
+ float m11 = matrix.M11, m12 = matrix.M12, m13 = matrix.M13;
+ float m21 = matrix.M21, m22 = matrix.M22, m23 = matrix.M23;
+ float m31 = matrix.M31, m32 = matrix.M32, m33 = matrix.M33;
+
+ // Compute the determinant of the linear part
+ float determinant = (m11 * ((m22 * m33) - (m23 * m32))) -
+ (m12 * ((m21 * m33) - (m23 * m31))) +
+ (m13 * ((m21 * m32) - (m22 * m31)));
+
+ // Check if the determinant is approximately ±1 (no scaling)
+ if (Math.Abs(Math.Abs(determinant) - 1f) > epsilon)
+ {
+ return false;
+ }
+
+ // All checks passed; the matrix represents rotation and/or skew (with possible translation)
+ return true;
+ }
}
diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
index 9027ee726..82b897ea5 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> transformMatrixFactories = new();
+ private readonly List> transformMatrixFactories = [];
///
/// Initializes a new instance of the class.
@@ -279,6 +279,30 @@ public class ProjectiveTransformBuilder
public ProjectiveTransformBuilder AppendTranslation(Vector2 position)
=> this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0)));
+ ///
+ /// Prepends a quad distortion matrix using the specified corner points.
+ ///
+ /// The top-left corner point of the distorted quad.
+ /// The top-right corner point of the distorted quad.
+ /// The bottom-right corner point of the distorted quad.
+ /// The bottom-left corner point of the distorted quad.
+ /// The .
+ public ProjectiveTransformBuilder PrependQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft)
+ => this.Prepend(size => TransformUtils.CreateQuadDistortionMatrix(
+ new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace));
+
+ ///
+ /// Appends a quad distortion matrix using the specified corner points.
+ ///
+ /// The top-left corner point of the distorted quad.
+ /// The top-right corner point of the distorted quad.
+ /// The bottom-right corner point of the distorted quad.
+ /// The bottom-left corner point of the distorted quad.
+ /// The .
+ public ProjectiveTransformBuilder AppendQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft)
+ => this.Append(size => TransformUtils.CreateQuadDistortionMatrix(
+ new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace));
+
///
/// Prepends a raw matrix.
///
@@ -361,18 +385,8 @@ public class ProjectiveTransformBuilder
/// The .
public Size GetTransformedSize(Rectangle sourceRectangle)
{
- Size size = sourceRectangle.Size;
-
- // Translate the origin matrix to cater for source rectangle offsets.
- Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0));
-
- foreach (Func factory in this.transformMatrixFactories)
- {
- matrix *= factory(size);
- CheckDegenerate(matrix);
- }
-
- return TransformUtils.GetTransformedSize(matrix, size);
+ Matrix4x4 matrix = this.BuildMatrix(sourceRectangle);
+ return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace);
}
private static void CheckDegenerate(Matrix4x4 matrix)
diff --git a/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs
index 1b6663e70..8728dd671 100644
--- a/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs
+++ b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs
@@ -47,37 +47,37 @@ public class Pad3Shuffle4Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |------------------------- |------------------- |-------------------------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:|------:|------:|------:|----------:|
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 120.64 ns | 7.190 ns | 21.200 ns | 114.26 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4 | 2. AVX | Empty | 96 | 23.63 ns | 0.175 ns | 0.155 ns | 23.65 ns | 0.15 | 0.01 | - | - | - | - |
-// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - |
+// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 96 | 25.25 ns | 0.356 ns | 0.298 ns | 25.27 ns | 0.17 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 14.80 ns | 0.358 ns | 1.032 ns | 14.64 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 96 | 24.84 ns | 0.376 ns | 0.333 ns | 24.74 ns | 1.57 | 0.06 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 96 | 24.58 ns | 0.471 ns | 0.704 ns | 24.38 ns | 1.60 | 0.09 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 258.92 ns | 4.873 ns | 4.069 ns | 257.95 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4 | 2. AVX | Empty | 384 | 41.41 ns | 0.859 ns | 1.204 ns | 41.33 ns | 0.16 | 0.00 | - | - | - | - |
-// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 384 | 40.74 ns | 0.848 ns | 0.793 ns | 40.48 ns | 0.16 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 74.50 ns | 0.490 ns | 0.383 ns | 74.49 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 384 | 40.74 ns | 0.624 ns | 0.584 ns | 40.72 ns | 0.55 | 0.01 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 384 | 38.28 ns | 0.534 ns | 0.417 ns | 38.22 ns | 0.51 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 503.91 ns | 6.466 ns | 6.048 ns | 501.58 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4 | 2. AVX | Empty | 768 | 62.86 ns | 0.332 ns | 0.277 ns | 62.80 ns | 0.12 | 0.00 | - | - | - | - |
-// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 768 | 64.59 ns | 0.469 ns | 0.415 ns | 64.62 ns | 0.13 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 110.51 ns | 0.592 ns | 0.554 ns | 110.33 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 768 | 64.72 ns | 1.306 ns | 1.090 ns | 64.51 ns | 0.59 | 0.01 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 768 | 62.11 ns | 0.816 ns | 0.682 ns | 61.98 ns | 0.56 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 1,005.84 ns | 13.176 ns | 12.325 ns | 1,004.70 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4 | 2. AVX | Empty | 1536 | 110.05 ns | 0.256 ns | 0.214 ns | 110.04 ns | 0.11 | 0.00 | - | - | - | - |
-// | Pad3Shuffle4 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4 | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 110.23 ns | 0.545 ns | 0.483 ns | 110.09 ns | 0.11 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 2. AVX | Empty | 1536 | 111.54 ns | 2.173 ns | 2.901 ns | 111.27 ns | 0.51 | 0.01 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - |
// 2023-02-21
// ##########
@@ -94,34 +94,34 @@ public class Pad3Shuffle4Channel
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:|
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 57.45 ns | 0.126 ns | 0.118 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.70 ns | 0.105 ns | 0.098 ns | 0.26 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 57.45 ns | 0.126 ns | 0.118 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 96 | 14.70 ns | 0.105 ns | 0.098 ns | 0.26 | - | - | - | - |
// | Pad3Shuffle4 | 3. AVX | Empty | 96 | 14.63 ns | 0.070 ns | 0.062 ns | 0.25 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 12.08 ns | 0.028 ns | 0.025 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.04 ns | 0.050 ns | 0.044 ns | 1.16 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 12.08 ns | 0.028 ns | 0.025 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 96 | 14.04 ns | 0.050 ns | 0.044 ns | 1.16 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 96 | 13.90 ns | 0.086 ns | 0.080 ns | 1.15 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 202.67 ns | 2.010 ns | 1.678 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.54 ns | 0.060 ns | 0.053 ns | 0.13 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 202.67 ns | 2.010 ns | 1.678 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 384 | 25.54 ns | 0.060 ns | 0.053 ns | 0.13 | - | - | - | - |
// | Pad3Shuffle4 | 3. AVX | Empty | 384 | 25.72 ns | 0.139 ns | 0.130 ns | 0.13 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 60.35 ns | 0.080 ns | 0.071 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.18 ns | 0.388 ns | 0.324 ns | 0.42 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 60.35 ns | 0.080 ns | 0.071 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 384 | 25.18 ns | 0.388 ns | 0.324 ns | 0.42 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 384 | 26.21 ns | 0.067 ns | 0.059 ns | 0.43 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 393.88 ns | 1.353 ns | 1.199 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 39.44 ns | 0.230 ns | 0.204 ns | 0.10 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 393.88 ns | 1.353 ns | 1.199 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 768 | 39.44 ns | 0.230 ns | 0.204 ns | 0.10 | - | - | - | - |
// | Pad3Shuffle4 | 3. AVX | Empty | 768 | 39.51 ns | 0.108 ns | 0.101 ns | 0.10 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 112.02 ns | 0.140 ns | 0.131 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 768 | 38.60 ns | 0.091 ns | 0.080 ns | 0.34 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 112.02 ns | 0.140 ns | 0.131 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 768 | 38.60 ns | 0.091 ns | 0.080 ns | 0.34 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 768 | 38.18 ns | 0.100 ns | 0.084 ns | 0.34 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 777.95 ns | 1.719 ns | 1.342 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 73.11 ns | 0.090 ns | 0.075 ns | 0.09 | - | - | - | - |
+// | Pad3Shuffle4 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 777.95 ns | 1.719 ns | 1.342 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4 | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 73.11 ns | 0.090 ns | 0.075 ns | 0.09 | - | - | - | - |
// | Pad3Shuffle4 | 3. AVX | Empty | 1536 | 73.41 ns | 0.125 ns | 0.117 ns | 0.09 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 218.14 ns | 0.377 ns | 0.334 ns | 1.00 | - | - | - | - |
-// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 72.55 ns | 1.418 ns | 1.184 ns | 0.33 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 218.14 ns | 0.377 ns | 0.334 ns | 1.00 | - | - | - | - |
+// | Pad3Shuffle4FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 72.55 ns | 1.418 ns | 1.184 ns | 0.33 | - | - | - | - |
// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 1536 | 73.15 ns | 0.330 ns | 0.292 ns | 0.34 | - | - | - | - |
diff --git a/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs
index 8b7b89eb3..e4c12900f 100644
--- a/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs
+++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs
@@ -43,21 +43,21 @@ public class Shuffle3Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |--------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:|
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 48.46 ns | 1.034 ns | 2.438 ns | 47.46 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle3 | 2. AVX | Empty | 96 | 32.42 ns | 0.537 ns | 0.476 ns | 32.34 ns | 0.66 | 0.04 | - | - | - | - |
-// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - |
+// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 96 | 32.51 ns | 0.373 ns | 0.349 ns | 32.56 ns | 0.66 | 0.03 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 199.04 ns | 1.512 ns | 1.180 ns | 199.17 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle3 | 2. AVX | Empty | 384 | 71.20 ns | 2.654 ns | 7.784 ns | 69.60 ns | 0.41 | 0.02 | - | - | - | - |
-// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - |
+// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 384 | 63.23 ns | 0.569 ns | 0.505 ns | 63.21 ns | 0.32 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 391.28 ns | 5.087 ns | 3.972 ns | 391.22 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle3 | 2. AVX | Empty | 768 | 109.12 ns | 2.149 ns | 2.010 ns | 108.66 ns | 0.28 | 0.01 | - | - | - | - |
-// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - |
+// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 768 | 106.51 ns | 0.734 ns | 0.613 ns | 106.56 ns | 0.27 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle3 | 2. AVX | Empty | 1536 | 190.41 ns | 1.090 ns | 0.851 ns | 190.38 ns | 0.25 | 0.00 | - | - | - | - |
-// | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - |
+// | Shuffle3 | 3. SSE | DOTNET_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - |
// 2023-02-21
// ##########
@@ -74,18 +74,18 @@ public class Shuffle3Channel
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |--------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:|
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 44.55 ns | 0.564 ns | 0.528 ns | 1.00 | - | - | - | - |
-// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 15.46 ns | 0.064 ns | 0.060 ns | 0.35 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 96 | 44.55 ns | 0.564 ns | 0.528 ns | 1.00 | - | - | - | - |
+// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 96 | 15.46 ns | 0.064 ns | 0.060 ns | 0.35 | - | - | - | - |
// | Shuffle3 | 3. AVX | Empty | 96 | 15.18 ns | 0.056 ns | 0.053 ns | 0.34 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 155.68 ns | 0.539 ns | 0.504 ns | 1.00 | - | - | - | - |
-// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 30.04 ns | 0.100 ns | 0.089 ns | 0.19 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 384 | 155.68 ns | 0.539 ns | 0.504 ns | 1.00 | - | - | - | - |
+// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 384 | 30.04 ns | 0.100 ns | 0.089 ns | 0.19 | - | - | - | - |
// | Shuffle3 | 3. AVX | Empty | 384 | 29.70 ns | 0.061 ns | 0.054 ns | 0.19 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 302.76 ns | 1.023 ns | 0.957 ns | 1.00 | - | - | - | - |
-// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 50.24 ns | 0.098 ns | 0.092 ns | 0.17 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 768 | 302.76 ns | 1.023 ns | 0.957 ns | 1.00 | - | - | - | - |
+// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 768 | 50.24 ns | 0.098 ns | 0.092 ns | 0.17 | - | - | - | - |
// | Shuffle3 | 3. AVX | Empty | 768 | 49.28 ns | 0.156 ns | 0.131 ns | 0.16 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 596.53 ns | 2.675 ns | 2.503 ns | 1.00 | - | - | - | - |
-// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 94.09 ns | 0.312 ns | 0.260 ns | 0.16 | - | - | - | - |
+// | Shuffle3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1536 | 596.53 ns | 2.675 ns | 2.503 ns | 1.00 | - | - | - | - |
+// | Shuffle3 | 2. SSE | DOTNET_EnableAVX=0 | 1536 | 94.09 ns | 0.312 ns | 0.260 ns | 0.16 | - | - | - | - |
// | Shuffle3 | 3. AVX | Empty | 1536 | 93.57 ns | 0.196 ns | 0.183 ns | 0.16 | - | - | - | - |
diff --git a/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs
index 5ade55c73..579e2c54d 100644
--- a/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs
+++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs
@@ -47,45 +47,45 @@ public class Shuffle4Slice3Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|----------:|------:|--------:|------:|------:|------:|----------:|
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 56.44 ns | 2.843 ns | 8.382 ns | 56.70 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3 | 2. AVX | Empty | 128 | 27.15 ns | 0.556 ns | 0.762 ns | 27.34 ns | 0.41 | 0.03 | - | - | - | - |
-// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - |
+// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 128 | 26.36 ns | 0.321 ns | 0.268 ns | 26.26 ns | 0.38 | 0.02 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 25.85 ns | 0.494 ns | 0.462 ns | 25.84 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 128 | 26.15 ns | 0.113 ns | 0.106 ns | 26.16 ns | 1.01 | 0.02 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 128 | 25.57 ns | 0.078 ns | 0.061 ns | 25.56 ns | 0.99 | 0.02 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 97.47 ns | 0.327 ns | 0.289 ns | 97.35 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3 | 2. AVX | Empty | 256 | 32.61 ns | 0.107 ns | 0.095 ns | 32.62 ns | 0.33 | 0.00 | - | - | - | - |
-// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 256 | 33.21 ns | 0.169 ns | 0.150 ns | 33.15 ns | 0.34 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 52.34 ns | 0.779 ns | 0.729 ns | 51.94 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 256 | 32.16 ns | 0.111 ns | 0.104 ns | 32.16 ns | 0.61 | 0.01 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 256 | 33.61 ns | 0.342 ns | 0.319 ns | 33.62 ns | 0.64 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 210.74 ns | 3.825 ns | 5.956 ns | 207.70 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3 | 2. AVX | Empty | 512 | 51.03 ns | 0.535 ns | 0.501 ns | 51.18 ns | 0.24 | 0.01 | - | - | - | - |
-// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - |
+// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 512 | 66.60 ns | 1.313 ns | 1.613 ns | 65.93 ns | 0.31 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 119.12 ns | 1.905 ns | 1.689 ns | 118.52 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 512 | 50.33 ns | 0.382 ns | 0.339 ns | 50.41 ns | 0.42 | 0.01 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 512 | 49.25 ns | 0.555 ns | 0.492 ns | 49.26 ns | 0.41 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 423.55 ns | 4.891 ns | 4.336 ns | 423.27 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3 | 2. AVX | Empty | 1024 | 77.13 ns | 1.355 ns | 2.264 ns | 76.19 ns | 0.19 | 0.01 | - | - | - | - |
-// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 79.39 ns | 0.103 ns | 0.086 ns | 79.37 ns | 0.19 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 226.57 ns | 2.930 ns | 2.598 ns | 226.10 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 1024 | 80.25 ns | 1.647 ns | 2.082 ns | 80.98 ns | 0.35 | 0.01 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 84.99 ns | 1.234 ns | 1.155 ns | 85.60 ns | 0.38 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 794.96 ns | 1.735 ns | 1.538 ns | 795.15 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3 | 2. AVX | Empty | 2048 | 128.41 ns | 0.417 ns | 0.390 ns | 128.24 ns | 0.16 | 0.00 | - | - | - | - |
-// | Shuffle4Slice3 | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3 | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 127.24 ns | 0.294 ns | 0.229 ns | 127.23 ns | 0.16 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 2. AVX | Empty | 2048 | 126.93 ns | 0.382 ns | 0.339 ns | 126.94 ns | 0.33 | 0.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - |
// 2023-02-21
// ##########
@@ -102,42 +102,42 @@ public class Shuffle4Slice3Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:|
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 45.59 ns | 0.166 ns | 0.147 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 128 | 15.62 ns | 0.056 ns | 0.052 ns | 0.34 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 45.59 ns | 0.166 ns | 0.147 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 128 | 15.62 ns | 0.056 ns | 0.052 ns | 0.34 | - | - | - | - |
// | Shuffle4Slice3 | 3. AVX | Empty | 128 | 16.37 ns | 0.047 ns | 0.040 ns | 0.36 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 13.23 ns | 0.028 ns | 0.026 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 128 | 14.41 ns | 0.013 ns | 0.012 ns | 1.09 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 13.23 ns | 0.028 ns | 0.026 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 128 | 14.41 ns | 0.013 ns | 0.012 ns | 1.09 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 128 | 14.70 ns | 0.050 ns | 0.047 ns | 1.11 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 85.48 ns | 0.192 ns | 0.179 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.18 ns | 0.230 ns | 0.204 ns | 0.22 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 85.48 ns | 0.192 ns | 0.179 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 256 | 19.18 ns | 0.230 ns | 0.204 ns | 0.22 | - | - | - | - |
// | Shuffle4Slice3 | 3. AVX | Empty | 256 | 18.66 ns | 0.017 ns | 0.015 ns | 0.22 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 24.34 ns | 0.078 ns | 0.073 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 256 | 18.58 ns | 0.061 ns | 0.057 ns | 0.76 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 24.34 ns | 0.078 ns | 0.073 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 256 | 18.58 ns | 0.061 ns | 0.057 ns | 0.76 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 256 | 19.23 ns | 0.018 ns | 0.016 ns | 0.79 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 165.31 ns | 0.742 ns | 0.694 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 512 | 28.10 ns | 0.077 ns | 0.068 ns | 0.17 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 165.31 ns | 0.742 ns | 0.694 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 512 | 28.10 ns | 0.077 ns | 0.068 ns | 0.17 | - | - | - | - |
// | Shuffle4Slice3 | 3. AVX | Empty | 512 | 28.99 ns | 0.018 ns | 0.014 ns | 0.18 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 53.45 ns | 0.270 ns | 0.226 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 512 | 27.50 ns | 0.034 ns | 0.028 ns | 0.51 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 53.45 ns | 0.270 ns | 0.226 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 512 | 27.50 ns | 0.034 ns | 0.028 ns | 0.51 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 512 | 28.76 ns | 0.017 ns | 0.015 ns | 0.54 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 323.87 ns | 0.549 ns | 0.487 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.81 ns | 0.056 ns | 0.050 ns | 0.13 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 323.87 ns | 0.549 ns | 0.487 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 40.81 ns | 0.056 ns | 0.050 ns | 0.13 | - | - | - | - |
// | Shuffle4Slice3 | 3. AVX | Empty | 1024 | 39.95 ns | 0.075 ns | 0.067 ns | 0.12 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 101.37 ns | 0.080 ns | 0.067 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.72 ns | 0.049 ns | 0.041 ns | 0.40 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 101.37 ns | 0.080 ns | 0.067 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 40.72 ns | 0.049 ns | 0.041 ns | 0.40 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 1024 | 39.78 ns | 0.029 ns | 0.027 ns | 0.39 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 642.95 ns | 2.067 ns | 1.933 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 73.19 ns | 0.082 ns | 0.077 ns | 0.11 | - | - | - | - |
+// | Shuffle4Slice3 | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 642.95 ns | 2.067 ns | 1.933 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3 | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 73.19 ns | 0.082 ns | 0.077 ns | 0.11 | - | - | - | - |
// | Shuffle4Slice3 | 3. AVX | Empty | 2048 | 69.83 ns | 0.319 ns | 0.267 ns | 0.11 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 196.85 ns | 0.238 ns | 0.211 ns | 1.00 | - | - | - | - |
-// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 72.89 ns | 0.117 ns | 0.098 ns | 0.37 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 196.85 ns | 0.238 ns | 0.211 ns | 1.00 | - | - | - | - |
+// | Shuffle4Slice3FastFallback | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 72.89 ns | 0.117 ns | 0.098 ns | 0.37 | - | - | - | - |
// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 2048 | 69.59 ns | 0.073 ns | 0.061 ns | 0.35 | - | - | - | - |
diff --git a/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs
index 911c4e0a5..6a16bb571 100644
--- a/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs
+++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs
@@ -42,25 +42,25 @@ public class ShuffleByte4Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:|
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 17.39 ns | 0.187 ns | 0.175 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 128 | 21.72 ns | 0.299 ns | 0.279 ns | 1.25 | 0.02 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 128 | 18.10 ns | 0.346 ns | 0.289 ns | 1.04 | 0.02 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 35.51 ns | 0.711 ns | 0.790 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 256 | 23.90 ns | 0.508 ns | 0.820 ns | 0.69 | 0.02 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 256 | 20.40 ns | 0.133 ns | 0.111 ns | 0.57 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 73.39 ns | 0.310 ns | 0.259 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 512 | 26.10 ns | 0.418 ns | 0.391 ns | 0.36 | 0.01 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 512 | 27.59 ns | 0.556 ns | 0.571 ns | 0.38 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 150.64 ns | 2.903 ns | 2.716 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 1024 | 38.67 ns | 0.801 ns | 1.889 ns | 0.24 | 0.02 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 47.13 ns | 0.948 ns | 1.054 ns | 0.31 | 0.01 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 2048 | 57.37 ns | 1.152 ns | 1.078 ns | 0.18 | 0.01 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - |
// 2023-02-21
// ##########
@@ -77,22 +77,22 @@ public class ShuffleByte4Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:|
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 10.76 ns | 0.033 ns | 0.029 ns | 1.00 | 0.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.39 ns | 0.045 ns | 0.040 ns | 1.06 | 0.01 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 10.76 ns | 0.033 ns | 0.029 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 128 | 11.39 ns | 0.045 ns | 0.040 ns | 1.06 | 0.01 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 128 | 14.05 ns | 0.029 ns | 0.024 ns | 1.31 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 32.09 ns | 0.655 ns | 1.000 ns | 1.00 | 0.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 14.03 ns | 0.047 ns | 0.041 ns | 0.44 | 0.02 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 32.09 ns | 0.655 ns | 1.000 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 256 | 14.03 ns | 0.047 ns | 0.041 ns | 0.44 | 0.02 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 256 | 15.18 ns | 0.052 ns | 0.043 ns | 0.48 | 0.03 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 59.26 ns | 0.084 ns | 0.070 ns | 1.00 | 0.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 18.80 ns | 0.036 ns | 0.034 ns | 0.32 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 59.26 ns | 0.084 ns | 0.070 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 512 | 18.80 ns | 0.036 ns | 0.034 ns | 0.32 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 512 | 17.69 ns | 0.038 ns | 0.034 ns | 0.30 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 112.48 ns | 0.285 ns | 0.253 ns | 1.00 | 0.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 31.57 ns | 0.041 ns | 0.036 ns | 0.28 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 112.48 ns | 0.285 ns | 0.253 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 31.57 ns | 0.041 ns | 0.036 ns | 0.28 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 1024 | 28.41 ns | 0.068 ns | 0.064 ns | 0.25 | 0.00 | - | - | - | - |
// | | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 218.59 ns | 0.303 ns | 0.283 ns | 1.00 | 0.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 53.04 ns | 0.106 ns | 0.099 ns | 0.24 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 218.59 ns | 0.303 ns | 0.283 ns | 1.00 | 0.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 53.04 ns | 0.106 ns | 0.099 ns | 0.24 | 0.00 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 2048 | 34.74 ns | 0.061 ns | 0.054 ns | 0.16 | 0.00 | - | - | - | - |
diff --git a/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs
index 5bb3cf916..7cc894486 100644
--- a/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs
+++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs
@@ -42,25 +42,25 @@ public class ShuffleFloat4Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:|
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 63.647 ns | 0.5475 ns | 0.4853 ns | 1.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 128 | 9.818 ns | 0.1457 ns | 0.1292 ns | 0.15 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 128 | 15.267 ns | 0.1005 ns | 0.0940 ns | 0.24 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 125.586 ns | 1.9312 ns | 1.8064 ns | 1.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 256 | 15.878 ns | 0.1983 ns | 0.1758 ns | 0.13 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 256 | 29.170 ns | 0.2925 ns | 0.2442 ns | 0.23 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 263.859 ns | 2.6660 ns | 2.3634 ns | 1.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 512 | 29.452 ns | 0.3334 ns | 0.3118 ns | 0.11 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 512 | 52.912 ns | 0.1932 ns | 0.1713 ns | 0.20 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 495.717 ns | 1.9850 ns | 1.8567 ns | 1.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 1024 | 53.757 ns | 0.3212 ns | 0.2847 ns | 0.11 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 1024 | 107.815 ns | 1.6201 ns | 1.3528 ns | 0.22 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - |
// | Shuffle4Channel | 2. AVX | Empty | 2048 | 105.120 ns | 0.6140 ns | 0.5443 ns | 0.11 | - | - | - | - |
-// | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - |
+// | Shuffle4Channel | 3. SSE | DOTNET_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - |
// 2023-02-21
// ##########
@@ -77,22 +77,22 @@ public class ShuffleFloat4Channel
//
// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
// |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:|
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 57.819 ns | 0.2360 ns | 0.1970 ns | 1.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.564 ns | 0.0234 ns | 0.0195 ns | 0.20 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 128 | 57.819 ns | 0.2360 ns | 0.1970 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 128 | 11.564 ns | 0.0234 ns | 0.0195 ns | 0.20 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 128 | 7.770 ns | 0.0696 ns | 0.0617 ns | 0.13 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 105.282 ns | 0.2713 ns | 0.2405 ns | 1.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.867 ns | 0.0393 ns | 0.0348 ns | 0.19 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 256 | 105.282 ns | 0.2713 ns | 0.2405 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 256 | 19.867 ns | 0.0393 ns | 0.0348 ns | 0.19 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 256 | 17.586 ns | 0.0582 ns | 0.0544 ns | 0.17 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 200.799 ns | 0.5678 ns | 0.5033 ns | 1.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 41.137 ns | 0.1524 ns | 0.1351 ns | 0.20 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 512 | 200.799 ns | 0.5678 ns | 0.5033 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 512 | 41.137 ns | 0.1524 ns | 0.1351 ns | 0.20 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 512 | 24.040 ns | 0.0445 ns | 0.0395 ns | 0.12 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 401.046 ns | 0.5865 ns | 0.5199 ns | 1.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 94.904 ns | 0.4633 ns | 0.4334 ns | 0.24 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 1024 | 401.046 ns | 0.5865 ns | 0.5199 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 1024 | 94.904 ns | 0.4633 ns | 0.4334 ns | 0.24 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 1024 | 68.456 ns | 0.1192 ns | 0.0996 ns | 0.17 | - | - | - | - |
// | | | | | | | | | | | | |
-// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 772.297 ns | 0.6270 ns | 0.5558 ns | 1.00 | - | - | - | - |
-// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 184.561 ns | 0.4319 ns | 0.4040 ns | 0.24 | - | - | - | - |
+// | Shuffle4Channel | 1. No HwIntrinsics | DOTNET_EnableHWIntrinsic=0,DOTNET_FeatureSIMD=0 | 2048 | 772.297 ns | 0.6270 ns | 0.5558 ns | 1.00 | - | - | - | - |
+// | Shuffle4Channel | 2. SSE | DOTNET_EnableAVX=0 | 2048 | 184.561 ns | 0.4319 ns | 0.4040 ns | 0.24 | - | - | - | - |
// | Shuffle4Channel | 3. AVX | Empty | 2048 | 133.634 ns | 1.7864 ns | 1.8345 ns | 0.17 | - | - | - | - |
diff --git a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
index 92f8917cf..e21d0c76d 100644
--- a/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
+++ b/tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
@@ -33,24 +33,24 @@ public partial class Config
// `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things
// like `LZCNT`, `BMI1`, or `BMI2`
// `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3`
- private const string EnableAES = "COMPlus_EnableAES";
- private const string EnableAVX = "COMPlus_EnableAVX";
- private const string EnableAVX2 = "COMPlus_EnableAVX2";
- private const string EnableBMI1 = "COMPlus_EnableBMI1";
- private const string EnableBMI2 = "COMPlus_EnableBMI2";
- private const string EnableFMA = "COMPlus_EnableFMA";
- private const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic";
- private const string EnableLZCNT = "COMPlus_EnableLZCNT";
- private const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ";
- private const string EnablePOPCNT = "COMPlus_EnablePOPCNT";
- private const string EnableSSE = "COMPlus_EnableSSE";
- private const string EnableSSE2 = "COMPlus_EnableSSE2";
- private const string EnableSSE3 = "COMPlus_EnableSSE3";
- private const string EnableSSE3_4 = "COMPlus_EnableSSE3_4";
- private const string EnableSSE41 = "COMPlus_EnableSSE41";
- private const string EnableSSE42 = "COMPlus_EnableSSE42";
- private const string EnableSSSE3 = "COMPlus_EnableSSSE3";
- private const string FeatureSIMD = "COMPlus_FeatureSIMD";
+ private const string EnableAES = "DOTNET_EnableAES";
+ private const string EnableAVX = "DOTNET_EnableAVX";
+ private const string EnableAVX2 = "DOTNET_EnableAVX2";
+ private const string EnableBMI1 = "DOTNET_EnableBMI1";
+ private const string EnableBMI2 = "DOTNET_EnableBMI2";
+ private const string EnableFMA = "DOTNET_EnableFMA";
+ private const string EnableHWIntrinsic = "DOTNET_EnableHWIntrinsic";
+ private const string EnableLZCNT = "DOTNET_EnableLZCNT";
+ private const string EnablePCLMULQDQ = "DOTNET_EnablePCLMULQDQ";
+ private const string EnablePOPCNT = "DOTNET_EnablePOPCNT";
+ private const string EnableSSE = "DOTNET_EnableSSE";
+ private const string EnableSSE2 = "DOTNET_EnableSSE2";
+ private const string EnableSSE3 = "DOTNET_EnableSSE3";
+ private const string EnableSSE3_4 = "DOTNET_EnableSSE3_4";
+ private const string EnableSSE41 = "DOTNET_EnableSSE41";
+ private const string EnableSSE42 = "DOTNET_EnableSSE42";
+ private const string EnableSSSE3 = "DOTNET_EnableSSSE3";
+ private const string FeatureSIMD = "DOTNET_FeatureSIMD";
public class HwIntrinsics_SSE_AVX : Config
{
diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
index 4408159ef..a705b24b2 100644
--- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
+++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
@@ -23,7 +23,7 @@
- net8.0
+ net8.0;net9.0
diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
index b93d01191..832f3d171 100644
--- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
+++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
@@ -19,7 +19,7 @@
- net8.0
+ net8.0;net9.0
diff --git a/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs
new file mode 100644
index 000000000..95b8d2013
--- /dev/null
+++ b/tests/ImageSharp.Tests/Common/GaussianEliminationSolverTest.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Processing.Processors.Transforms.Linear;
+
+namespace SixLabors.ImageSharp.Tests.Common;
+
+public class GaussianEliminationSolverTest
+{
+ [Theory]
+ [MemberData(nameof(MatrixTestData))]
+ public void CanSolve(double[][] matrix, double[] result, double[] expected)
+ {
+ GaussianEliminationSolver.Solve(matrix, result);
+
+ for (int i = 0; i < expected.Length; i++)
+ {
+ Assert.Equal(result[i], expected[i], 4);
+ }
+ }
+
+ public static TheoryData MatrixTestData
+ {
+ get
+ {
+ TheoryData data = [];
+ {
+ double[][] matrix =
+ [
+ [2, 3, 4],
+ [1, 2, 3],
+ [3, -4, 0],
+ ];
+ double[] result = [6, 4, 10];
+ double[] expected = [18 / 11f, -14 / 11f, 18 / 11f];
+ data.Add(matrix, result, expected);
+ }
+
+ {
+ double[][] matrix =
+ [
+ [1, 4, -1],
+ [2, 5, 8],
+ [1, 3, -3],
+ ];
+ double[] result = [4, 15, 1];
+ double[] expected = [1, 1, 1];
+ data.Add(matrix, result, expected);
+ }
+
+ {
+ double[][] matrix =
+ [
+ [-1, 0, 0],
+ [0, 1, 0],
+ [0, 0, 1],
+ ];
+ double[] result = [1, 2, 3];
+ double[] expected = [-1, 2, 3];
+ data.Add(matrix, result, expected);
+ }
+
+ return data;
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 8b4aa3d70..09cfe2cbe 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -719,7 +719,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
// ImageMagick cannot decode this image.
image.DebugSave(provider);
image.CompareToReferenceOutput(
- ImageComparer.Exact,
+ ImageComparer.TolerantPercentage(0.0018F), // NET 9+ Uses zlib-ng to decompress, which manages to decode 2 extra pixels.
provider,
appendPixelTypeToFileName: false);
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
index 10492af8a..11b1749ab 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
@@ -516,6 +516,21 @@ public class WebpEncoderTests
image.VerifyEncoder(provider, "webp", string.Empty, encoder);
}
+ // https://github.com/SixLabors/ImageSharp/issues/2801
+ [Theory]
+ [WithFile(Lossy.Issue2801, PixelTypes.Rgba32)]
+ public void WebpDecoder_CanDecode_Issue2801(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ WebpEncoder encoder = new()
+ {
+ Quality = 100
+ };
+
+ using Image image = provider.GetImage();
+ image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.TolerantPercentage(0.0994F));
+ }
+
public static void RunEncodeLossy_WithPeakImage()
{
TestImageProvider provider = TestImageProvider.File(TestImageLossyFullPath);
@@ -531,6 +546,44 @@ public class WebpEncoderTests
[Fact]
public void RunEncodeLossy_WithPeakImage_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.DisableHWIntrinsic);
+ [Theory]
+ [WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
+ public void CanSave_NonSeekableStream(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage();
+ WebpEncoder encoder = new();
+
+ using MemoryStream seekable = new();
+ image.Save(seekable, encoder);
+
+ using MemoryStream memoryStream = new();
+ using NonSeekableStream nonSeekable = new(memoryStream);
+
+ image.Save(nonSeekable, encoder);
+
+ Assert.True(seekable.ToArray().SequenceEqual(memoryStream.ToArray()));
+ }
+
+ [Theory]
+ [WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
+ public async Task CanSave_NonSeekableStream_Async(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage();
+ WebpEncoder encoder = new();
+
+ await using MemoryStream seekable = new();
+ image.Save(seekable, encoder);
+
+ await using MemoryStream memoryStream = new();
+ await using NonSeekableStream nonSeekable = new(memoryStream);
+
+ await image.SaveAsync(nonSeekable, encoder);
+
+ Assert.True(seekable.ToArray().SequenceEqual(memoryStream.ToArray()));
+ }
+
private static ImageComparer GetComparer(int quality)
{
float tolerance = 0.01f; // ~1.0%
diff --git a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
index 1803cfddb..390170cfe 100644
--- a/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
+++ b/tests/ImageSharp.Tests/IO/ChunkedMemoryStreamTests.cs
@@ -13,6 +13,8 @@ namespace SixLabors.ImageSharp.Tests.IO;
///
public class ChunkedMemoryStreamTests
{
+ private readonly Random bufferFiller = new(123);
+
///
/// The default length in bytes of each buffer chunk when allocating large buffers.
///
@@ -30,7 +32,7 @@ public class ChunkedMemoryStreamTests
[Fact]
public void MemoryStream_GetPositionTest_Negative()
{
- using var ms = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream ms = new(this.allocator);
long iCurrentPos = ms.Position;
for (int i = -1; i > -6; i--)
{
@@ -42,7 +44,7 @@ public class ChunkedMemoryStreamTests
[Fact]
public void MemoryStream_ReadTest_Negative()
{
- var ms2 = new ChunkedMemoryStream(this.allocator);
+ ChunkedMemoryStream ms2 = new(this.allocator);
Assert.Throws(() => ms2.Read(null, 0, 0));
Assert.Throws(() => ms2.Read(new byte[] { 1 }, -1, 0));
@@ -64,7 +66,7 @@ public class ChunkedMemoryStreamTests
public void MemoryStream_ReadByteTest(int length)
{
using MemoryStream ms = this.CreateTestStream(length);
- using var cms = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream cms = new(this.allocator);
ms.CopyTo(cms);
cms.Position = 0;
@@ -85,7 +87,7 @@ public class ChunkedMemoryStreamTests
public void MemoryStream_ReadByteBufferTest(int length)
{
using MemoryStream ms = this.CreateTestStream(length);
- using var cms = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream cms = new(this.allocator);
ms.CopyTo(cms);
cms.Position = 0;
@@ -105,10 +107,11 @@ public class ChunkedMemoryStreamTests
[InlineData(DefaultSmallChunkSize * 4)]
[InlineData((int)(DefaultSmallChunkSize * 5.5))]
[InlineData(DefaultSmallChunkSize * 16)]
+ [InlineData(DefaultSmallChunkSize * 32)]
public void MemoryStream_ReadByteBufferSpanTest(int length)
{
using MemoryStream ms = this.CreateTestStream(length);
- using var cms = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream cms = new(this.allocator);
ms.CopyTo(cms);
cms.Position = 0;
@@ -122,18 +125,24 @@ public class ChunkedMemoryStreamTests
}
}
- [Fact]
- public void MemoryStream_WriteToTests()
+ [Theory]
+ [InlineData(DefaultSmallChunkSize)]
+ [InlineData((int)(DefaultSmallChunkSize * 1.5))]
+ [InlineData(DefaultSmallChunkSize * 4)]
+ [InlineData((int)(DefaultSmallChunkSize * 5.5))]
+ [InlineData(DefaultSmallChunkSize * 16)]
+ [InlineData(DefaultSmallChunkSize * 32)]
+ public void MemoryStream_WriteToTests(int length)
{
- using (var ms2 = new ChunkedMemoryStream(this.allocator))
+ using (ChunkedMemoryStream ms2 = new(this.allocator))
{
byte[] bytArrRet;
- byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
+ byte[] bytArr = this.CreateTestBuffer(length);
// [] Write to memoryStream, check the memoryStream
ms2.Write(bytArr, 0, bytArr.Length);
- using var readonlyStream = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream readonlyStream = new(this.allocator);
ms2.WriteTo(readonlyStream);
readonlyStream.Flush();
readonlyStream.Position = 0;
@@ -146,11 +155,11 @@ public class ChunkedMemoryStreamTests
}
// [] Write to memoryStream, check the memoryStream
- using (var ms2 = new ChunkedMemoryStream(this.allocator))
- using (var ms3 = new ChunkedMemoryStream(this.allocator))
+ using (ChunkedMemoryStream ms2 = new(this.allocator))
+ using (ChunkedMemoryStream ms3 = new(this.allocator))
{
byte[] bytArrRet;
- byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
+ byte[] bytArr = this.CreateTestBuffer(length);
ms2.Write(bytArr, 0, bytArr.Length);
ms2.WriteTo(ms3);
@@ -164,21 +173,29 @@ public class ChunkedMemoryStreamTests
}
}
- [Fact]
- public void MemoryStream_WriteToSpanTests()
+ [Theory]
+ [InlineData(DefaultSmallChunkSize)]
+ [InlineData((int)(DefaultSmallChunkSize * 1.5))]
+ [InlineData(DefaultSmallChunkSize * 4)]
+ [InlineData((int)(DefaultSmallChunkSize * 5.5))]
+ [InlineData(DefaultSmallChunkSize * 16)]
+ [InlineData(DefaultSmallChunkSize * 32)]
+ public void MemoryStream_WriteToSpanTests(int length)
{
- using (var ms2 = new ChunkedMemoryStream(this.allocator))
+ using (ChunkedMemoryStream ms2 = new(this.allocator))
{
Span bytArrRet;
- Span bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
+ Span bytArr = this.CreateTestBuffer(length);
// [] Write to memoryStream, check the memoryStream
ms2.Write(bytArr, 0, bytArr.Length);
- using var readonlyStream = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream readonlyStream = new(this.allocator);
ms2.WriteTo(readonlyStream);
+
readonlyStream.Flush();
readonlyStream.Position = 0;
+
bytArrRet = new byte[(int)readonlyStream.Length];
readonlyStream.Read(bytArrRet, 0, (int)readonlyStream.Length);
for (int i = 0; i < bytArr.Length; i++)
@@ -188,13 +205,14 @@ public class ChunkedMemoryStreamTests
}
// [] Write to memoryStream, check the memoryStream
- using (var ms2 = new ChunkedMemoryStream(this.allocator))
- using (var ms3 = new ChunkedMemoryStream(this.allocator))
+ using (ChunkedMemoryStream ms2 = new(this.allocator))
+ using (ChunkedMemoryStream ms3 = new(this.allocator))
{
Span bytArrRet;
- Span bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
+ Span bytArr = this.CreateTestBuffer(length);
ms2.Write(bytArr, 0, bytArr.Length);
+
ms2.WriteTo(ms3);
ms3.Position = 0;
bytArrRet = new byte[(int)ms3.Length];
@@ -209,37 +227,35 @@ public class ChunkedMemoryStreamTests
[Fact]
public void MemoryStream_WriteByteTests()
{
- using (var ms2 = new ChunkedMemoryStream(this.allocator))
- {
- byte[] bytArrRet;
- byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
+ using ChunkedMemoryStream ms2 = new(this.allocator);
+ byte[] bytArrRet;
+ byte[] bytArr = new byte[] { byte.MinValue, byte.MaxValue, 1, 2, 3, 4, 5, 6, 128, 250 };
- for (int i = 0; i < bytArr.Length; i++)
- {
- ms2.WriteByte(bytArr[i]);
- }
+ for (int i = 0; i < bytArr.Length; i++)
+ {
+ ms2.WriteByte(bytArr[i]);
+ }
- using var readonlyStream = new ChunkedMemoryStream(this.allocator);
- ms2.WriteTo(readonlyStream);
- readonlyStream.Flush();
- readonlyStream.Position = 0;
- bytArrRet = new byte[(int)readonlyStream.Length];
- readonlyStream.Read(bytArrRet, 0, (int)readonlyStream.Length);
- for (int i = 0; i < bytArr.Length; i++)
- {
- Assert.Equal(bytArr[i], bytArrRet[i]);
- }
+ using ChunkedMemoryStream readonlyStream = new(this.allocator);
+ ms2.WriteTo(readonlyStream);
+ readonlyStream.Flush();
+ readonlyStream.Position = 0;
+ bytArrRet = new byte[(int)readonlyStream.Length];
+ readonlyStream.Read(bytArrRet, 0, (int)readonlyStream.Length);
+ for (int i = 0; i < bytArr.Length; i++)
+ {
+ Assert.Equal(bytArr[i], bytArrRet[i]);
}
}
[Fact]
public void MemoryStream_WriteToTests_Negative()
{
- using var ms2 = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream ms2 = new(this.allocator);
Assert.Throws(() => ms2.WriteTo(null));
ms2.Write(new byte[] { 1 }, 0, 1);
- var readonlyStream = new MemoryStream(new byte[1028], false);
+ MemoryStream readonlyStream = new(new byte[1028], false);
Assert.Throws(() => ms2.WriteTo(readonlyStream));
readonlyStream.Dispose();
@@ -286,7 +302,7 @@ public class ChunkedMemoryStreamTests
[MemberData(nameof(CopyToData))]
public void CopyTo(Stream source, byte[] expected)
{
- using var destination = new ChunkedMemoryStream(this.allocator);
+ using ChunkedMemoryStream destination = new(this.allocator);
source.CopyTo(destination);
Assert.InRange(source.Position, source.Length, int.MaxValue); // Copying the data should have read to the end of the stream or stayed past the end.
Assert.Equal(expected, destination.ToArray());
@@ -297,16 +313,16 @@ public class ChunkedMemoryStreamTests
IEnumerable allImageFiles = Directory.EnumerateFiles(TestEnvironment.InputImagesDirectoryFullPath, "*.*", SearchOption.AllDirectories)
.Where(s => !s.EndsWith("txt", StringComparison.OrdinalIgnoreCase));
- var result = new List();
+ List result = new();
foreach (string path in allImageFiles)
{
- result.Add(path.Substring(TestEnvironment.InputImagesDirectoryFullPath.Length));
+ result.Add(path[TestEnvironment.InputImagesDirectoryFullPath.Length..]);
}
return result;
}
- public static IEnumerable AllTestImages = GetAllTestImages();
+ public static IEnumerable AllTestImages { get; } = GetAllTestImages();
[Theory]
[WithFileCollection(nameof(AllTestImages), PixelTypes.Rgba32)]
@@ -334,40 +350,77 @@ public class ChunkedMemoryStreamTests
((TestImageProvider.FileProvider)provider).FilePath);
using FileStream fs = File.OpenRead(fullPath);
- using var nonSeekableStream = new NonSeekableStream(fs);
+ using NonSeekableStream nonSeekableStream = new(fs);
+
+ using Image actual = Image.Load(nonSeekableStream);
+
+ ImageComparer.Exact.VerifySimilarity(expected, actual);
+ expected.Dispose();
+ }
+
+ [Theory]
+ [WithFileCollection(nameof(AllTestImages), PixelTypes.Rgba32)]
+ public void EncoderIntegrationTest(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (!TestEnvironment.Is64BitProcess)
+ {
+ return;
+ }
+
+ Image expected;
+ try
+ {
+ expected = provider.GetImage();
+ }
+ catch
+ {
+ // The image is invalid
+ return;
+ }
- var actual = Image.Load(nonSeekableStream);
+ string fullPath = Path.Combine(
+ TestEnvironment.InputImagesDirectoryFullPath,
+ ((TestImageProvider.FileProvider)provider).FilePath);
+
+ using MemoryStream ms = new();
+ using NonSeekableStream nonSeekableStream = new(ms);
+ expected.SaveAsWebp(nonSeekableStream);
+
+ using Image actual = Image.Load(nonSeekableStream);
ImageComparer.Exact.VerifySimilarity(expected, actual);
+ expected.Dispose();
}
public static IEnumerable