diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index fb1d33277..175a9f777 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -34,7 +34,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable private readonly MemoryAllocator memoryAllocator; /// - /// The configuration instance for the decoding operation. + /// The configuration instance for the encoding operation. /// private readonly Configuration configuration; diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index ed09c9966..deb0a37f0 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -202,9 +202,9 @@ internal class QoiDecoderCore : IImageDecoderInternals // Get one pixel from the difference (-2..1) of the previous pixel case QoiChunk.QoiOpDiff: - byte redDifference = (byte)((operationByte & 0b00110000) >> 4); - byte greenDifference = (byte)((operationByte & 0b00001100) >> 2); - byte blueDifference = (byte)(operationByte & 0b00000011); + int redDifference = (operationByte & 0b00110000) >> 4; + int greenDifference = (operationByte & 0b00001100) >> 2; + int blueDifference = operationByte & 0b00000011; readPixel = previousPixel with { R = (byte)Numerics.Modulo256(previousPixel.R + (redDifference - 2)), @@ -219,14 +219,14 @@ internal class QoiDecoderCore : IImageDecoderInternals // Get green difference in 6 bits and red and blue differences // depending on the green one case QoiChunk.QoiOpLuma: - byte diffGreen = (byte)(operationByte & 0b00111111); - byte currentGreen = (byte)Numerics.Modulo256(previousPixel.G + (diffGreen - 32)); - byte nextByte = (byte)stream.ReadByte(); - byte diffRedDG = (byte)(nextByte >> 4); - byte diffBlueDG = (byte)(nextByte & 0b00001111); - byte currentRed = (byte)Numerics.Modulo256(diffRedDG - 8 + (diffGreen - 32) + previousPixel.R); - byte currentBlue = (byte)Numerics.Modulo256(diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B); - readPixel = previousPixel with { R = currentRed, B = currentBlue, G = currentGreen }; + int diffGreen = operationByte & 0b00111111; + int currentGreen = Numerics.Modulo256(previousPixel.G + (diffGreen - 32)); + int nextByte = stream.ReadByte(); + int diffRedDG = nextByte >> 4; + int diffBlueDG = nextByte & 0b00001111; + int currentRed = Numerics.Modulo256(diffRedDG - 8 + (diffGreen - 32) + previousPixel.R); + int currentBlue = Numerics.Modulo256(diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B); + readPixel = previousPixel with { R = (byte)currentRed, B = (byte)currentBlue, G = (byte)currentGreen }; pixel.FromRgba32(readPixel); pixelArrayPosition = GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; diff --git a/src/ImageSharp/Formats/Qoi/QoiEncoder.cs b/src/ImageSharp/Formats/Qoi/QoiEncoder.cs index 85763a89e..b3769d45c 100644 --- a/src/ImageSharp/Formats/Qoi/QoiEncoder.cs +++ b/src/ImageSharp/Formats/Qoi/QoiEncoder.cs @@ -27,7 +27,7 @@ public class QoiEncoder : ImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - QoiEncoderCore encoder = new(this, image.GetMemoryAllocator()); + QoiEncoderCore encoder = new(this, image.GetMemoryAllocator(), image.GetConfiguration()); encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs index 75a08d7a4..251b7436e 100644 --- a/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs @@ -24,15 +24,21 @@ public class QoiEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; + /// + /// The configuration instance for the encoding operation. + /// + private readonly Configuration configuration; + /// /// Initializes a new instance of the class. /// /// The encoder with options. /// The to use for buffer allocations. - public QoiEncoderCore(QoiEncoder encoder, MemoryAllocator memoryAllocator) + public QoiEncoderCore(QoiEncoder encoder, MemoryAllocator memoryAllocator, Configuration configuration) { this.encoder = encoder; this.memoryAllocator = memoryAllocator; + this.configuration = configuration; } /// @@ -75,15 +81,17 @@ public class QoiEncoderCore : IImageEncoderInternals Rgba32 previousPixel = new(0, 0, 0, 255); Rgba32 currentRgba32 = default; Buffer2D pixels = image.Frames[0].PixelBuffer; + using IMemoryOwner rgbaRowBuffer = this.memoryAllocator.Allocate(pixels.Width); + Span rgbaRow = rgbaRowBuffer.GetSpan(); for (int i = 0; i < pixels.Height; i++) { Span row = pixels.DangerousGetRowSpan(i); + PixelOperations.Instance.ToRgba32(this.configuration, row, rgbaRow); for (int j = 0; j < row.Length && i < pixels.Height; j++) { // We get the RGBA value from pixels - TPixel currentPixel = row[j]; - currentPixel.ToRgba32(ref currentRgba32); + currentRgba32 = rgbaRow[j]; // First, we check if the current pixel is equal to the previous one // If so, we do a QOI_OP_RUN @@ -97,7 +105,7 @@ public class QoiEncoderCore : IImageEncoderInternals * and we should discuss what to do about this approach and * if it's correct */ - byte repetitions = 0; + int repetitions = 0; do { repetitions++; @@ -112,15 +120,15 @@ public class QoiEncoderCore : IImageEncoderInternals } row = pixels.DangerousGetRowSpan(i); + PixelOperations.Instance.ToRgba32(this.configuration, row, rgbaRow); } - currentPixel = row[j]; - currentPixel.ToRgba32(ref currentRgba32); + currentRgba32 = rgbaRow[j]; } while (currentRgba32.Equals(previousPixel) && repetitions < 62); j--; - stream.WriteByte((byte)((byte)QoiChunk.QoiOpRun | (repetitions - 1))); + stream.WriteByte((byte)((int)QoiChunk.QoiOpRun | (repetitions - 1))); /* If it's a QOI_OP_RUN, we don't overwrite the previous pixel since * it will be taken and compared on the next iteration @@ -131,7 +139,7 @@ public class QoiEncoderCore : IImageEncoderInternals // else, we check if it exists in the previously seen pixels // If so, we do a QOI_OP_INDEX int pixelArrayPosition = GetArrayPosition(currentRgba32); - if (previouslySeenPixels[pixelArrayPosition].Equals(currentPixel)) + if (previouslySeenPixels[pixelArrayPosition].Equals(currentRgba32)) { stream.WriteByte((byte)pixelArrayPosition); } @@ -141,9 +149,9 @@ public class QoiEncoderCore : IImageEncoderInternals // Since it wasn't found on the previously seen pixels, we save it previouslySeenPixels[pixelArrayPosition] = currentRgba32; - sbyte diffRed = (sbyte)(currentRgba32.R - previousPixel.R); - sbyte diffGreen = (sbyte)(currentRgba32.G - previousPixel.G); - sbyte diffBlue = (sbyte)(currentRgba32.B - previousPixel.B); + int diffRed = currentRgba32.R - previousPixel.R; + int diffGreen = currentRgba32.G - previousPixel.G; + int diffBlue = currentRgba32.B - previousPixel.B; // If so, we do a QOI_OP_DIFF if (diffRed is >= -2 and <= 1 && @@ -152,26 +160,26 @@ public class QoiEncoderCore : IImageEncoderInternals currentRgba32.A == previousPixel.A) { // Bottom limit is -2, so we add 2 to make it equal to 0 - byte dr = (byte)(diffRed + 2); - byte dg = (byte)(diffGreen + 2); - byte db = (byte)(diffBlue + 2); - byte valueToWrite = (byte)((byte)QoiChunk.QoiOpDiff | (dr << 4) | (dg << 2) | db); + int dr = diffRed + 2; + int dg = diffGreen + 2; + int db = diffBlue + 2; + byte valueToWrite = (byte)((int)QoiChunk.QoiOpDiff | (dr << 4) | (dg << 2) | db); stream.WriteByte(valueToWrite); } else { // else, we check if the green difference is less than -32..31 and the rest -8..7 // If so, we do a QOI_OP_LUMA - sbyte diffRedGreen = (sbyte)(diffRed - diffGreen); - sbyte diffBlueGreen = (sbyte)(diffBlue - diffGreen); + int diffRedGreen = diffRed - diffGreen; + int diffBlueGreen = diffBlue - diffGreen; if (diffGreen is >= -32 and <= 31 && diffRedGreen is >= -8 and <= 7 && diffBlueGreen is >= -8 and <= 7 && currentRgba32.A == previousPixel.A) { - byte dr_dg = (byte)(diffRedGreen + 8); - byte db_dg = (byte)(diffBlueGreen + 8); - byte byteToWrite1 = (byte)((byte)QoiChunk.QoiOpLuma | (diffGreen + 32)); + int dr_dg = diffRedGreen + 8; + int db_dg = diffBlueGreen + 8; + byte byteToWrite1 = (byte)((int)QoiChunk.QoiOpLuma | (diffGreen + 32)); byte byteToWrite2 = (byte)((dr_dg << 4) | db_dg); stream.WriteByte(byteToWrite1); stream.WriteByte(byteToWrite2); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Qoi/IdentifyQoi.cs b/tests/ImageSharp.Benchmarks/Codecs/Qoi/IdentifyQoi.cs deleted file mode 100644 index 9631d80cb..000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Qoi/IdentifyQoi.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Qoi; -using SixLabors.ImageSharp.Tests; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Qoi; - -[Config(typeof(Config.ShortMultiFramework))] -public class IdentifyQoi -{ - private byte[] qoiBytes; - - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - - [Params(TestImages.Qoi.TestCardRGBA, TestImages.Qoi.TestCard, TestImages.Qoi.QoiLogo, TestImages.Qoi.EdgeCase, TestImages.Png.Bike)] - public string TestImage { get; set; } - - [GlobalSetup] - public void ReadImages() => this.qoiBytes ??= File.ReadAllBytes(this.TestImageFullPath); - - [Benchmark] - public ImageInfo Identify() - { - using MemoryStream memoryStream = new(this.qoiBytes); - return QoiDecoder.Instance.Identify(DecoderOptions.Default, memoryStream); - } -} diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 217e3165c..0ba2f4b94 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -69,8 +69,5 @@ - - -