diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs index 92a2c0156..fb5663464 100644 --- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs @@ -1,8 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.ImageSharp.Formats.WebP.Filters; + namespace SixLabors.ImageSharp.Formats.WebP { internal ref struct AlphaDecoder diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index 6f0c4618c..19babc4a3 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public const int MaxColorCacheBits = 11; /// - /// The maximum number of allowed transforms in a bitstream. + /// The maximum number of allowed transforms in a VP8L bitstream. /// public const int MaxNumberOfTransforms = 4; @@ -104,5 +104,51 @@ namespace SixLabors.ImageSharp.Formats.WebP NumLiteralCodes, NumLiteralCodes, NumLiteralCodes, NumDistanceCodes }; + + // VP8 constants + public const int NumMbSegments = 4; + + public const int MaxNumPartitions = 8; + + // Paragraph 14.1 + public static readonly int[] DcTable = + { + 4, 5, 6, 7, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 16, 17, 17, + 18, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, + 91, 93, 95, 96, 98, 100, 101, 102, + 104, 106, 108, 110, 112, 114, 116, 118, + 122, 124, 126, 128, 130, 132, 134, 136, + 138, 140, 143, 145, 148, 151, 154, 157 + }; + + public static readonly int[] AcTable = + { + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 60, + 62, 64, 66, 68, 70, 72, 74, 76, + 78, 80, 82, 84, 86, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, + 110, 112, 114, 116, 119, 122, 125, 128, + 131, 134, 137, 140, 143, 146, 149, 152, + 155, 158, 161, 164, 167, 170, 173, 177, + 181, 185, 189, 193, 197, 201, 205, 209, + 213, 217, 221, 225, 229, 234, 239, 245, + 249, 254, 259, 264, 269, 274, 279, 284 + }; } } diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index f5e1af2cb..8de4c350a 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -374,6 +374,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Paragraph 9.3: Parse the segment header. var vp8SegmentHeader = new Vp8SegmentHeader(); vp8SegmentHeader.UseSegment = bitReader.ReadBool(); + bool hasValue = false; if (vp8SegmentHeader.UseSegment) { vp8SegmentHeader.UpdateMap = bitReader.ReadBool(); @@ -383,14 +384,14 @@ namespace SixLabors.ImageSharp.Formats.WebP vp8SegmentHeader.Delta = bitReader.ReadBool(); for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) { - bool hasValue = bitReader.ReadBool(); + hasValue = bitReader.ReadBool(); uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0; vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue; } for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) { - bool hasValue = bitReader.ReadBool(); + hasValue = bitReader.ReadBool(); uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0; vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue; } @@ -419,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++) { - bool hasValue = bitReader.ReadBool(); + hasValue = bitReader.ReadBool(); if (hasValue) { vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6); @@ -428,7 +429,7 @@ namespace SixLabors.ImageSharp.Formats.WebP for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++) { - bool hasValue = bitReader.ReadBool(); + hasValue = bitReader.ReadBool(); if (hasValue) { vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6); @@ -442,7 +443,61 @@ namespace SixLabors.ImageSharp.Formats.WebP int lastPart = numPartsMinusOne; // TODO: check if we have enough data available here, throw exception if not + // Paragraph 9.6: Dequantization Indices + int baseQ0 = (int)bitReader.ReadValue(7); + hasValue = bitReader.ReadBool(); + int dqy1Dc = hasValue ? bitReader.ReadSignedValue(4) : 0; + hasValue = bitReader.ReadBool(); + int dqy2Dc = hasValue ? bitReader.ReadSignedValue(4) : 0; + hasValue = bitReader.ReadBool(); + int dqy2Ac = hasValue ? bitReader.ReadSignedValue(4) : 0; + hasValue = bitReader.ReadBool(); + int dquvDc = hasValue ? bitReader.ReadSignedValue(4) : 0; + hasValue = bitReader.ReadBool(); + int dquvAc = hasValue ? bitReader.ReadSignedValue(4) : 0; + for (int i = 0; i < WebPConstants.NumMbSegments; ++i) + { + int q; + if (vp8SegmentHeader.UseSegment) + { + q = vp8SegmentHeader.Quantizer[i]; + if (!vp8SegmentHeader.Delta) + { + q += baseQ0; + } + } + else + { + if (i > 0) + { + // dec->dqm_[i] = dec->dqm_[0]; + continue; + } + else + { + q = baseQ0; + } + } + + var m = new Vp8QuantMatrix(); + m.Y1Mat[0] = WebPConstants.DcTable[this.Clip(q + dqy1Dc, 127)]; + m.Y1Mat[1] = WebPConstants.AcTable[this.Clip(q + 0, 127)]; + m.Y2Mat[0] = WebPConstants.DcTable[this.Clip(q + dqy2Dc, 127)] * 2; + + // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. + // The smallest precision for that is '(x*6349) >> 12' but 16 is a good word size. + m.Y2Mat[1] = (WebPConstants.AcTable[this.Clip(q + dqy2Ac, 127)] * 101581) >> 16; + if (m.Y2Mat[1] < 8) + { + m.Y2Mat[1] = 8; + } + m.UVMat[0] = WebPConstants.DcTable[this.Clip(q + dquvDc, 117)]; + m.UVMat[1] = WebPConstants.AcTable[this.Clip(q + dquvAc, 127)]; + + // For dithering strength evaluation. + m.UvQuant = q + dquvAc; + } return new WebPImageInfo() { @@ -578,5 +633,10 @@ namespace SixLabors.ImageSharp.Formats.WebP throw new ImageFormatException("Invalid WebP data."); } + + private int Clip(int v, int M) + { + return v < 0 ? 0 : v > M ? M : v; + } } }