From d14b9819a5eb2429060d533bb418e84663f0dd97 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Mar 2020 13:14:10 +0100 Subject: [PATCH] Move alpha decoding related methods into the AlphaDecoder class --- src/ImageSharp/Formats/WebP/AlphaDecoder.cs | 93 +++++++++++++++++- src/ImageSharp/Formats/WebP/LosslessUtils.cs | 2 +- .../Formats/WebP/WebPLosslessDecoder.cs | 95 +------------------ 3 files changed, 93 insertions(+), 97 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs index 0163d74c0..fc0bb92af 100644 --- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs @@ -5,7 +5,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.WebP @@ -98,8 +98,6 @@ namespace SixLabors.ImageSharp.Formats.WebP /// public IMemoryOwner Alpha { get; } - public int CropTop { get; } - /// /// Gets a value indicating whether the alpha channel uses compression. /// @@ -218,6 +216,34 @@ namespace SixLabors.ImageSharp.Formats.WebP this.PrevRow = lastRow - 1; } + public void ExtractPalettedAlphaRows(int lastRow) + { + // For vertical and gradient filtering, we need to decode the part above the + // cropTop row, in order to have the correct spatial predictors. + int topRow = (this.AlphaFilterType is WebPAlphaFilterType.None || this.AlphaFilterType is WebPAlphaFilterType.Horizontal) ? 0 : this.LastRow; + int firstRow = (this.LastRow < topRow) ? topRow : this.LastRow; + if (lastRow > firstRow) + { + // Special method for paletted alpha data. + Span output = this.Alpha.Memory.Span; + Span pixelData = this.Vp8LDec.Pixels.Memory.Span; + Span pixelDataAsBytes = MemoryMarshal.Cast(pixelData); + Span dst = output.Slice(this.Width * firstRow); + Span input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow); + + if (this.Vp8LDec.Transforms.Count is 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) + { + WebPThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing"); + } + + Vp8LTransform transform = this.Vp8LDec.Transforms[0]; + ColorIndexInverseTransformAlpha(transform, firstRow, lastRow, input, dst); + this.AlphaApplyFilter(firstRow, lastRow, dst, this.Width); + } + + this.LastRow = lastRow; + } + /// /// Once the image-stream is decoded into ARGB color values, the transparency information will be extracted from the green channel of the ARGB quadruplet. /// @@ -237,6 +263,46 @@ namespace SixLabors.ImageSharp.Formats.WebP ExtractGreen(input, output, pixelCount); } + private static void ColorIndexInverseTransformAlpha( + Vp8LTransform transform, + int yStart, + int yEnd, + Span src, + Span dst) + { + int bitsPerPixel = 8 >> transform.Bits; + int width = transform.XSize; + Span colorMap = transform.Data.Memory.Span; + int srcOffset = 0; + int dstOffset = 0; + if (bitsPerPixel < 8) + { + int pixelsPerByte = 1 << transform.Bits; + int countMask = pixelsPerByte - 1; + int bitMask = (1 << bitsPerPixel) - 1; + for (int y = yStart; y < yEnd; ++y) + { + int packedPixels = 0; + for (int x = 0; x < width; ++x) + { + if ((x & countMask) is 0) + { + packedPixels = src[srcOffset]; + srcOffset++; + } + + dst[dstOffset] = GetAlphaValue((int)colorMap[packedPixels & bitMask]); + dstOffset++; + packedPixels >>= bitsPerPixel; + } + } + } + else + { + MapAlpha(src, colorMap, dst, yStart, yEnd, width); + } + } + private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { byte pred = (byte)(prev == null ? 0 : prev[0]); @@ -290,7 +356,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// transform (color indexing), and trivial non-green literals. /// /// The VP8L meta data. - /// True, if alpha channel has one byte per pixel, otherwise 4. + /// True, if alpha channel needs one byte per pixel, otherwise 4. private static bool Is8BOptimizable(Vp8LMetadata hdr) { if (hdr.ColorCacheSize > 0) @@ -322,6 +388,25 @@ namespace SixLabors.ImageSharp.Formats.WebP return true; } + private static void MapAlpha(Span src, Span colorMap, Span dst, int yStart, int yEnd, int width) + { + int offset = 0; + for (int y = yStart; y < yEnd; ++y) + { + for (int x = 0; x < width; ++x) + { + dst[offset] = GetAlphaValue((int)colorMap[src[offset]]); + offset++; + } + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static byte GetAlphaValue(int val) + { + return (byte)((val >> 8) & 0xff); + } + [MethodImpl(InliningOptions.ShortMethod)] private static int GradientPredictor(byte a, byte b, byte c) { diff --git a/src/ImageSharp/Formats/WebP/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/LosslessUtils.cs index 720fb57d9..8ad2ef19d 100644 --- a/src/ImageSharp/Formats/WebP/LosslessUtils.cs +++ b/src/ImageSharp/Formats/WebP/LosslessUtils.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } /// - /// If there are not many unique pixel values, it may be more efficient to create a color index array and replace the pixel values by the array's indices. + /// If there are not many unique pixel values, it is more efficient to create a color index array and replace the pixel values by the array's indices. /// This will reverse the color index transform. /// /// The transform data contains color table size and the entries in the color table. diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 2c0b69911..88c1cd0d1 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -745,7 +745,7 @@ namespace SixLabors.ImageSharp.Formats.WebP ++row; if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) { - this.ExtractPalettedAlphaRows(dec, row); + dec.ExtractPalettedAlphaRows(row); } } } @@ -775,7 +775,7 @@ namespace SixLabors.ImageSharp.Formats.WebP ++row; if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows is 0)) { - this.ExtractPalettedAlphaRows(dec, row); + dec.ExtractPalettedAlphaRows(row); } } @@ -793,90 +793,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } // Process the remaining rows corresponding to last row-block. - this.ExtractPalettedAlphaRows(dec, row > lastRow ? lastRow : row); - } - - private void ExtractPalettedAlphaRows(AlphaDecoder dec, int lastRow) - { - // For vertical and gradient filtering, we need to decode the part above the - // cropTop row, in order to have the correct spatial predictors. - int topRow = (dec.AlphaFilterType is WebPAlphaFilterType.None || dec.AlphaFilterType is WebPAlphaFilterType.Horizontal) - ? dec.CropTop - : dec.LastRow; - int firstRow = (dec.LastRow < topRow) ? topRow : dec.LastRow; - if (lastRow > firstRow) - { - // Special method for paletted alpha data. - Span output = dec.Alpha.Memory.Span; - Span pixelData = dec.Vp8LDec.Pixels.Memory.Span; - Span pixelDataAsBytes = MemoryMarshal.Cast(pixelData); - Span dst = output.Slice(dec.Width * firstRow); - Span input = pixelDataAsBytes.Slice(dec.Vp8LDec.Width * firstRow); - - if (dec.Vp8LDec.Transforms.Count is 0 || dec.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) - { - WebPThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing"); - } - - Vp8LTransform transform = dec.Vp8LDec.Transforms[0]; - this.ColorIndexInverseTransformAlpha(transform, firstRow, lastRow, input, dst); - dec.AlphaApplyFilter(firstRow, lastRow, dst, dec.Width); - } - - dec.LastRow = lastRow; - } - - private void ColorIndexInverseTransformAlpha( - Vp8LTransform transform, - int yStart, - int yEnd, - Span src, - Span dst) - { - int bitsPerPixel = 8 >> transform.Bits; - int width = transform.XSize; - Span colorMap = transform.Data.Memory.Span; - int srcOffset = 0; - int dstOffset = 0; - if (bitsPerPixel < 8) - { - int pixelsPerByte = 1 << transform.Bits; - int countMask = pixelsPerByte - 1; - int bitMask = (1 << bitsPerPixel) - 1; - for (int y = yStart; y < yEnd; ++y) - { - int packedPixels = 0; - for (int x = 0; x < width; ++x) - { - if ((x & countMask) is 0) - { - packedPixels = src[srcOffset]; - srcOffset++; - } - - dst[dstOffset] = GetAlphaValue((int)colorMap[packedPixels & bitMask]); - dstOffset++; - packedPixels >>= bitsPerPixel; - } - } - } - else - { - MapAlpha(src, colorMap, dst, yStart, yEnd, width); - } - } - - private static void MapAlpha(Span src, Span colorMap, Span dst, int yStart, int yEnd, int width) - { - int offset = 0; - for (int y = yStart; y < yEnd; ++y) - { - for (int x = 0; x < width; ++x) - { - dst[offset] = GetAlphaValue((int)colorMap[src[offset]]); - offset++; - } - } + dec.ExtractPalettedAlphaRows(row > lastRow ? lastRow : row); } private void UpdateDecoder(Vp8LDecoder decoder, int width, int height) @@ -1048,11 +965,5 @@ namespace SixLabors.ImageSharp.Formats.WebP huff.Value |= hCode.Value << shift; return hCode.BitsUsed; } - - [MethodImpl(InliningOptions.ShortMethod)] - private static byte GetAlphaValue(int val) - { - return (byte)((val >> 8) & 0xff); - } } }