Skip to content

Commit b89bc54

Browse files
committed
Add new member to WebpFrameMetadata
1 parent 5737e4a commit b89bc54

File tree

13 files changed

+102
-69
lines changed

13 files changed

+102
-69
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ protected static void WriteMetadataProfile(Stream stream, byte[]? metadataBytes,
231231
/// The background color is also used when the Disposal method is 1.
232232
/// </param>
233233
/// <param name="loopCount">The number of times to loop the animation. If it is 0, this means infinitely.</param>
234-
public static void WriteAnimationParameter(Stream stream, uint background, ushort loopCount)
234+
public static void WriteAnimationParameter(Stream stream, Color background, ushort loopCount)
235235
{
236236
Span<byte> buf = stackalloc byte[4];
237237
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.AnimationParameter);
238238
stream.Write(buf);
239239
BinaryPrimitives.WriteUInt32LittleEndian(buf, sizeof(uint) + sizeof(ushort));
240240
stream.Write(buf);
241-
BinaryPrimitives.WriteUInt32LittleEndian(buf, background);
241+
BinaryPrimitives.WriteUInt32LittleEndian(buf, background.ToRgba32().Rgba);
242242
stream.Write(buf);
243243
BinaryPrimitives.WriteUInt16LittleEndian(buf[..2], loopCount);
244244
stream.Write(buf[..2]);
@@ -249,7 +249,7 @@ public static void WriteAnimationParameter(Stream stream, uint background, ushor
249249
/// </summary>
250250
/// <param name="stream">The stream to write to.</param>
251251
/// <param name="animation">Animation frame data.</param>
252-
public static long WriteAnimationFrame(Stream stream, AnimationFrameData animation)
252+
public static long WriteAnimationFrame(Stream stream, WebpFrameData animation)
253253
{
254254
Span<byte> buf = stackalloc byte[4];
255255
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Animation);
@@ -262,6 +262,8 @@ public static long WriteAnimationFrame(Stream stream, AnimationFrameData animati
262262
WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, animation.Width - 1);
263263
WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, animation.Height - 1);
264264
WebpChunkParsingUtils.WriteUInt24LittleEndian(stream, animation.Duration);
265+
266+
// TODO: If we can clip the indexed frame for transparent bounds we can set properties here.
265267
byte flag = (byte)(((int)animation.BlendingMethod << 1) | (int)animation.DisposalMethod);
266268
stream.WriteByte(flag);
267269
return position;

src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ public Vp8LEncoder(
235235
/// </summary>
236236
public Vp8LHashChain HashChain { get; }
237237

238-
public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAnimation, uint background = 0, uint loopCount = 0)
238+
public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAnimation)
239239
where TPixel : unmanaged, IPixel<TPixel>
240240
{
241241
// Write bytes from the bitwriter buffer to the stream.
@@ -257,7 +257,8 @@ public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAni
257257

258258
if (hasAnimation)
259259
{
260-
BitWriterBase.WriteAnimationParameter(stream, background, (ushort)loopCount);
260+
WebpMetadata webpMetadata = metadata.GetWebpMetadata();
261+
BitWriterBase.WriteAnimationParameter(stream, webpMetadata.AnimationBackground, webpMetadata.AnimationLoopCount);
261262
}
262263
}
263264

@@ -304,11 +305,14 @@ public void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAnim
304305

