|
|
|
@ -62,7 +62,7 @@ internal sealed class WebpLossyDecoder |
|
|
|
// Paragraph 9.2: color space and clamp type follow.
|
|
|
|
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1); |
|
|
|
sbyte clampType = (sbyte)this.bitReader.ReadValue(1); |
|
|
|
Vp8PictureHeader pictureHeader = new Vp8PictureHeader |
|
|
|
Vp8PictureHeader pictureHeader = new() |
|
|
|
{ |
|
|
|
Width = (uint)width, |
|
|
|
Height = (uint)height, |
|
|
|
@ -73,55 +73,51 @@ internal sealed class WebpLossyDecoder |
|
|
|
}; |
|
|
|
|
|
|
|
// Paragraph 9.3: Parse the segment header.
|
|
|
|
Vp8Proba proba = new Vp8Proba(); |
|
|
|
Vp8Proba proba = new(); |
|
|
|
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba); |
|
|
|
|
|
|
|
using (Vp8Decoder decoder = new Vp8Decoder( |
|
|
|
info.Vp8FrameHeader, |
|
|
|
pictureHeader, |
|
|
|
vp8SegmentHeader, |
|
|
|
proba, |
|
|
|
this.memoryAllocator)) |
|
|
|
{ |
|
|
|
Vp8Io io = InitializeVp8Io(decoder, pictureHeader); |
|
|
|
using Vp8Decoder decoder = new( |
|
|
|
info.Vp8FrameHeader, |
|
|
|
pictureHeader, |
|
|
|
vp8SegmentHeader, |
|
|
|
proba, |
|
|
|
this.memoryAllocator); |
|
|
|
Vp8Io io = InitializeVp8Io(decoder, pictureHeader); |
|
|
|
|
|
|
|
// Paragraph 9.4: Parse the filter specs.
|
|
|
|
this.ParseFilterHeader(decoder); |
|
|
|
decoder.PrecomputeFilterStrengths(); |
|
|
|
// Paragraph 9.4: Parse the filter specs.
|
|
|
|
this.ParseFilterHeader(decoder); |
|
|
|
decoder.PrecomputeFilterStrengths(); |
|
|
|
|
|
|
|
// Paragraph 9.5: Parse partitions.
|
|
|
|
this.ParsePartitions(decoder); |
|
|
|
// Paragraph 9.5: Parse partitions.
|
|
|
|
this.ParsePartitions(decoder); |
|
|
|
|
|
|
|
// Paragraph 9.6: Dequantization Indices.
|
|
|
|
this.ParseDequantizationIndices(decoder); |
|
|
|
// Paragraph 9.6: Dequantization Indices.
|
|
|
|
this.ParseDequantizationIndices(decoder); |
|
|
|
|
|
|
|
// Ignore the value of update probabilities.
|
|
|
|
this.bitReader.ReadBool(); |
|
|
|
// Ignore the value of update probabilities.
|
|
|
|
this.bitReader.ReadBool(); |
|
|
|
|
|
|
|
// Paragraph 13.4: Parse probabilities.
|
|
|
|
this.ParseProbabilities(decoder); |
|
|
|
// Paragraph 13.4: Parse probabilities.
|
|
|
|
this.ParseProbabilities(decoder); |
|
|
|
|
|
|
|
// Decode image data.
|
|
|
|
this.ParseFrame(decoder, io); |
|
|
|
// Decode image data.
|
|
|
|
this.ParseFrame(decoder, io); |
|
|
|
|
|
|
|
if (info.Features?.Alpha == true) |
|
|
|
{ |
|
|
|
using (AlphaDecoder alphaDecoder = new AlphaDecoder( |
|
|
|
width, |
|
|
|
height, |
|
|
|
alphaData, |
|
|
|
info.Features.AlphaChunkHeader, |
|
|
|
this.memoryAllocator, |
|
|
|
this.configuration)) |
|
|
|
{ |
|
|
|
alphaDecoder.Decode(); |
|
|
|
DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels); |
|
|
|
} |
|
|
|
if (info.Features?.Alpha == true) |
|
|
|
{ |
|
|
|
using AlphaDecoder alphaDecoder = new( |
|
|
|
width, |
|
|
|
height, |
|
|
|
alphaData, |
|
|
|
info.Features.AlphaChunkHeader, |
|
|
|
this.memoryAllocator, |
|
|
|
this.configuration); |
|
|
|
alphaDecoder.Decode(); |
|
|
|
DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -199,8 +195,8 @@ internal sealed class WebpLossyDecoder |
|
|
|
{ |
|
|
|
// Hardcoded tree parsing.
|
|
|
|
block.Segment = this.bitReader.GetBit((int)dec.Probabilities.Segments[0]) == 0 |
|
|
|
? (byte)this.bitReader.GetBit((int)dec.Probabilities.Segments[1]) |
|
|
|
: (byte)(this.bitReader.GetBit((int)dec.Probabilities.Segments[2]) + 2); |
|
|
|
? (byte)this.bitReader.GetBit((int)dec.Probabilities.Segments[1]) |
|
|
|
: (byte)(this.bitReader.GetBit((int)dec.Probabilities.Segments[2]) + 2); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -595,57 +591,65 @@ internal sealed class WebpLossyDecoder |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (dec.Filter == LoopFilter.Simple) |
|
|
|
switch (dec.Filter) |
|
|
|
{ |
|
|
|
int offset = dec.CacheYOffset + (mbx * 16); |
|
|
|
if (mbx > 0) |
|
|
|
case LoopFilter.Simple: |
|
|
|
{ |
|
|
|
LossyUtils.SimpleHFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4); |
|
|
|
} |
|
|
|
int offset = dec.CacheYOffset + (mbx * 16); |
|
|
|
if (mbx > 0) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleHFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4); |
|
|
|
} |
|
|
|
|
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleHFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit); |
|
|
|
} |
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleHFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit); |
|
|
|
} |
|
|
|
|
|
|
|
if (mby > 0) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleVFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4); |
|
|
|
} |
|
|
|
if (mby > 0) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleVFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4); |
|
|
|
} |
|
|
|
|
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleVFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (dec.Filter == LoopFilter.Complex) |
|
|
|
{ |
|
|
|
int uvBps = dec.CacheUvStride; |
|
|
|
int yOffset = dec.CacheYOffset + (mbx * 16); |
|
|
|
int uvOffset = dec.CacheUvOffset + (mbx * 8); |
|
|
|
int hevThresh = filterInfo.HighEdgeVarianceThreshold; |
|
|
|
if (mbx > 0) |
|
|
|
{ |
|
|
|
LossyUtils.HFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh); |
|
|
|
LossyUtils.HFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh); |
|
|
|
} |
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.SimpleVFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit); |
|
|
|
} |
|
|
|
|
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.HFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh); |
|
|
|
LossyUtils.HFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (mby > 0) |
|
|
|
case LoopFilter.Complex: |
|
|
|
{ |
|
|
|
LossyUtils.VFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh); |
|
|
|
LossyUtils.VFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh); |
|
|
|
} |
|
|
|
int uvBps = dec.CacheUvStride; |
|
|
|
int yOffset = dec.CacheYOffset + (mbx * 16); |
|
|
|
int uvOffset = dec.CacheUvOffset + (mbx * 8); |
|
|
|
int hevThresh = filterInfo.HighEdgeVarianceThreshold; |
|
|
|
if (mbx > 0) |
|
|
|
{ |
|
|
|
LossyUtils.HFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh); |
|
|
|
LossyUtils.HFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh); |
|
|
|
} |
|
|
|
|
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.VFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh); |
|
|
|
LossyUtils.VFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh); |
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.HFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh); |
|
|
|
LossyUtils.HFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh); |
|
|
|
} |
|
|
|
|
|
|
|
if (mby > 0) |
|
|
|
{ |
|
|
|
LossyUtils.VFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh); |
|
|
|
LossyUtils.VFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh); |
|
|
|
} |
|
|
|
|
|
|
|
if (filterInfo.UseInnerFiltering) |
|
|
|
{ |
|
|
|
LossyUtils.VFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh); |
|
|
|
LossyUtils.VFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1067,7 +1071,7 @@ internal sealed class WebpLossyDecoder |
|
|
|
|
|
|
|
private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba) |
|
|
|
{ |
|
|
|
Vp8SegmentHeader vp8SegmentHeader = new Vp8SegmentHeader |
|
|
|
Vp8SegmentHeader vp8SegmentHeader = new() |
|
|
|
{ |
|
|
|
UseSegment = this.bitReader.ReadBool() |
|
|
|
}; |
|
|
|
@ -1333,18 +1337,12 @@ internal sealed class WebpLossyDecoder |
|
|
|
private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz) |
|
|
|
{ |
|
|
|
nzCoeffs <<= 2; |
|
|
|
if (nz > 3) |
|
|
|
nzCoeffs |= nz switch |
|
|
|
{ |
|
|
|
nzCoeffs |= 3; |
|
|
|
} |
|
|
|
else if (nz > 1) |
|
|
|
{ |
|
|
|
nzCoeffs |= 2; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
nzCoeffs |= (uint)dcNz; |
|
|
|
} |
|
|
|
> 3 => 3, |
|
|
|
> 1 => 2, |
|
|
|
_ => (uint)dcNz |
|
|
|
}; |
|
|
|
|
|
|
|
return nzCoeffs; |
|
|
|
} |
|
|
|
@ -1358,13 +1356,13 @@ internal sealed class WebpLossyDecoder |
|
|
|
if (mbx == 0) |
|
|
|
{ |
|
|
|
return mby == 0 |
|
|
|
? 6 // B_DC_PRED_NOTOPLEFT
|
|
|
|
: 5; // B_DC_PRED_NOLEFT
|
|
|
|
? 6 // B_DC_PRED_NOTOPLEFT
|
|
|
|
: 5; // B_DC_PRED_NOLEFT
|
|
|
|
} |
|
|
|
|
|
|
|
return mby == 0 |
|
|
|
? 4 // B_DC_PRED_NOTOP
|
|
|
|
: 0; // B_DC_PRED
|
|
|
|
? 4 // B_DC_PRED_NOTOP
|
|
|
|
: 0; // B_DC_PRED
|
|
|
|
} |
|
|
|
|
|
|
|
return mode; |
|
|
|
|