Skip to content

Commit afe2133

Browse files
Merge pull request #2569 from Poker-sang/animated-webp-encoder
Animated webp encoder
2 parents 7e1ee9e + f461378 commit afe2133

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1306
-920
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers.Binary;
5+
using System.Text;
6+
7+
namespace SixLabors.ImageSharp.Common.Helpers;
8+
9+
internal static class RiffHelper
10+
{
11+
/// <summary>
12+
/// The header bytes identifying RIFF file.
13+
/// </summary>
14+
private const uint RiffFourCc = 0x52_49_46_46;
15+
16+
public static void WriteRiffFile(Stream stream, string formType, Action<Stream> func) =>
17+
WriteChunk(stream, RiffFourCc, s =>
18+
{
19+
s.Write(Encoding.ASCII.GetBytes(formType));
20+
func(s);
21+
});
22+
23+
public static void WriteChunk(Stream stream, uint fourCc, Action<Stream> func)
24+
{
25+
Span<byte> buffer = stackalloc byte[4];
26+
27+
// write the fourCC
28+
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
29+
stream.Write(buffer);
30+
31+
long sizePosition = stream.Position;
32+
stream.Position += 4;
33+
34+
func(stream);
35+
36+
long position = stream.Position;
37+
38+
uint dataSize = (uint)(position - sizePosition - 4);
39+
40+
// padding
41+
if (dataSize % 2 == 1)
42+
{
43+
stream.WriteByte(0);
44+
position++;
45+
}
46+
47+
BinaryPrimitives.WriteUInt32LittleEndian(buffer, dataSize);
48+
stream.Position = sizePosition;
49+
stream.Write(buffer);
50+
stream.Position = position;
51+
}
52+
53+
public static void WriteChunk(Stream stream, uint fourCc, ReadOnlySpan<byte> data)
54+
{
55+
Span<byte> buffer = stackalloc byte[4];
56+
57+
// write the fourCC
58+
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
59+
stream.Write(buffer);
60+
uint size = (uint)data.Length;
61+
BinaryPrimitives.WriteUInt32LittleEndian(buffer, size);
62+
stream.Write(buffer);
63+
stream.Write(data);
64+
65+
// padding
66+
if (size % 2 is 1)
67+
{
68+
stream.WriteByte(0);
69+
}
70+
}
71+
72+
public static unsafe void WriteChunk<TStruct>(Stream stream, uint fourCc, in TStruct chunk)
73+
where TStruct : unmanaged
74+
{
75+
fixed (TStruct* ptr = &chunk)
76+
{
77+
WriteChunk(stream, fourCc, new Span<byte>(ptr, sizeof(TStruct)));
78+
}
79+
}
80+
81+
public static long BeginWriteChunk(Stream stream, uint fourCc)
82+
{
83+
Span<byte> buffer = stackalloc byte[4];
84+
85+
// write the fourCC
86+
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourCc);
87+
stream.Write(buffer);
88+
89+
long sizePosition = stream.Position;
90+
stream.Position += 4;
91+
92+
return sizePosition;
93+
}
94+
95+
public static void EndWriteChunk(Stream stream, long sizePosition)
96+
{
97+
Span<byte> buffer = stackalloc byte[4];
98+
99+
long position = stream.Position;
100+
101+
uint dataSize = (uint)(position - sizePosition - 4);
102+
103+
// padding
104+
if (dataSize % 2 is 1)
105+
{
106+
stream.WriteByte(0);
107+
position++;
108+
}
109+
110+
BinaryPrimitives.WriteUInt32LittleEndian(buffer, dataSize);
111+
stream.Position = sizePosition;
112+
stream.Write(buffer);
113+
stream.Position = position;
114+
}
115+
116+
public static long BeginWriteRiffFile(Stream stream, string formType)
117+
{
118+
long sizePosition = BeginWriteChunk(stream, RiffFourCc);
119+
stream.Write(Encoding.ASCII.GetBytes(formType));
120+
return sizePosition;
121+
}
122+
123+
public static void EndWriteRiffFile(Stream stream, long sizePosition) => EndWriteChunk(stream, sizePosition);
124+
}

src/ImageSharp/Formats/Webp/AlphaDecoder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaCh
5959

