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))); + } } } }