diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs
index d7162bc61..f609b15d3 100644
--- a/src/ImageSharp/Advanced/IPixelSource.cs
+++ b/src/ImageSharp/Advanced/IPixelSource.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
@@ -6,6 +6,17 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Advanced
{
+ ///
+ /// Encapsulates the basic properties and methods required to manipulate images.
+ ///
+ internal interface IPixelSource
+ {
+ ///
+ /// Gets the pixel buffer.
+ ///
+ Buffer2D PixelBuffer { get; }
+ }
+
///
/// Encapsulates the basic properties and methods required to manipulate images.
///
@@ -18,4 +29,4 @@ namespace SixLabors.ImageSharp.Advanced
///
Buffer2D PixelBuffer { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
index 3c8570a2a..1fe2a24c7 100644
--- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp
///
/// Restricts a to be within a specified range.
///
- /// The The value to clamp.
+ /// The value to clamp.
/// The minimum value. If value is less than min, min will be returned.
/// The maximum value. If value is greater than max, max will be returned.
///
@@ -137,4 +137,4 @@ namespace SixLabors.ImageSharp
return value;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 02267de1a..7919bc148 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -353,7 +353,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadImageDescriptor();
IManagedByteBuffer localColorTable = null;
- IManagedByteBuffer indices = null;
+ Buffer2D indices = null;
try
{
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
@@ -364,11 +364,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.stream.Read(localColorTable.Array, 0, length);
}
- indices = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.imageDescriptor.Width * this.imageDescriptor.Height, AllocationOptions.Clean);
+ indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
- this.ReadFrameIndices(this.imageDescriptor, indices.GetSpan());
+ this.ReadFrameIndices(indices);
ReadOnlySpan colorTable = MemoryMarshal.Cast((localColorTable ?? this.globalColorTable).GetSpan());
- this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
+ this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
// Skip any remaining blocks
this.SkipBlock();
@@ -383,16 +383,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Reads the frame indices marking the color to use for each pixel.
///
- /// The .
- /// The pixel array to write to.
+ /// The 2D pixel buffer to write to.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span indices)
+ private void ReadFrameIndices(Buffer2D indices)
{
int dataSize = this.stream.ReadByte();
- using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream))
- {
- lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
- }
+ using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream);
+ lzwDecoder.DecodePixels(dataSize, indices);
}
///
@@ -404,10 +401,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The indexed pixels.
/// The color table containing the available colors.
/// The
- private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Span indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor)
+ private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Buffer2D indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor)
where TPixel : unmanaged, IPixel
{
- ref byte indicesRef = ref MemoryMarshal.GetReference(indices);
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
@@ -440,13 +436,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.RestoreToBackground(imageFrame);
}
- int i = 0;
int interlacePass = 0; // The interlace pass
int interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line
-
- for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
+ int descriptorTop = descriptor.Top;
+ int descriptorBottom = descriptorTop + descriptor.Height;
+ int descriptorLeft = descriptor.Left;
+ int descriptorRight = descriptorLeft + descriptor.Width;
+ bool transFlag = this.graphicsControlExtension.TransparencyFlag;
+ byte transIndex = this.graphicsControlExtension.TransparencyIndex;
+
+ for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++)
{
+ ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.GetRowSpan(y - descriptorTop));
+
// Check if this image is interlaced.
int writeY; // the target y offset to write to
if (descriptor.InterlaceFlag)
@@ -482,35 +485,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
- bool transFlag = this.graphicsControlExtension.TransparencyFlag;
if (!transFlag)
{
// #403 The left + width value can be larger than the image width
- for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
+ for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++)
{
- int index = Unsafe.Add(ref indicesRef, i);
+ int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft);
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
Rgb24 rgb = colorTable[index];
pixel.FromRgb24(rgb);
-
- i++;
}
}
else
{
- byte transIndex = this.graphicsControlExtension.TransparencyIndex;
- for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
+ for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++)
{
- int index = Unsafe.Add(ref indicesRef, i);
+ int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft);
if (transIndex != index)
{
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
Rgb24 rgb = colorTable[index];
pixel.FromRgb24(rgb);
}
-
- i++;
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 887540930..62410025c 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -6,6 +6,7 @@ using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -471,7 +472,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
where TPixel : unmanaged, IPixel
{
using var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth);
- encoder.Encode(image.GetPixelBufferSpan(), stream);
+ encoder.Encode(((IPixelSource)image).PixelBuffer, stream);
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
index 0129db0e3..8289ee75b 100644
--- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
@@ -65,15 +65,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Decodes and decompresses all pixel indices from the stream.
///
- /// The width of the pixel index array.
- /// The height of the pixel index array.
/// Size of the data.
/// The pixel array to decode to.
- public void DecodePixels(int width, int height, int dataSize, Span pixels)
+ public void DecodePixels(int dataSize, Buffer2D pixels)
{
Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
// The resulting index table length.
+ int width = pixels.Width;
+ int height = pixels.Height;
int length = width * height;
// Calculate the clear code. The value of the clear code is 2 ^ dataSize
@@ -105,17 +105,28 @@ namespace SixLabors.ImageSharp.Formats.Gif
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan());
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan());
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan());
- ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels);
for (code = 0; code < clearCode; code++)
{
Unsafe.Add(ref suffixRef, code) = (byte)code;
}
- Span buffer = stackalloc byte[255];
+ Span buffer = stackalloc byte[byte.MaxValue];
+ int y = 0;
+ int x = 0;
+ int rowMax = width;
+ ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(y));
while (xyz < length)
{
+ // Reset row reference.
+ if (xyz == rowMax)
+ {
+ x = 0;
+ pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(++y));
+ rowMax = (y * width) + width;
+ }
+
if (top == 0)
{
if (bits < codeSize)
@@ -209,7 +220,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
- Unsafe.Add(ref pixelsRef, xyz++) = (byte)Unsafe.Add(ref pixelStackRef, top);
+ xyz++;
+ Unsafe.Add(ref pixelsRowRef, x++) = (byte)Unsafe.Add(ref pixelStackRef, top);
}
}
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index 056076bf0..516b82396 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -41,13 +41,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private const int HashSize = 5003;
+ ///
+ /// The amount to shift each code.
+ ///
+ private const int HashShift = 4;
+
///
/// Mask used when shifting pixel values
///
private static readonly int[] Masks =
{
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF,
- 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
+ 0b0,
+ 0b1,
+ 0b11,
+ 0b111,
+ 0b1111,
+ 0b11111,
+ 0b111111,
+ 0b1111111,
+ 0b11111111,
+ 0b111111111,
+ 0b1111111111,
+ 0b11111111111,
+ 0b111111111111,
+ 0b1111111111111,
+ 0b11111111111111,
+ 0b111111111111111,
+ 0b1111111111111111
};
///
@@ -80,16 +100,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private readonly byte[] accumulators = new byte[256];
- ///
- /// For dynamic table sizing
- ///
- private readonly int hsize = HashSize;
-
- ///
- /// The current position within the pixelArray.
- ///
- private int position;
-
///
/// Number of bits/code
///
@@ -177,15 +187,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Encodes and compresses the indexed pixels to the stream.
///
- /// The span of indexed pixels.
+ /// The 2D buffer of indexed pixels.
/// The stream to write to.
- public void Encode(ReadOnlySpan indexedPixels, Stream stream)
+ public void Encode(Buffer2D indexedPixels, Stream stream)
{
// Write "initial code size" byte
stream.WriteByte((byte)this.initialCodeSize);
- this.position = 0;
-
// Compress and write the pixel data
this.Compress(indexedPixels, this.initialCodeSize + 1, stream);
@@ -199,10 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The number of bits
/// See
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetMaxcode(int bitCount)
- {
- return (1 << bitCount) - 1;
- }
+ private static int GetMaxcode(int bitCount) => (1 << bitCount) - 1;
///
/// Add a character to the end of the current packet, and if it is 254 characters,
@@ -239,25 +244,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Reset the code table.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ResetCodeTable()
- {
- this.hashTable.GetSpan().Fill(-1);
- }
+ private void ResetCodeTable() => this.hashTable.GetSpan().Fill(-1);
///
/// Compress the packets to the stream.
///
- /// The span of indexed pixels.
+ /// The 2D buffer of indexed pixels.
/// The initial bits.
/// The stream to write to.
- private void Compress(ReadOnlySpan indexedPixels, int initialBits, Stream stream)
+ private void Compress(Buffer2D indexedPixels, int initialBits, Stream stream)
{
- int fcode;
- int c;
- int ent;
- int hsizeReg;
- int hshift;
-
// Set up the globals: globalInitialBits - initial number of bits
this.globalInitialBits = initialBits;
@@ -265,92 +261,82 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.clearFlag = false;
this.bitCount = this.globalInitialBits;
this.maxCode = GetMaxcode(this.bitCount);
-
this.clearCode = 1 << (initialBits - 1);
this.eofCode = this.clearCode + 1;
this.freeEntry = this.clearCode + 2;
+ this.accumulatorCount = 0; // Clear packet
- this.accumulatorCount = 0; // clear packet
-
- ent = this.NextPixel(indexedPixels);
-
- // TODO: PERF: It looks like hshift could be calculated once statically.
- hshift = 0;
- for (fcode = this.hsize; fcode < 65536; fcode *= 2)
- {
- ++hshift;
- }
-
- hshift = 8 - hshift; // set hash code range bound
-
- hsizeReg = this.hsize;
-
- this.ResetCodeTable(); // clear hash table
-
+ this.ResetCodeTable(); // Clear hash table
this.Output(this.clearCode, stream);
ref int hashTableRef = ref MemoryMarshal.GetReference(this.hashTable.GetSpan());
ref int codeTableRef = ref MemoryMarshal.GetReference(this.codeTable.GetSpan());
- while (this.position < indexedPixels.Length)
- {
- c = this.NextPixel(indexedPixels);
+ int entry = indexedPixels[0, 0];
- fcode = (c << MaxBits) + ent;
- int i = (c << hshift) ^ ent /* = 0 */;
+ for (int y = 0; y < indexedPixels.Height; y++)
+ {
+ ref byte rowSpanRef = ref MemoryMarshal.GetReference(indexedPixels.GetRowSpan(y));
+ int offsetX = y == 0 ? 1 : 0;
- if (Unsafe.Add(ref hashTableRef, i) == fcode)
+ for (int x = offsetX; x < indexedPixels.Width; x++)
{
- ent = Unsafe.Add(ref codeTableRef, i);
- continue;
- }
+ int code = Unsafe.Add(ref rowSpanRef, x);
+ int freeCode = (code << MaxBits) + entry;
+ int hashIndex = (code << HashShift) ^ entry;
- // Non-empty slot
- if (Unsafe.Add(ref hashTableRef, i) >= 0)
- {
- int disp = 1;
- if (i != 0)
+ if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode)
{
- disp = hsizeReg - i;
+ entry = Unsafe.Add(ref codeTableRef, hashIndex);
+ continue;
}
- do
+ // Non-empty slot
+ if (Unsafe.Add(ref hashTableRef, hashIndex) >= 0)
{
- if ((i -= disp) < 0)
+ int disp = 1;
+ if (hashIndex != 0)
+ {
+ disp = HashSize - hashIndex;
+ }
+
+ do
{
- i += hsizeReg;
+ if ((hashIndex -= disp) < 0)
+ {
+ hashIndex += HashSize;
+ }
+
+ if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode)
+ {
+ entry = Unsafe.Add(ref codeTableRef, hashIndex);
+ break;
+ }
}
+ while (Unsafe.Add(ref hashTableRef, hashIndex) >= 0);
- if (Unsafe.Add(ref hashTableRef, i) == fcode)
+ if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode)
{
- ent = Unsafe.Add(ref codeTableRef, i);
- break;
+ continue;
}
}
- while (Unsafe.Add(ref hashTableRef, i) >= 0);
- if (Unsafe.Add(ref hashTableRef, i) == fcode)
+ this.Output(entry, stream);
+ entry = code;
+ if (this.freeEntry < MaxMaxCode)
{
- continue;
+ Unsafe.Add(ref codeTableRef, hashIndex) = this.freeEntry++; // code -> hashtable
+ Unsafe.Add(ref hashTableRef, hashIndex) = freeCode;
+ }
+ else
+ {
+ this.ClearBlock(stream);
}
- }
-
- this.Output(ent, stream);
- ent = c;
- if (this.freeEntry < MaxMaxCode)
- {
- Unsafe.Add(ref codeTableRef, i) = this.freeEntry++; // code -> hashtable
- Unsafe.Add(ref hashTableRef, i) = fcode;
- }
- else
- {
- this.ClearBlock(stream);
}
}
- // Put out the final code.
- this.Output(ent, stream);
-
+ // Output the final code.
+ this.Output(entry, stream);
this.Output(this.eofCode, stream);
}
@@ -366,19 +352,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.accumulatorCount = 0;
}
- ///
- /// Reads the next pixel from the image.
- ///
- /// The span of indexed pixels.
- ///
- /// The
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int NextPixel(ReadOnlySpan indexedPixels)
- {
- return indexedPixels[this.position++] & 0xFF;
- }
-
///
/// Output the current code to the stream.
///
diff --git a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs
index ac737f452..c271ce742 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/IndexedImageFrame{TPixel}.cs
@@ -4,6 +4,7 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -13,10 +14,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// A pixel-specific image frame where each pixel buffer value represents an index in a color palette.
///
/// The pixel format.
- public sealed class IndexedImageFrame : IDisposable
+ public sealed class IndexedImageFrame : IPixelSource, IDisposable
where TPixel : unmanaged, IPixel
{
- private IMemoryOwner pixelsOwner;
+ private Buffer2D pixelBuffer;
private IMemoryOwner paletteOwner;
private bool isDisposed;
@@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.Configuration = configuration;
this.Width = width;
this.Height = height;
- this.pixelsOwner = configuration.MemoryAllocator.AllocateManagedByteBuffer(width * height);
+ this.pixelBuffer = configuration.MemoryAllocator.Allocate2D(width, height);
// Copy the palette over. We want the lifetime of this frame to be independant of any palette source.
this.paletteOwner = configuration.MemoryAllocator.Allocate(palette.Length);
@@ -67,16 +68,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
public ReadOnlyMemory Palette { get; }
- ///
- /// Gets the pixels of this .
- ///
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public ReadOnlySpan GetPixelBufferSpan() => this.pixelsOwner.GetSpan(); // TODO: Buffer2D
+ ///
+ Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer;
///
/// Gets the representation of the pixels as a of contiguous memory
- /// at row beginning from the the first pixel on that row.
+ /// at row beginning from the first pixel on that row.
///
/// The row index in the pixel buffer.
/// The pixel row as a .
@@ -87,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
///
///
/// Gets the representation of the pixels as a of contiguous memory
- /// at row beginning from the the first pixel on that row.
+ /// at row beginning from the first pixel on that row.
///
///
/// Note: Values written to this span are not sanitized against the palette length.
@@ -98,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// The pixel row as a .
[MethodImpl(InliningOptions.ShortMethod)]
public Span GetWritablePixelRowSpanUnsafe(int rowIndex)
- => this.pixelsOwner.GetSpan().Slice(rowIndex * this.Width, this.Width);
+ => this.pixelBuffer.GetRowSpan(rowIndex);
///
public void Dispose()
@@ -106,9 +103,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (!this.isDisposed)
{
this.isDisposed = true;
- this.pixelsOwner.Dispose();
+ this.pixelBuffer.Dispose();
this.paletteOwner.Dispose();
- this.pixelsOwner = null;
+ this.pixelBuffer = null;
this.paletteOwner = null;
}
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
index 4583b7cff..386caf1be 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
@@ -68,21 +68,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
- ReadOnlySpan quantizedPixelSpan = this.quantized.GetPixelBufferSpan();
ReadOnlySpan paletteSpan = this.quantized.Palette.Span;
int offsetY = this.bounds.Top;
int offsetX = this.bounds.Left;
- int width = this.bounds.Width;
for (int y = rows.Min; y < rows.Max; y++)
{
Span row = this.source.GetPixelRowSpan(y);
- int rowStart = (y - offsetY) * width;
+ ReadOnlySpan quantizedRow = this.quantized.GetPixelRowSpan(y - offsetY);
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
- int i = rowStart + x - offsetX;
- row[x] = paletteSpan[quantizedPixelSpan[i]];
+ row[x] = paletteSpan[quantizedRow[x - offsetX]];
}
}
}
diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
index 7e4eced8f..7945741b0 100644
--- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
+++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
@@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests
using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds()))
{
int index = this.GetTransparentIndex(quantized);
- Assert.Equal(index, quantized.GetPixelBufferSpan()[0]);
+ Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]);
}
}
}
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests
using (IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds()))
{
int index = this.GetTransparentIndex(quantized);
- Assert.Equal(index, quantized.GetPixelBufferSpan()[0]);
+ Assert.Equal(index, quantized.GetPixelRowSpan(0)[0]);
}
}
}
diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
index 2a0a02d94..37b8cab60 100644
--- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
+++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs
@@ -24,10 +24,11 @@ namespace SixLabors.ImageSharp.Tests.Quantization
using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
Assert.Equal(1, result.Palette.Length);
- Assert.Equal(1, result.GetPixelBufferSpan().Length);
+ Assert.Equal(1, result.Width);
+ Assert.Equal(1, result.Height);
Assert.Equal(Color.Black, (Color)result.Palette.Span[0]);
- Assert.Equal(0, result.GetPixelBufferSpan()[0]);
+ Assert.Equal(0, result.GetPixelRowSpan(0)[0]);
}
[Fact]
@@ -43,10 +44,11 @@ namespace SixLabors.ImageSharp.Tests.Quantization
using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
Assert.Equal(1, result.Palette.Length);
- Assert.Equal(1, result.GetPixelBufferSpan().Length);
+ Assert.Equal(1, result.Width);
+ Assert.Equal(1, result.Height);
Assert.Equal(default, result.Palette.Span[0]);
- Assert.Equal(0, result.GetPixelBufferSpan()[0]);
+ Assert.Equal(0, result.GetPixelRowSpan(0)[0]);
}
[Fact]
@@ -88,7 +90,8 @@ namespace SixLabors.ImageSharp.Tests.Quantization
using IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
Assert.Equal(256, result.Palette.Length);
- Assert.Equal(256, result.GetPixelBufferSpan().Length);
+ Assert.Equal(1, result.Width);
+ Assert.Equal(256, result.Height);
var actualImage = new Image(1, 256);
@@ -97,17 +100,18 @@ namespace SixLabors.ImageSharp.Tests.Quantization
for (int y = 0; y < actualImage.Height; y++)
{
Span row = actualImage.GetPixelRowSpan(y);
- ReadOnlySpan quantizedPixelSpan = result.GetPixelBufferSpan();
- int yy = y * actualImage.Width;
+ ReadOnlySpan quantizedPixelSpan = result.GetPixelRowSpan(y);
for (int x = 0; x < actualImage.Width; x++)
{
- int i = x + yy;
- row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])];
+ row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
}
}
- Assert.True(image.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan()));
+ for (int y = 0; y < image.Height; y++)
+ {
+ Assert.True(image.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y)));
+ }
}
[Theory]
@@ -155,25 +159,27 @@ namespace SixLabors.ImageSharp.Tests.Quantization
using (IndexedImageFrame result = frameQuantizer.QuantizeFrame(frame, frame.Bounds()))
{
Assert.Equal(4 * 8, result.Palette.Length);
- Assert.Equal(256, result.GetPixelBufferSpan().Length);
+ Assert.Equal(1, result.Width);
+ Assert.Equal(256, result.Height);
ReadOnlySpan paletteSpan = result.Palette.Span;
int paletteCount = paletteSpan.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
{
Span row = actualImage.GetPixelRowSpan(y);
- ReadOnlySpan quantizedPixelSpan = result.GetPixelBufferSpan();
- int yy = y * actualImage.Width;
+ ReadOnlySpan quantizedPixelSpan = result.GetPixelRowSpan(y);
for (int x = 0; x < actualImage.Width; x++)
{
- int i = x + yy;
- row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])];
+ row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])];
}
}
}
- Assert.True(expectedImage.GetPixelSpan().SequenceEqual(actualImage.GetPixelSpan()));
+ for (int y = 0; y < expectedImage.Height; y++)
+ {
+ Assert.True(expectedImage.GetPixelRowSpan(y).SequenceEqual(actualImage.GetPixelRowSpan(y)));
+ }
}
}
}