Browse Source

refactor

pull/2569/head
Poker 3 years ago
parent
commit
62ab3a1eef
No known key found for this signature in database GPG Key ID: C65A6AD457D5C8F8
  1. 11
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  2. 104
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  3. 5
      src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
  4. 4
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  5. 5
      src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

11
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Drawing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
@ -181,7 +180,7 @@ internal abstract class BitWriterBase
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="animation">Animation frame data.</param> /// <param name="animation">Animation frame data.</param>
/// <param name="data">Frame data.</param> /// <param name="data">Frame data.</param>
protected void WriteAnimationFrame(Stream stream, AnimationFrameData animation, byte[] data) protected void WriteAnimationFrame(Stream stream, AnimationFrameData animation, Span<byte> data)
{ {
uint size = AnimationFrameData.HeaderSize + (uint)data.Length; uint size = AnimationFrameData.HeaderSize + (uint)data.Length;
Span<byte> buf = this.scratchBuffer.Span[..4]; Span<byte> buf = this.scratchBuffer.Span[..4];
@ -260,6 +259,14 @@ internal abstract class BitWriterBase
flags |= 8; flags |= 8;
} }
/*
if (isAnimated)
{
// Set animated flag.
flags |= 2;
}
*/
if (xmpProfile != null) if (xmpProfile != null)
{ {
// Set xmp bit. // Set xmp bit.

104
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -116,7 +116,7 @@ internal class Vp8BitWriter : BitWriterBase
else else
{ {
this.PutBit(v >= 9, 165); this.PutBit(v >= 9, 165);
this.PutBit(!((v & 1) != 0), 145); this.PutBit((v & 1) == 0, 145);
} }
} }
else else
@ -462,7 +462,7 @@ internal class Vp8BitWriter : BitWriterBase
Vp8BitWriter bitWriterPartZero = new(expectedSize, this.enc); Vp8BitWriter bitWriterPartZero = new(expectedSize, this.enc);
// Partition #0 with header and partition sizes. // Partition #0 with header and partition sizes.
uint size0 = this.GeneratePartition0(bitWriterPartZero); uint size0 = bitWriterPartZero.GeneratePartition0();
uint vp8Size = WebpConstants.Vp8FrameHeaderSize + size0; uint vp8Size = WebpConstants.Vp8FrameHeaderSize + size0;
vp8Size += numBytes; vp8Size += numBytes;
@ -495,47 +495,47 @@ internal class Vp8BitWriter : BitWriterBase
} }
} }
private uint GeneratePartition0(Vp8BitWriter bitWriter) private uint GeneratePartition0()
{ {
bitWriter.PutBitUniform(0); // colorspace this.PutBitUniform(0); // colorspace
bitWriter.PutBitUniform(0); // clamp type this.PutBitUniform(0); // clamp type
this.WriteSegmentHeader(bitWriter); this.WriteSegmentHeader();
this.WriteFilterHeader(bitWriter); this.WriteFilterHeader();
bitWriter.PutBits(0, 2); this.PutBits(0, 2);
this.WriteQuant(bitWriter); this.WriteQuant();
bitWriter.PutBitUniform(0); this.PutBitUniform(0);
this.WriteProbas(bitWriter); this.WriteProbas();
this.CodeIntraModes(bitWriter); this.CodeIntraModes();
bitWriter.Finish(); this.Finish();
return (uint)bitWriter.NumBytes(); return (uint)this.NumBytes();
} }
private void WriteSegmentHeader(Vp8BitWriter bitWriter) private void WriteSegmentHeader()
{ {
Vp8EncSegmentHeader hdr = this.enc.SegmentHeader; Vp8EncSegmentHeader hdr = this.enc.SegmentHeader;
Vp8EncProba proba = this.enc.Proba; Vp8EncProba proba = this.enc.Proba;
if (bitWriter.PutBitUniform(hdr.NumSegments > 1 ? 1 : 0) != 0) if (this.PutBitUniform(hdr.NumSegments > 1 ? 1 : 0) != 0)
{ {
// We always 'update' the quant and filter strength values. // We always 'update' the quant and filter strength values.
int updateData = 1; int updateData = 1;
bitWriter.PutBitUniform(hdr.UpdateMap ? 1 : 0); this.PutBitUniform(hdr.UpdateMap ? 1 : 0);
if (bitWriter.PutBitUniform(updateData) != 0) if (this.PutBitUniform(updateData) != 0)
{ {
// We always use absolute values, not relative ones. // We always use absolute values, not relative ones.
bitWriter.PutBitUniform(1); // (segment_feature_mode = 1. Paragraph 9.3.) this.PutBitUniform(1); // (segment_feature_mode = 1. Paragraph 9.3.)
for (int s = 0; s < WebpConstants.NumMbSegments; ++s) for (int s = 0; s < WebpConstants.NumMbSegments; ++s)
{ {
bitWriter.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7); this.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7);
} }
for (int s = 0; s < WebpConstants.NumMbSegments; ++s) for (int s = 0; s < WebpConstants.NumMbSegments; ++s)
{ {
bitWriter.PutSignedBits(this.enc.SegmentInfos[s].FStrength, 6); this.PutSignedBits(this.enc.SegmentInfos[s].FStrength, 6);
} }
} }
@ -543,50 +543,50 @@ internal class Vp8BitWriter : BitWriterBase
{ {
for (int s = 0; s < 3; ++s) for (int s = 0; s < 3; ++s)
{ {
if (bitWriter.PutBitUniform(proba.Segments[s] != 255 ? 1 : 0) != 0) if (this.PutBitUniform(proba.Segments[s] != 255 ? 1 : 0) != 0)
{ {
bitWriter.PutBits(proba.Segments[s], 8); this.PutBits(proba.Segments[s], 8);
} }
} }
} }
} }
} }
private void WriteFilterHeader(Vp8BitWriter bitWriter) private void WriteFilterHeader()
{ {
Vp8FilterHeader hdr = this.enc.FilterHeader; Vp8FilterHeader hdr = this.enc.FilterHeader;
bool useLfDelta = hdr.I4x4LfDelta != 0; bool useLfDelta = hdr.I4x4LfDelta != 0;
bitWriter.PutBitUniform(hdr.Simple ? 1 : 0); this.PutBitUniform(hdr.Simple ? 1 : 0);
bitWriter.PutBits((uint)hdr.FilterLevel, 6); this.PutBits((uint)hdr.FilterLevel, 6);
bitWriter.PutBits((uint)hdr.Sharpness, 3); this.PutBits((uint)hdr.Sharpness, 3);
if (bitWriter.PutBitUniform(useLfDelta ? 1 : 0) != 0) if (this.PutBitUniform(useLfDelta ? 1 : 0) != 0)
{ {
// '0' is the default value for i4x4LfDelta at frame #0. // '0' is the default value for i4x4LfDelta at frame #0.
bool needUpdate = hdr.I4x4LfDelta != 0; bool needUpdate = hdr.I4x4LfDelta != 0;
if (bitWriter.PutBitUniform(needUpdate ? 1 : 0) != 0) if (this.PutBitUniform(needUpdate ? 1 : 0) != 0)
{ {
// we don't use refLfDelta => emit four 0 bits. // we don't use refLfDelta => emit four 0 bits.
bitWriter.PutBits(0, 4); this.PutBits(0, 4);
// we use modeLfDelta for i4x4 // we use modeLfDelta for i4x4
bitWriter.PutSignedBits(hdr.I4x4LfDelta, 6); this.PutSignedBits(hdr.I4x4LfDelta, 6);
bitWriter.PutBits(0, 3); // all others unused. this.PutBits(0, 3); // all others unused.
} }
} }
} }
// Nominal quantization parameters // Nominal quantization parameters
private void WriteQuant(Vp8BitWriter bitWriter) private void WriteQuant()
{ {
bitWriter.PutBits((uint)this.enc.BaseQuant, 7); this.PutBits((uint)this.enc.BaseQuant, 7);
bitWriter.PutSignedBits(this.enc.DqY1Dc, 4); this.PutSignedBits(this.enc.DqY1Dc, 4);
bitWriter.PutSignedBits(this.enc.DqY2Dc, 4); this.PutSignedBits(this.enc.DqY2Dc, 4);
bitWriter.PutSignedBits(this.enc.DqY2Ac, 4); this.PutSignedBits(this.enc.DqY2Ac, 4);
bitWriter.PutSignedBits(this.enc.DqUvDc, 4); this.PutSignedBits(this.enc.DqUvDc, 4);
bitWriter.PutSignedBits(this.enc.DqUvAc, 4); this.PutSignedBits(this.enc.DqUvAc, 4);
} }
private void WriteProbas(Vp8BitWriter bitWriter) private void WriteProbas()
{ {
Vp8EncProba probas = this.enc.Proba; Vp8EncProba probas = this.enc.Proba;
for (int t = 0; t < WebpConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
@ -599,25 +599,25 @@ internal class Vp8BitWriter : BitWriterBase
{ {
byte p0 = probas.Coeffs[t][b].Probabilities[c].Probabilities[p]; byte p0 = probas.Coeffs[t][b].Probabilities[c].Probabilities[p];
bool update = p0 != WebpLookupTables.DefaultCoeffsProba[t, b, c, p]; bool update = p0 != WebpLookupTables.DefaultCoeffsProba[t, b, c, p];
if (bitWriter.PutBit(update, WebpLookupTables.CoeffsUpdateProba[t, b, c, p])) if (this.PutBit(update, WebpLookupTables.CoeffsUpdateProba[t, b, c, p]))
{ {
bitWriter.PutBits(p0, 8); this.PutBits(p0, 8);
} }
} }
} }
} }
} }
if (bitWriter.PutBitUniform(probas.UseSkipProba ? 1 : 0) != 0) if (this.PutBitUniform(probas.UseSkipProba ? 1 : 0) != 0)
{ {
bitWriter.PutBits(probas.SkipProba, 8); this.PutBits(probas.SkipProba, 8);
} }
} }
// Writes the partition #0 modes (that is: all intra modes) // Writes the partition #0 modes (that is: all intra modes)
private void CodeIntraModes(Vp8BitWriter bitWriter) private void CodeIntraModes()
{ {
var it = new Vp8EncIterator(this.enc.YTop, this.enc.UvTop, this.enc.Nz, this.enc.MbInfo, this.enc.Preds, this.enc.TopDerr, this.enc.Mbw, this.enc.Mbh); Vp8EncIterator it = new(this.enc);
int predsWidth = this.enc.PredsWidth; int predsWidth = this.enc.PredsWidth;
do do
@ -627,18 +627,18 @@ internal class Vp8BitWriter : BitWriterBase
Span<byte> preds = it.Preds.AsSpan(predIdx); Span<byte> preds = it.Preds.AsSpan(predIdx);
if (this.enc.SegmentHeader.UpdateMap) if (this.enc.SegmentHeader.UpdateMap)
{ {
bitWriter.PutSegment(mb.Segment, this.enc.Proba.Segments); this.PutSegment(mb.Segment, this.enc.Proba.Segments);
} }
if (this.enc.Proba.UseSkipProba) if (this.enc.Proba.UseSkipProba)
{ {
bitWriter.PutBit(mb.Skip, this.enc.Proba.SkipProba); this.PutBit(mb.Skip, this.enc.Proba.SkipProba);
} }
if (bitWriter.PutBit(mb.MacroBlockType != 0, 145)) if (this.PutBit(mb.MacroBlockType != 0, 145))
{ {
// i16x16 // i16x16
bitWriter.PutI16Mode(preds[0]); this.PutI16Mode(preds[0]);
} }
else else
{ {
@ -649,7 +649,7 @@ internal class Vp8BitWriter : BitWriterBase
for (int x = 0; x < 4; x++) for (int x = 0; x < 4; x++)
{ {
byte[] probas = WebpLookupTables.ModesProba[topPred[x], left]; byte[] probas = WebpLookupTables.ModesProba[topPred[x], left];
left = bitWriter.PutI4Mode(it.Preds[predIdx + x], probas); left = this.PutI4Mode(it.Preds[predIdx + x], probas);
} }
topPred = it.Preds.AsSpan(predIdx); topPred = it.Preds.AsSpan(predIdx);
@ -657,7 +657,7 @@ internal class Vp8BitWriter : BitWriterBase
} }
} }
bitWriter.PutUvMode(mb.UvMode); this.PutUvMode(mb.UvMode);
} }
while (it.Next()); while (it.Next());
} }

