diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
index d7787b3a00..4252f895b8 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
-using System.Drawing;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
@@ -181,7 +180,7 @@ internal abstract class BitWriterBase
/// The stream to write to.
/// Animation frame data.
/// Frame data.
- protected void WriteAnimationFrame(Stream stream, AnimationFrameData animation, byte[] data)
+ protected void WriteAnimationFrame(Stream stream, AnimationFrameData animation, Span data)
{
uint size = AnimationFrameData.HeaderSize + (uint)data.Length;
Span buf = this.scratchBuffer.Span[..4];
@@ -260,6 +259,14 @@ internal abstract class BitWriterBase
flags |= 8;
}
+ /*
+ if (isAnimated)
+ {
+ // Set animated flag.
+ flags |= 2;
+ }
+ */
+
if (xmpProfile != null)
{
// Set xmp bit.
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
index 597ecef42a..cd84f109eb 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
@@ -116,7 +116,7 @@ internal class Vp8BitWriter : BitWriterBase
else
{
this.PutBit(v >= 9, 165);
- this.PutBit(!((v & 1) != 0), 145);
+ this.PutBit((v & 1) == 0, 145);
}
}
else
@@ -462,7 +462,7 @@ internal class Vp8BitWriter : BitWriterBase
Vp8BitWriter bitWriterPartZero = new(expectedSize, this.enc);
// Partition #0 with header and partition sizes.
- uint size0 = this.GeneratePartition0(bitWriterPartZero);
+ uint size0 = bitWriterPartZero.GeneratePartition0();
uint vp8Size = WebpConstants.Vp8FrameHeaderSize + size0;
vp8Size += numBytes;
@@ -495,47 +495,47 @@ internal class Vp8BitWriter : BitWriterBase
}
}
- private uint GeneratePartition0(Vp8BitWriter bitWriter)
+ private uint GeneratePartition0()
{
- bitWriter.PutBitUniform(0); // colorspace
- bitWriter.PutBitUniform(0); // clamp type
+ this.PutBitUniform(0); // colorspace
+ this.PutBitUniform(0); // clamp type
- this.WriteSegmentHeader(bitWriter);
- this.WriteFilterHeader(bitWriter);
+ this.WriteSegmentHeader();
+ this.WriteFilterHeader();
- bitWriter.PutBits(0, 2);
+ this.PutBits(0, 2);
- this.WriteQuant(bitWriter);
- bitWriter.PutBitUniform(0);
- this.WriteProbas(bitWriter);
- this.CodeIntraModes(bitWriter);
+ this.WriteQuant();
+ this.PutBitUniform(0);
+ this.WriteProbas();
+ 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;
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.
int updateData = 1;
- bitWriter.PutBitUniform(hdr.UpdateMap ? 1 : 0);
- if (bitWriter.PutBitUniform(updateData) != 0)
+ this.PutBitUniform(hdr.UpdateMap ? 1 : 0);
+ if (this.PutBitUniform(updateData) != 0)
{
// 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)
{
- bitWriter.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7);
+ this.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7);
}
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)
{
- 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;
bool useLfDelta = hdr.I4x4LfDelta != 0;
- bitWriter.PutBitUniform(hdr.Simple ? 1 : 0);
- bitWriter.PutBits((uint)hdr.FilterLevel, 6);
- bitWriter.PutBits((uint)hdr.Sharpness, 3);
- if (bitWriter.PutBitUniform(useLfDelta ? 1 : 0) != 0)
+ this.PutBitUniform(hdr.Simple ? 1 : 0);
+ this.PutBits((uint)hdr.FilterLevel, 6);
+ this.PutBits((uint)hdr.Sharpness, 3);
+ if (this.PutBitUniform(useLfDelta ? 1 : 0) != 0)
{
// '0' is the default value for i4x4LfDelta at frame #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.
- bitWriter.PutBits(0, 4);
+ this.PutBits(0, 4);
// we use modeLfDelta for i4x4
- bitWriter.PutSignedBits(hdr.I4x4LfDelta, 6);
- bitWriter.PutBits(0, 3); // all others unused.
+ this.PutSignedBits(hdr.I4x4LfDelta, 6);
+ this.PutBits(0, 3); // all others unused.
}
}
}
// Nominal quantization parameters
- private void WriteQuant(Vp8BitWriter bitWriter)
+ private void WriteQuant()
{
- bitWriter.PutBits((uint)this.enc.BaseQuant, 7);
- bitWriter.PutSignedBits(this.enc.DqY1Dc, 4);
- bitWriter.PutSignedBits(this.enc.DqY2Dc, 4);
- bitWriter.PutSignedBits(this.enc.DqY2Ac, 4);
- bitWriter.PutSignedBits(this.enc.DqUvDc, 4);
- bitWriter.PutSignedBits(this.enc.DqUvAc, 4);
+ this.PutBits((uint)this.enc.BaseQuant, 7);
+ this.PutSignedBits(this.enc.DqY1Dc, 4);
+ this.PutSignedBits(this.enc.DqY2Dc, 4);
+ this.PutSignedBits(this.enc.DqY2Ac, 4);
+ this.PutSignedBits(this.enc.DqUvDc, 4);
+ this.PutSignedBits(this.enc.DqUvAc, 4);
}
- private void WriteProbas(Vp8BitWriter bitWriter)
+ private void WriteProbas()
{
Vp8EncProba probas = this.enc.Proba;
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];
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)
- 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;
do
@@ -627,18 +627,18 @@ internal class Vp8BitWriter : BitWriterBase
Span preds = it.Preds.AsSpan(predIdx);
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)
{
- 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
- bitWriter.PutI16Mode(preds[0]);
+ this.PutI16Mode(preds[0]);
}
else
{
@@ -649,7 +649,7 @@ internal class Vp8BitWriter : BitWriterBase
for (int x = 0; x < 4; x++)
{
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);
@@ -657,7 +657,7 @@ internal class Vp8BitWriter : BitWriterBase
}
}
- bitWriter.PutUvMode(mb.UvMode);
+ this.PutUvMode(mb.UvMode);
}
while (it.Next());
}
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
index 7211f93766..a7c96edb7c 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
@@ -50,6 +50,11 @@ internal class Vp8EncIterator
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)
{
this.YTop = yTop;
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index ce5d3bac11..c65099af88 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -328,7 +328,7 @@ internal class Vp8Encoder : IDisposable
int yStride = width;
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 alphas = stackalloc int[WebpConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
int totalMb = this.Mbw * this.Mbw;
@@ -520,7 +520,7 @@ internal class Vp8Encoder : IDisposable
Span y = this.Y.GetSpan();
Span u = this.U.GetSpan();
Span 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 sizeP0 = 0;
long distortion = 0;
diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index 65f654dddc..81a7aebdf9 100644
--- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
@@ -162,6 +162,11 @@ internal class WebpAnimationDecoder : IDisposable
features.AlphaChunkHeader = alphaChunkHeader;
break;
case WebpChunkType.Vp8L:
+ if (hasAlpha)
+ {
+ WebpThrowHelper.ThrowNotSupportedException("Alpha channel is not supported for lossless webp images.");
+ }
+
webpInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features);
break;
default: