diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index 73fb3fed23..060ec0f0f9 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -366,117 +366,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
sbyte clampType = (sbyte)bitReader.ReadValue(1);
var vp8PictureHeader = new Vp8PictureHeader()
{
- Width = width,
- Height = height,
+ Width = (uint)width,
+ Height = (uint)height,
XScale = xScale,
YScale = yScale,
ColorSpace = colorSpace,
ClampType = clampType
};
- // Paragraph 9.3: Parse the segment header.
- bool hasValue;
- var proba = new Vp8Proba();
- Vp8SegmentHeader vp8SegmentHeader = ParseSegmentHeader(bitReader, proba);
-
- // Paragraph 9.4: Parse the filter specs.
- Vp8FilterHeader vp8FilterHeader = ParseFilterHeader(bitReader);
-
- // TODO: Review Paragraph 9.5: ParsePartitions.
- int numPartsMinusOne = (1 << (int)bitReader.ReadValue(2)) - 1;
- int lastPart = numPartsMinusOne;
- // TODO: check if we have enough data available here, throw exception if not
- int partStart = bitReader.Pos + (lastPart * 3);
-
- // 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;
- }
-
- // Ignore the value of update_proba
- bitReader.ReadBool();
-
- // Paragraph 13.4: Parse probabilities.
- for (int t = 0; t < WebPConstants.NumTypes; ++t)
- {
- for (int b = 0; b < WebPConstants.NumBands; ++b)
- {
- for (int c = 0; c < WebPConstants.NumCtx; ++c)
- {
- for (int p = 0; p < WebPConstants.NumProbas; ++p)
- {
- var prob = WebPConstants.CoeffsUpdateProba[t, b, c, p];
- int v = bitReader.GetBit(prob) == 0 ? (int)bitReader.ReadValue(8) : WebPConstants.DefaultCoeffsProba[t, b, c, p];
- proba.Bands[t, b].Probabilities[c].Probabilities[p] = (uint)v;
- }
- }
- }
-
- for (int b = 0; b < 16 + 1; ++b)
- {
- // TODO: This needs to be reviewed and fixed.
- // proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
- }
- }
-
- // TODO: those values needs to be stored somewhere
- bool useSkipProba = bitReader.ReadBool();
- if (useSkipProba)
- {
- var skipP = bitReader.ReadValue(8);
- }
-
return new WebPImageInfo()
{
Width = width,
@@ -486,97 +383,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
Features = features,
Vp8Profile = (sbyte)version,
Vp8FrameHeader = vp8FrameHeader,
- Vp8SegmentHeader = vp8SegmentHeader,
- Vp8FilterHeader = vp8FilterHeader,
Vp8PictureHeader = vp8PictureHeader,
Vp8BitReader = bitReader
};
}
- private static Vp8FilterHeader ParseFilterHeader(Vp8BitReader bitReader)
- {
- var vp8FilterHeader = new Vp8FilterHeader();
- vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
- vp8FilterHeader.Level = (int)bitReader.ReadValue(6);
- vp8FilterHeader.Sharpness = (int)bitReader.ReadValue(3);
- vp8FilterHeader.UseLfDelta = bitReader.ReadBool();
-
- // TODO: use enum here?
- // 0 = 0ff, 1 = simple, 2 = complex
- int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2;
- bool hasValue;
- if (vp8FilterHeader.UseLfDelta)
- {
- // Update lf-delta?
- if (bitReader.ReadBool())
- {
- for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++)
- {
- hasValue = bitReader.ReadBool();
- if (hasValue)
- {
- vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6);
- }
- }
-
- for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++)
- {
- hasValue = bitReader.ReadBool();
- if (hasValue)
- {
- vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6);
- }
- }
- }
- }
-
- return vp8FilterHeader;
- }
-
- private static Vp8SegmentHeader ParseSegmentHeader(Vp8BitReader bitReader, Vp8Proba proba)
- {
- var vp8SegmentHeader = new Vp8SegmentHeader();
- vp8SegmentHeader.UseSegment = bitReader.ReadBool();
- bool hasValue;
- if (vp8SegmentHeader.UseSegment)
- {
- vp8SegmentHeader.UpdateMap = bitReader.ReadBool();
- bool updateData = bitReader.ReadBool();
- if (updateData)
- {
- vp8SegmentHeader.Delta = bitReader.ReadBool();
- for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
- {
- hasValue = bitReader.ReadBool();
- uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0;
- vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue;
- }
-
- for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
- {
- hasValue = bitReader.ReadBool();
- uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0;
- vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue;
- }
-
- if (vp8SegmentHeader.UpdateMap)
- {
- for (int s = 0; s < proba.Segments.Length; ++s)
- {
- hasValue = bitReader.ReadBool();
- proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255;
- }
- }
- }
- }
- else
- {
- vp8SegmentHeader.UpdateMap = false;
- }
-
- return vp8SegmentHeader;
- }
-
///
/// Reads the header of a lossless webp image.
///
@@ -695,10 +506,5 @@ 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;
- }
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs
index 1eeb4d2e0a..dace37aada 100644
--- a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs
+++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs
@@ -45,16 +45,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
public Vp8PictureHeader Vp8PictureHeader { get; set; }
- ///
- /// Gets or sets the VP8 segment header.
- ///
- public Vp8SegmentHeader Vp8SegmentHeader { get; set; }
-
- ///
- /// Gets or sets the VP8 filter header.
- ///
- public Vp8FilterHeader Vp8FilterHeader { get; set; }
-
///
/// Gets or sets the VP8L bitreader. Will be null, if its not lossless image.
///
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index 86fb099fd6..59528067e8 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -1,11 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.Collections.Generic;
-using System.IO;
-using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -36,6 +33,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO residue signal from DCT: 4x4 blocks of DCT transforms, 16Y, 4U, 4V
Vp8Profile vp8Profile = this.DecodeProfile(vp8Version);
+
+ // Paragraph 9.3: Parse the segment header.
+ var proba = new Vp8Proba();
+ Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
+
+ // Paragraph 9.4: Parse the filter specs.
+ Vp8FilterHeader vp8FilterHeader = this.ParseFilterHeader();
+
+ // TODO: Review Paragraph 9.5: ParsePartitions.
+ int numPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1;
+ int lastPart = numPartsMinusOne;
+ // TODO: check if we have enough data available here, throw exception if not
+ int partStart = this.bitReader.Pos + (lastPart * 3);
+
+ // Paragraph 9.6: Dequantization Indices.
+ this.ParseDequantizationIndices(vp8SegmentHeader);
+
+ // Ignore the value of update_proba
+ this.bitReader.ReadBool();
+
+ // Paragraph 13.4: Parse probabilities.
+ this.ParseProbabilities(proba);
}
private Vp8Profile DecodeProfile(int version)
@@ -58,11 +77,191 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
+ private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba)
+ {
+ var vp8SegmentHeader = new Vp8SegmentHeader
+ {
+ UseSegment = this.bitReader.ReadBool()
+ };
+ if (vp8SegmentHeader.UseSegment)
+ {
+ vp8SegmentHeader.UpdateMap = this.bitReader.ReadBool();
+ bool updateData = this.bitReader.ReadBool();
+ if (updateData)
+ {
+ vp8SegmentHeader.Delta = this.bitReader.ReadBool();
+ bool hasValue;
+ for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
+ {
+ hasValue = this.bitReader.ReadBool();
+ uint quantizeValue = hasValue ? this.bitReader.ReadValue(7) : 0;
+ vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue;
+ }
+
+ for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
+ {
+ hasValue = this.bitReader.ReadBool();
+ uint filterStrengthValue = hasValue ? this.bitReader.ReadValue(6) : 0;
+ vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue;
+ }
+
+ if (vp8SegmentHeader.UpdateMap)
+ {
+ for (int s = 0; s < proba.Segments.Length; ++s)
+ {
+ hasValue = bitReader.ReadBool();
+ proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255;
+ }
+ }
+ }
+ }
+ else
+ {
+ vp8SegmentHeader.UpdateMap = false;
+ }
+
+ return vp8SegmentHeader;
+ }
+
+ private Vp8FilterHeader ParseFilterHeader()
+ {
+ var vp8FilterHeader = new Vp8FilterHeader();
+ vp8FilterHeader.LoopFilter = this.bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
+ vp8FilterHeader.Level = (int)this.bitReader.ReadValue(6);
+ vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3);
+ vp8FilterHeader.UseLfDelta = this.bitReader.ReadBool();
+
+ // TODO: use enum here?
+ // 0 = 0ff, 1 = simple, 2 = complex
+ int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2;
+ if (vp8FilterHeader.UseLfDelta)
+ {
+ // Update lf-delta?
+ if (this.bitReader.ReadBool())
+ {
+ bool hasValue;
+ for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++)
+ {
+ hasValue = this.bitReader.ReadBool();
+ if (hasValue)
+ {
+ vp8FilterHeader.RefLfDelta[i] = this.bitReader.ReadSignedValue(6);
+ }
+ }
+
+ for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++)
+ {
+ hasValue = this.bitReader.ReadBool();
+ if (hasValue)
+ {
+ vp8FilterHeader.ModeLfDelta[i] = this.bitReader.ReadSignedValue(6);
+ }
+ }
+ }
+ }
+
+ return vp8FilterHeader;
+ }
+
+ private void ParseDequantizationIndices(Vp8SegmentHeader vp8SegmentHeader)
+ {
+ int baseQ0 = (int)this.bitReader.ReadValue(7);
+ bool hasValue = this.bitReader.ReadBool();
+ int dqy1Dc = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
+ hasValue = this.bitReader.ReadBool();
+ int dqy2Dc = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
+ hasValue = this.bitReader.ReadBool();
+ int dqy2Ac = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
+ hasValue = this.bitReader.ReadBool();
+ int dquvDc = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
+ hasValue = this.bitReader.ReadBool();
+ int dquvAc = hasValue ? this.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;
+ }
+ }
+
+ private void ParseProbabilities(Vp8Proba proba)
+ {
+ for (int t = 0; t < WebPConstants.NumTypes; ++t)
+ {
+ for (int b = 0; b < WebPConstants.NumBands; ++b)
+ {
+ for (int c = 0; c < WebPConstants.NumCtx; ++c)
+ {
+ for (int p = 0; p < WebPConstants.NumProbas; ++p)
+ {
+ var prob = WebPConstants.CoeffsUpdateProba[t, b, c, p];
+ int v = this.bitReader.GetBit(prob) == 0
+ ? (int)this.bitReader.ReadValue(8)
+ : WebPConstants.DefaultCoeffsProba[t, b, c, p];
+ proba.Bands[t, b].Probabilities[c].Probabilities[p] = (uint)v;
+ }
+ }
+ }
+
+ for (int b = 0; b < 16 + 1; ++b)
+ {
+ // TODO: This needs to be reviewed and fixed.
+ // proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
+ }
+ }
+
+ // TODO: those values needs to be stored somewhere
+ bool useSkipProba = this.bitReader.ReadBool();
+ if (useSkipProba)
+ {
+ var skipP = this.bitReader.ReadValue(8);
+ }
+ }
+
static bool Is8bOptimizable(Vp8LMetadata hdr)
{
int i;
if (hdr.ColorCacheSize > 0)
+ {
return false;
+ }
// When the Huffman tree contains only one symbol, we can skip the
// call to ReadSymbol() for red/blue/alpha channels.
@@ -70,15 +269,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
List htrees = hdr.HTreeGroups[i].HTrees;
if (htrees[HuffIndex.Red][0].Value > 0)
+ {
return false;
+ }
+
if (htrees[HuffIndex.Blue][0].Value > 0)
+ {
return false;
+ }
+
if (htrees[HuffIndex.Alpha][0].Value > 0)
+ {
return false;
+ }
}
return true;
}
+
+ private int Clip(int v, int M)
+ {
+ return v < 0 ? 0 : v > M ? M : v;
+ }
}
struct YUVPixel