From 0af68d8a6bf33a6566127563fd4249a600e39b52 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 1 Mar 2020 14:02:00 +0100 Subject: [PATCH] Fix decoding very small lossy webp images --- src/ImageSharp/Formats/WebP/Vp8Decoder.cs | 21 ++++++++++++------- .../Formats/WebP/WebPLossyDecoder.cs | 20 +++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs index cb4ef199d9..848994d4ed 100644 --- a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs +++ b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs @@ -50,12 +50,15 @@ namespace SixLabors.ImageSharp.Formats.WebP uint height = pictureHeader.Height; // TODO: use memory allocator - this.CacheY = new byte[width * height]; // TODO: this is way too much mem, figure out what the min req is. - this.CacheU = new byte[width * height]; - this.CacheV = new byte[width * height]; - this.TmpYBuffer = new byte[width * height]; // TODO: figure out min buffer length - this.TmpUBuffer = new byte[width * height]; // TODO: figure out min buffer length - this.TmpVBuffer = new byte[width * height]; // TODO: figure out min buffer length + int extraRows = WebPConstants.FilterExtraRows[2]; // TODO: assuming worst case: complex filter + int extraY = extraRows * this.CacheYStride; + int extraUv = (extraRows / 2) * this.CacheUvStride; + this.CacheY = new byte[width * height + extraY + 256]; // TODO: this is way too much mem, figure out what the min req is. + this.CacheU = new byte[width * height + extraUv + 256]; + this.CacheV = new byte[width * height + extraUv + 256]; + this.TmpYBuffer = new byte[width * height + extraY]; // TODO: figure out min buffer length + this.TmpUBuffer = new byte[width * height + extraUv]; // TODO: figure out min buffer length + this.TmpVBuffer = new byte[width * height + extraUv]; // TODO: figure out min buffer length this.Bgr = new byte[width * height * 4]; for (int i = 0; i < this.YuvBuffer.Length; i++) @@ -66,6 +69,10 @@ namespace SixLabors.ImageSharp.Formats.WebP for (int i = 0; i < this.CacheY.Length; i++) { this.CacheY[i] = 205; + } + + for (int i = 0; i < this.CacheU.Length; i++) + { this.CacheU[i] = 205; this.CacheV[i] = 205; } @@ -82,7 +89,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public Vp8SegmentHeader SegmentHeader { get; } // number of partitions minus one. - public uint NumPartsMinusOne { get; } + public int NumPartsMinusOne { get; set; } // per-partition boolean decoders. public Vp8BitReader[] Vp8BitReaders { get; } diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index 0ddc75c6f4..82c9b9bd0e 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -452,13 +452,13 @@ namespace SixLabors.ImageSharp.Formats.WebP Span vOut = dec.CacheV.AsSpan(dec.CacheUvOffset + (mbx * 8)); for (int j = 0; j < 16; ++j) { - yDst.Slice(j * WebPConstants.Bps, 16).CopyTo(yOut.Slice(j * dec.CacheYStride)); + yDst.Slice(j * WebPConstants.Bps, Math.Min(16, yOut.Length)).CopyTo(yOut.Slice(j * dec.CacheYStride)); } for (int j = 0; j < 8; ++j) { - uDst.Slice(j * WebPConstants.Bps, 8).CopyTo(uOut.Slice(j * dec.CacheUvStride)); - vDst.Slice(j * WebPConstants.Bps, 8).CopyTo(vOut.Slice(j * dec.CacheUvStride)); + uDst.Slice(j * WebPConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(j * dec.CacheUvStride)); + vDst.Slice(j * WebPConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(j * dec.CacheUvStride)); } } } @@ -579,6 +579,11 @@ namespace SixLabors.ImageSharp.Formats.WebP yEnd -= extraYRows; } + if (yEnd > io.CropBottom) + { + yEnd = io.CropBottom; // make sure we don't overflow on last row. + } + if (yStart < yEnd) { io.Y = io.Y.Slice(io.CropLeft); @@ -791,7 +796,7 @@ namespace SixLabors.ImageSharp.Formats.WebP else { left.NoneZeroAcDcCoeffs = macroBlock.NoneZeroAcDcCoeffs = 0; - if (blockData.IsI4x4) + if (!blockData.IsI4x4) { left.NoneZeroDcCoeffs = macroBlock.NoneZeroDcCoeffs = 0; } @@ -917,8 +922,7 @@ namespace SixLabors.ImageSharp.Formats.WebP block.NonZeroUv = nonZeroUv; // We look at the mode-code of each block and check if some blocks have less - // than three non-zero coeffs (code < 2). This is to avoid dithering flat and - // empty blocks. + // than three non-zero coeffs (code < 2). This is to avoid dithering flat and empty blocks. block.Dither = (byte)((nonZeroUv & 0xaaaa) > 0 ? 0 : q.Dither); return (nonZeroY | nonZeroUv) is 0; @@ -1161,8 +1165,8 @@ namespace SixLabors.ImageSharp.Formats.WebP int startIdx = (int)this.bitReader.PartitionLength; Span sz = this.bitReader.Data.AsSpan(startIdx); int sizeLeft = (int)size; - int numPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; - int lastPart = numPartsMinusOne; + dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; + int lastPart = dec.NumPartsMinusOne; int partStart = startIdx + (lastPart * 3); sizeLeft -= lastPart * 3;