6060
if (this.Compressed)
6161
{
62-
Vp8LBitReader bitReader = new(data);
62+
Vp8LBitReader bitReader = new Vp8LBitReader(data);
6363
this.LosslessDecoder = new WebpLosslessDecoder(bitReader, memoryAllocator, configuration);
6464
this.LosslessDecoder.DecodeImageStream(this.Vp8LDec, width, height, true);
6565

src/ImageSharp/Formats/Webp/AlphaEncoder.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,25 @@ internal static class AlphaEncoder
1919
/// Data is either compressed as lossless webp image or uncompressed.
2020
/// </summary>
2121
/// <typeparam name="TPixel">The pixel format.</typeparam>
22-
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
22+
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
2323
/// <param name="configuration">The global configuration.</param>
2424
/// <param name="memoryAllocator">The memory manager.</param>
2525
/// <param name="skipMetadata">Whether to skip metadata encoding.</param>
2626
/// <param name="compress">Indicates, if the data should be compressed with the lossless webp compression.</param>
2727
/// <param name="size">The size in bytes of the alpha data.</param>
2828
/// <returns>The encoded alpha data.</returns>
2929
public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
30-
Image<TPixel> image,
30+
ImageFrame<TPixel> frame,
3131
Configuration configuration,
3232
MemoryAllocator memoryAllocator,
3333
bool skipMetadata,
3434
bool compress,
3535
out int size)
3636
where TPixel : unmanaged, IPixel<TPixel>
3737
{
38-
int width = image.Width;
39-
int height = image.Height;
40-
IMemoryOwner<byte> alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
38+
int width = frame.Width;
39+
int height = frame.Height;
40+
IMemoryOwner<byte> alphaData = ExtractAlphaChannel(frame, configuration, memoryAllocator);
4141

4242
if (compress)
4343
{
@@ -58,9 +58,9 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
5858
// The transparency information will be stored in the green channel of the ARGB quadruplet.
5959
// The green channel is allowed extra transformation steps in the specification -- unlike the other channels,
6060
// that can improve compression.
61-
using Image<Rgba32> alphaAsImage = DispatchAlphaToGreen(image, alphaData.GetSpan());
61+
using ImageFrame<Rgba32> alphaAsFrame = DispatchAlphaToGreen(frame, alphaData.GetSpan());
6262

63-
size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, alphaData);
63+
size = lossLessEncoder.EncodeAlphaImageData(alphaAsFrame, alphaData);
6464

6565
return alphaData;
6666
}
@@ -73,19 +73,19 @@ public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
7373
/// Store the transparency in the green channel.
7474
/// </summary>
7575
/// <typeparam name="TPixel">The pixel format.</typeparam>
76-
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
76+
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
7777
/// <param name="alphaData">A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</param>
78-
/// <returns>The transparency image.</returns>
79-
private static Image<Rgba32> DispatchAlphaToGreen<TPixel>(Image<TPixel> image, Span<byte> alphaData)
78+
/// <returns>The transparency frame.</returns>
79+
private static ImageFrame<Rgba32> DispatchAlphaToGreen<TPixel>(ImageFrame<TPixel> frame, Span<byte> alphaData)
8080
where TPixel : unmanaged, IPixel<TPixel>
8181
{
82-
int width = image.Width;
83-
int height = image.Height;
84-
Image<Rgba32> alphaAsImage = new(width, height);
82+
int width = frame.Width;
83+
int height = frame.Height;
84+
ImageFrame<Rgba32> alphaAsFrame = new ImageFrame<Rgba32>(Configuration.Default, width, height);
8585

8686
for (int y = 0; y < height; y++)
8787
{
88-
Memory<Rgba32> rowBuffer = alphaAsImage.DangerousGetPixelRowMemory(y);
88+
Memory<Rgba32> rowBuffer = alphaAsFrame.DangerousGetPixelRowMemory(y);
8989
Span<Rgba32> pixelRow = rowBuffer.Span;
9090
Span<byte> alphaRow = alphaData.Slice(y * width, width);
9191
for (int x = 0; x < width; x++)
@@ -95,23 +95,23 @@ private static Image<Rgba32> DispatchAlphaToGreen<TPixel>(Image<TPixel> image, S
9595
}
9696
}
9797

98-
return alphaAsImage;
98+
return alphaAsFrame;
9999
}
100100

101101
/// <summary>
102102
/// Extract the alpha data of the image.
103103
/// </summary>
104104
/// <typeparam name="TPixel">The pixel format.</typeparam>
105-
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
105+
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
106106
/// <param name="configuration">The global configuration.</param>
107107
/// <param name="memoryAllocator">The memory manager.</param>
108108
/// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns>
109-
private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator)
109+
private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(ImageFrame<TPixel> frame, Configuration configuration, MemoryAllocator memoryAllocator)
110110
where TPixel : unmanaged, IPixel<TPixel>
111111
{
112-
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
113-
int height = image.Height;
114-
int width = image.Width;
112+
Buffer2D<TPixel> imageBuffer = frame.PixelBuffer;
113+
int height = frame.Height;
114+
int width = frame.Width;
115115
IMemoryOwner<byte> alphaDataBuffer = memoryAllocator.Allocate<byte>(width * height);
116116
Span<byte> alphaData = alphaDataBuffer.GetSpan();
117117

src/ImageSharp/Formats/Webp/AnimationFrameData.cs

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)