5
src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs

@ -50,6 +50,11 @@ internal class Vp8EncIterator
private int uvTopIdx; private int uvTopIdx;
public Vp8EncIterator(Vp8Encoder enc)
: this(enc.YTop, enc.UvTop, enc.Nz, enc.MbInfo, enc.Preds, enc.TopDerr, enc.Mbw, enc.Mbh)
{
}
public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, sbyte[] topDerr, int mbw, int mbh) public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, sbyte[] topDerr, int mbw, int mbh)
{ {
this.YTop = yTop; this.YTop = yTop;

4
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -328,7 +328,7 @@ internal class Vp8Encoder : IDisposable
int yStride = width; int yStride = width;
int uvStride = (yStride + 1) >> 1; int uvStride = (yStride + 1) >> 1;
Vp8EncIterator it = new(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); Vp8EncIterator it = new(this);
Span<int> alphas = stackalloc int[WebpConstants.MaxAlpha + 1]; Span<int> alphas = stackalloc int[WebpConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha); this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
int totalMb = this.Mbw * this.Mbw; int totalMb = this.Mbw * this.Mbw;
@ -520,7 +520,7 @@ internal class Vp8Encoder : IDisposable
Span<byte> y = this.Y.GetSpan(); Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan(); Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.GetSpan(); Span<byte> v = this.V.GetSpan();
Vp8EncIterator it = new(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); Vp8EncIterator it = new(this);
long size = 0; long size = 0;
long sizeP0 = 0; long sizeP0 = 0;
long distortion = 0; long distortion = 0;

5
src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

@ -162,6 +162,11 @@ internal class WebpAnimationDecoder : IDisposable
features.AlphaChunkHeader = alphaChunkHeader; features.AlphaChunkHeader = alphaChunkHeader;
break; break;
case WebpChunkType.Vp8L: case WebpChunkType.Vp8L:
if (hasAlpha)
{
WebpThrowHelper.ThrowNotSupportedException("Alpha channel is not supported for lossless webp images.");
}
webpInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); webpInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features);
break; break;
default: default:

Loading…
Cancel
Save