From a87f78123f14f5c6a1b194da7743c36dc9f384c3 Mon Sep 17 00:00:00 2001 From: LuisAlfredo92 <92luisalfredo@protonmail.com> Date: Sat, 24 Jun 2023 21:47:01 -0600 Subject: [PATCH] Fixing edge case See https://github.com/phoboslab/qoi/issues/258 and previous commit --- src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs | 19 ++++++++++++++++--- .../Decode_Rgba32_edgecase.png | 4 ++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs index 11db0575bd..af07050558 100644 --- a/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs +++ b/src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs @@ -146,6 +146,12 @@ internal class QoiDecoderCore : IImageDecoderInternals { Rgba32[] previouslySeenPixels = new Rgba32[64]; Rgba32 previousPixel = new (0,0,0,255); + + // We save the pixel to avoid loosing the fully opaque black pixel + // See https://github.com/phoboslab/qoi/issues/258 + int pixelArrayPosition = this.GetArrayPosition(previousPixel); + previouslySeenPixels[pixelArrayPosition] = previousPixel; + for (int i = 0; i < this.header.Height; i++) { for (int j = 0; j < this.header.Width; j++) @@ -154,9 +160,9 @@ internal class QoiDecoderCore : IImageDecoderInternals byte[] pixelBytes; Rgba32 readPixel; TPixel pixel = new(); - int pixelArrayPosition; switch ((QoiChunkEnum)operationByte) { + // Reading one pixel with previous alpha intact case QoiChunkEnum.QOI_OP_RGB: pixelBytes = new byte[3]; if (stream.Read(pixelBytes) < 3) @@ -170,6 +176,7 @@ internal class QoiDecoderCore : IImageDecoderInternals previouslySeenPixels[pixelArrayPosition] = readPixel; break; + // Reading one pixel with new alpha case QoiChunkEnum.QOI_OP_RGBA: pixelBytes = new byte[4]; if (stream.Read(pixelBytes) < 4) @@ -186,12 +193,14 @@ internal class QoiDecoderCore : IImageDecoderInternals default: switch ((QoiChunkEnum)(operationByte & 0b11000000)) { + // Getting one pixel from previously seen pixels case QoiChunkEnum.QOI_OP_INDEX: readPixel = previouslySeenPixels[operationByte]; pixel.FromRgba32(readPixel); break; + + // Get one pixel from the difference (-2..1) of the previous pixel case QoiChunkEnum.QOI_OP_DIFF: - // Get each value byte redDifference = (byte)((operationByte & 0b00110000) >> 4), greenDifference = (byte)((operationByte & 0b00001100) >> 2), blueDifference = (byte)(operationByte & 0b00000011); @@ -205,8 +214,10 @@ internal class QoiDecoderCore : IImageDecoderInternals pixelArrayPosition = this.GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; + + // Get green difference in 6 bits and red and blue differences + // depending on the green one case QoiChunkEnum.QOI_OP_LUMA: - // Get difference green channel byte diffGreen = (byte)(operationByte & 0b00111111), currentGreen = (byte)((previousPixel.G + (diffGreen - 32)) % 256), nextByte = (byte)stream.ReadByte(), @@ -219,6 +230,8 @@ internal class QoiDecoderCore : IImageDecoderInternals pixelArrayPosition = this.GetArrayPosition(readPixel); previouslySeenPixels[pixelArrayPosition] = readPixel; break; + + // Repeating the previous pixel 1..63 times case QoiChunkEnum.QOI_OP_RUN: byte repetitions = (byte)(operationByte & 0b00111111); if(repetitions is 62 or 63) diff --git a/tests/Images/External/ReferenceOutput/QoiDecoderTests/Decode_Rgba32_edgecase.png b/tests/Images/External/ReferenceOutput/QoiDecoderTests/Decode_Rgba32_edgecase.png index f10fba3c29..d499d74c31 100644 --- a/tests/Images/External/ReferenceOutput/QoiDecoderTests/Decode_Rgba32_edgecase.png +++ b/tests/Images/External/ReferenceOutput/QoiDecoderTests/Decode_Rgba32_edgecase.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b39ab87da8d615f1ebb1348789a767b6421771dfcf11cd0b7cac618a0b8d6f1 -size 918 +oid sha256:12c966382b318c58578e3823ac066c597ce1e16ce7c2315b0f9d66451803a082 +size 1245