305306
if (hasAnimation)
306307
{
307-
prevPosition = BitWriterBase.WriteAnimationFrame(stream, new AnimationFrameData
308+
WebpFrameMetadata frameMetadata = frame.Metadata.GetWebpMetadata();
309+
prevPosition = BitWriterBase.WriteAnimationFrame(stream, new WebpFrameData
308310
{
309311
Width = (uint)frame.Width,
310312
Height = (uint)frame.Height,
311-
Duration = frame.Metadata.GetWebpMetadata().FrameDuration
313+
Duration = frameMetadata.FrameDelay,
314+
BlendingMethod = frameMetadata.BlendMethod,
315+
DisposalMethod = frameMetadata.DisposalMethod
312316
});
313317
}
314318

@@ -547,7 +551,7 @@ private CrunchConfig[] EncoderAnalyze(ReadOnlySpan<uint> bgra, int width, int he
547551
EntropyIx entropyIdx = this.AnalyzeEntropy(bgra, width, height, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero);
548552

549553
bool doNotCache = false;
550-
List<CrunchConfig> crunchConfigs = new List<CrunchConfig>();
554+
List<CrunchConfig> crunchConfigs = new();
551555

552556
if (this.method == WebpEncodingMethod.BestQuality && this.quality == 100)
553557
{
@@ -641,8 +645,8 @@ private void EncodeImage(int width, int height, bool useCache, CrunchConfig conf
641645
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
642646

643647
this.bitWriter.Reset(bwInit);
644-
Vp8LHistogram tmpHisto = new Vp8LHistogram(cacheBits);
645-
List<Vp8LHistogram> histogramImage = new List<Vp8LHistogram>(histogramImageXySize);
648+
Vp8LHistogram tmpHisto = new(cacheBits);
649+
List<Vp8LHistogram> histogramImage = new(histogramImageXySize);
646650
for (int i = 0; i < histogramImageXySize; i++)
647651
{
648652
histogramImage.Add(new Vp8LHistogram(cacheBits));
@@ -839,7 +843,7 @@ private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8L
839843
refsTmp1,
840844
refsTmp2);
841845

842-
List<Vp8LHistogram> histogramImage = new List<Vp8LHistogram>
846+
List<Vp8LHistogram> histogramImage = new()
843847
{
844848
new Vp8LHistogram(cacheBits)
845849
};
@@ -941,7 +945,7 @@ private void StoreFullHuffmanCode(Span<HuffmanTree> huffTree, HuffmanTreeToken[]
941945
int i;
942946
byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes];
943947
short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes];
944-
HuffmanTreeCode huffmanCode = new HuffmanTreeCode
948+
HuffmanTreeCode huffmanCode = new()
945949
{
946950
NumSymbols = WebpConstants.CodeLengthCodes,
947951
CodeLengths = codeLengthBitDepth,
@@ -1192,7 +1196,7 @@ private EntropyIx AnalyzeEntropy(ReadOnlySpan<uint> bgra, int width, int height,
11921196
histo[(int)HistoIx.HistoBluePred * 256]++;
11931197
histo[(int)HistoIx.HistoAlphaPred * 256]++;
11941198

1195-
Vp8LBitEntropy bitEntropy = new Vp8LBitEntropy();
1199+
Vp8LBitEntropy bitEntropy = new();
11961200
for (int j = 0; j < (int)HistoIx.HistoTotal; j++)
11971201
{
11981202
bitEntropy.Init();
@@ -1318,7 +1322,7 @@ private bool AnalyzeAndCreatePalette(ReadOnlySpan<uint> bgra, int width, int hei
13181322
/// <returns>The number of palette entries.</returns>
13191323
private static int GetColorPalette(ReadOnlySpan<uint> bgra, int width, int height, Span<uint> palette)
13201324
{
1321-
HashSet<uint> colors = new HashSet<uint>();
1325+
HashSet<uint> colors = new();
13221326
for (int y = 0; y < height; y++)
13231327
{
13241328
ReadOnlySpan<uint> bgraRow = bgra.Slice(y * width, width);
@@ -1870,9 +1874,9 @@ public void AllocateTransformBuffer(int width, int height)
18701874
/// </summary>
18711875
public void ClearRefs()
18721876
{
1873-
for (int i = 0; i < this.Refs.Length; i++)
1877+
foreach (Vp8LBackwardRefs t in this.Refs)
18741878
{
1875-
this.Refs[i].Refs.Clear();
1879+
t.Refs.Clear();
18761880
}
18771881
}
18781882

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ public Vp8Encoder(
309309
/// </summary>
310310
private int MbHeaderLimit { get; }
311311

312-
public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAlpha, bool hasAnimation, uint background = 0, uint loopCount = 0)
312+
public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAlpha, bool hasAnimation)
313313
where TPixel : unmanaged, IPixel<TPixel>
314314
{
315315
// Write bytes from the bitwriter buffer to the stream.
@@ -331,7 +331,8 @@ public void EncodeHeader<TPixel>(Image<TPixel> image, Stream stream, bool hasAlp
331331

332332
if (hasAnimation)
333333
{
334-
BitWriterBase.WriteAnimationParameter(stream, background, (ushort)loopCount);
334+
WebpMetadata webpMetadata = metadata.GetWebpMetadata();
335+
BitWriterBase.WriteAnimationParameter(stream, webpMetadata.AnimationBackground, webpMetadata.AnimationLoopCount);
335336
}
336337
}
337338

@@ -395,7 +396,7 @@ private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAni
395396
int yStride = width;
396397
int uvStride = (yStride + 1) >> 1;
397398

398-
Vp8EncIterator it = new Vp8EncIterator(this);
399+
Vp8EncIterator it = new(this);
399400
Span<int> alphas = stackalloc int[WebpConstants.MaxAlpha + 1];
400401
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
401402
int totalMb = this.Mbw * this.Mbw;
@@ -416,8 +417,8 @@ private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAni
416417
this.StatLoop(width, height, yStride, uvStride);
417418
it.Init();
418419
Vp8EncIterator.InitFilter();
419-
Vp8ModeScore info = new Vp8ModeScore();
420-
Vp8Residual residual = new Vp8Residual();
420+
Vp8ModeScore info = new();
421+
Vp8Residual residual = new();
421422
do
422423
{
423424
bool dontUseSkip = !this.Proba.UseSkipProba;
@@ -474,11 +475,14 @@ private void Encode<TPixel>(ImageFrame<TPixel> frame, Stream stream, bool hasAni
474475

475476
if (hasAnimation)
476477
{
477-
prevPosition = BitWriterBase.WriteAnimationFrame(stream, new AnimationFrameData
478+
WebpFrameMetadata frameMetadata = frame.Metadata.GetWebpMetadata();
479+
prevPosition = BitWriterBase.WriteAnimationFrame(stream, new WebpFrameData
478480
{
479481
Width = (uint)frame.Width,
480482
Height = (uint)frame.Height,
481-
Duration = frame.Metadata.GetWebpMetadata().FrameDuration
483+
Duration = frameMetadata.FrameDelay,
484+
BlendingMethod = frameMetadata.BlendMethod,
485+
DisposalMethod = frameMetadata.DisposalMethod
482486
});
483487
}
484488

@@ -529,7 +533,7 @@ private void StatLoop(int width, int height, int yStride, int uvStride)
529533
Vp8RdLevel rdOpt = this.method >= WebpEncodingMethod.Level3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
530534
int nbMbs = this.Mbw * this.Mbh;
531535

532-
PassStats stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality);
536+
PassStats stats = new(targetSize, targetPsnr, QMin, QMax, this.quality);
533537
this.Proba.ResetTokenStats();
534538

535539
// Fast mode: quick analysis pass over few mbs. Better than nothing.
@@ -597,15 +601,15 @@ private long OneStatPass(int width, int height, int yStride, int uvStride, Vp8Rd
597601
Span<byte> y = this.Y.GetSpan();
598602
Span<byte> u = this.U.GetSpan();
599603
Span<byte> v = this.V.GetSpan();
600-
Vp8EncIterator it = new Vp8EncIterator(this);
604+
Vp8EncIterator it = new(this);
601605
long size = 0;
602606
long sizeP0 = 0;
603607
long distortion = 0;
604608
long pixelCount = nbMbs * 384;
605609

606610
it.Init();
607611
this.SetLoopParams(stats.Q);
608-
Vp8ModeScore info = new Vp8ModeScore();
612+
Vp8ModeScore info = new();
609613
do
610614
{
611615
info.Clear();
@@ -1167,7 +1171,7 @@ private void CodeResiduals(Vp8EncIterator it, Vp8ModeScore rd, Vp8Residual resid
11671171
private void RecordResiduals(Vp8EncIterator it, Vp8ModeScore rd)
11681172
{
11691173
int x, y, ch;
1170-
Vp8Residual residual = new Vp8Residual();
1174+
Vp8Residual residual = new();
11711175
bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
11721176

11731177
it.NzToBytes();

src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, WebpFeatures feat
138138
private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? image, ref ImageFrame<TPixel>? previousFrame, uint width, uint height, Color backgroundColor)
139139
where TPixel : unmanaged, IPixel<TPixel>
140140
{
141-
AnimationFrameData frameData = AnimationFrameData.Parse(stream);
141+
WebpFrameData frameData = WebpFrameData.Parse(stream);
142142
long streamStartPosition = stream.Position;
143143
Span<byte> buffer = stackalloc byte[4];
144144

@@ -153,7 +153,7 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
153153
}
154154

155155
WebpImageInfo? webpInfo = null;
156-
WebpFeatures features = new WebpFeatures();
156+
WebpFeatures features = new();
157157
switch (chunkType)
158158
{
159159
case WebpChunkType.Vp8:
@@ -180,15 +180,15 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
180180
{
181181
image = new Image<TPixel>(this.configuration, (int)width, (int)height, backgroundColor.ToPixel<TPixel>(), this.metadata);
182182

183-
SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData.Duration);
183+
SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData);
184184

185185
imageFrame = image.Frames.RootFrame;
186186
}
187187
else
188188
{
189189
currentFrame = image!.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
190190

191-
SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
191+
SetFrameMetadata(currentFrame.Metadata, frameData);
192192

193193
imageFrame = currentFrame;
194194
}
@@ -199,15 +199,15 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
199199
int frameHeight = (int)frameData.Height;
200200
Rectangle regionRectangle = Rectangle.FromLTRB(frameX, frameY, frameX + frameWidth, frameY + frameHeight);
201201

202-
if (frameData.DisposalMethod is AnimationDisposalMethod.Dispose)
202+
if (frameData.DisposalMethod is WebpDisposalMethod.Dispose)
203203
{
204204
this.RestoreToBackground(imageFrame, backgroundColor);
205205
}
206206

207207
using Buffer2D<TPixel> decodedImage = this.DecodeImageData<TPixel>(frameData, webpInfo);
208208
DrawDecodedImageOnCanvas(decodedImage, imageFrame, frameX, frameY, frameWidth, frameHeight);
209209

210-
if (previousFrame != null && frameData.BlendingMethod is AnimationBlendingMethod.AlphaBlending)
210+
if (previousFrame != null && frameData.BlendingMethod is WebpBlendingMethod.AlphaBlending)
211211
{
212212
this.AlphaBlend(previousFrame, imageFrame, frameX, frameY, frameWidth, frameHeight);
213213
}
@@ -222,12 +222,13 @@ private uint ReadFrame<TPixel>(BufferedReadStream stream, ref Image<TPixel>? ima
222222
/// Sets the frames metadata.
223223
/// </summary>
224224
/// <param name="meta">The metadata.</param>
225-
/// <param name="duration">The frame duration.</param>
226-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
227-
private static void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
225+
/// <param name="frameData">The frame data.</param>
226+
private static void SetFrameMetadata(ImageFrameMetadata meta, WebpFrameData frameData)
228227
{
229228
WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
230-
frameMetadata.FrameDuration = duration;
229+
frameMetadata.FrameDelay = frameData.Duration;
230+
frameMetadata.BlendMethod = frameData.BlendingMethod;
231+
frameMetadata.DisposalMethod = frameData.DisposalMethod;
231232
}
232233

233234
/// <summary>
@@ -256,24 +257,24 @@ private byte ReadAlphaData(BufferedReadStream stream)
256257
/// <param name="frameData">The frame data.</param>
257258
/// <param name="webpInfo">The webp information.</param>
258259
/// <returns>A decoded image.</returns>
259-
private Buffer2D<TPixel> DecodeImageData<TPixel>(AnimationFrameData frameData, WebpImageInfo webpInfo)
260+
private Buffer2D<TPixel> DecodeImageData<TPixel>(WebpFrameData frameData, WebpImageInfo webpInfo)
260261
where TPixel : unmanaged, IPixel<TPixel>
261262
{
262-
Image<TPixel> decodedImage = new Image<TPixel>((int)frameData.Width, (int)frameData.Height);
263+
Image<TPixel> decodedImage = new((int)frameData.Width, (int)frameData.Height);
263264

264265
try
265266
{
266267
Buffer2D<TPixel> pixelBufferDecoded = decodedImage.GetRootFramePixelBuffer();
267268
if (webpInfo.IsLossless)
268269
{
269270
WebpLosslessDecoder losslessDecoder =
270-
new WebpLosslessDecoder(webpInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
271+
new(webpInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
271272
losslessDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height);
272273
}
273274
else
274275
{
275276
WebpLossyDecoder lossyDecoder =
276-
new WebpLossyDecoder(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
277+
new(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
277278
lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.alphaData);
278279
}
279280

src/ImageSharp/Formats/Webp/AnimationBlendingMethod.cs renamed to src/ImageSharp/Formats/Webp/WebpBlendingMethod.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
66
/// <summary>
77
/// Indicates how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
88
/// </summary>
9-
internal enum AnimationBlendingMethod
9+
public enum WebpBlendingMethod
1010
{
1111
/// <summary>
1212
/// Use alpha blending. After disposing of the previous frame, render the current frame on the canvas using alpha-blending.

0 commit comments

Comments
 (0)