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 or WebpAlphaFilterType.Horizontal ? 0 : this.LastRow; int firstRow = this.LastRow < topRow ? topRow : this.LastRow; if (lastRow > firstRow) { // Special method for paletted alpha data. Span <byte> output = this.Alpha.Memory.Span; Span <uint> pixelData = this.Vp8LDec.Pixels.Memory.Span; Span <byte> pixelDataAsBytes = MemoryMarshal.Cast <uint, byte>(pixelData); Span <byte> dst = output.Slice(this.Width * firstRow); Span <byte> input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow); if (this.Vp8LDec.Transforms.Count == 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; }
/// <summary> /// Decodes and filters the maybe compressed alpha data. /// </summary> public void Decode() { if (!this.Compressed) { Span <byte> dataSpan = this.Data.Memory.Span; int pixelCount = this.Width * this.Height; if (dataSpan.Length < pixelCount) { WebpThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); } Span <byte> alphaSpan = this.Alpha.Memory.Span; if (this.AlphaFilterType == WebpAlphaFilterType.None) { dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan); return; } Span <byte> deltas = dataSpan; Span <byte> dst = alphaSpan; Span <byte> prev = default; for (int y = 0; y < this.Height; y++) { switch (this.AlphaFilterType) { case WebpAlphaFilterType.Horizontal: HorizontalUnfilter(prev, deltas, dst, this.Width); break; case WebpAlphaFilterType.Vertical: VerticalUnfilter(prev, deltas, dst, this.Width); break; case WebpAlphaFilterType.Gradient: GradientUnfilter(prev, deltas, dst, this.Width); break; } prev = dst; deltas = deltas.Slice(this.Width); dst = dst.Slice(this.Width); } } else { if (this.Use8BDecode) { this.LosslessDecoder.DecodeAlphaData(this); } else { this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span); this.ExtractAlphaRows(this.Vp8LDec); } } }
/// <summary> /// Initializes a new instance of the <see cref="AlphaDecoder"/> class. /// </summary> /// <param name="width">The width of the image.</param> /// <param name="height">The height of the image.</param> /// <param name="data">The (maybe compressed) alpha data.</param> /// <param name="alphaChunkHeader">The first byte of the alpha image stream contains information on how to decode the stream.</param> /// <param name="memoryAllocator">Used for allocating memory during decoding.</param> /// <param name="configuration">The configuration.</param> public AlphaDecoder(int width, int height, IMemoryOwner <byte> data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration) { this.Width = width; this.Height = height; this.Data = data; this.memoryAllocator = memoryAllocator; this.LastRow = 0; int totalPixels = width * height; var compression = (WebpAlphaCompressionMethod)(alphaChunkHeader & 0x03); if (compression is not WebpAlphaCompressionMethod.NoCompression and not WebpAlphaCompressionMethod.WebpLosslessCompression) { WebpThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found"); } this.Compressed = compression == WebpAlphaCompressionMethod.WebpLosslessCompression; // The filtering method used. Only values between 0 and 3 are valid. int filter = (alphaChunkHeader >> 2) & 0x03; if (filter is < (int)WebpAlphaFilterType.None or > (int)WebpAlphaFilterType.Gradient) { WebpThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {filter} found"); } this.Alpha = memoryAllocator.Allocate <byte>(totalPixels); this.AlphaFilterType = (WebpAlphaFilterType)filter; this.Vp8LDec = new Vp8LDecoder(width, height, memoryAllocator); if (this.Compressed) { var bitReader = new Vp8LBitReader(data); this.LosslessDecoder = new WebpLosslessDecoder(bitReader, memoryAllocator, configuration); this.LosslessDecoder.DecodeImageStream(this.Vp8LDec, width, height, true); this.Use8BDecode = this.Vp8LDec.Transforms.Count > 0 && Is8BOptimizable(this.Vp8LDec.Metadata); } }