我正在尝试使用 SixLabors ImageSharp 创建动画 GIF 到 PNG 转换器。我找到了这个关于创建 gif 的文档,但我想要相反的操作。
我在文档中找不到如何将字节块复制到目标图像。
我非常确定有一种比使用逐字节方法更有效的方法将字节块复制到输出图像。
这是我的尝试,但我遇到了问题
WriteBytesToImage
public static class GifToSpritesheet
{
public static async Task<Image> Convert(Image gif)
{
var size = gif.Size();
var nbFrames = gif.Frames.Count;
var resultWidth = size.Width * nbFrames;
var resultHeight = size.Height;
var result = new Image<Rgba32>(resultWidth, resultHeight);
int xOffset = 0;
foreach (var frame in gif.Frames.Cast<ImageFrame<Rgba32>>())
{
var bytes = await GetBytesFromFrameAsync(frame);
WriteBytesToImage(bytes, resultWidth, result);
}
return null;
}
// Src : https://khalidabuhakmeh.com/gifs-in-console-output-imagesharp-and-spectreconsole
private static async Task<byte[]> GetBytesFromFrameAsync(ImageFrame<Rgba32> imageFrame, CancellationTokenSource cancellationTokenSource = null)
{
using var image = new Image<Rgba32>(imageFrame.Width, imageFrame.Height);
for (var y = 0; y < image.Height; y++)
{
for (var x = 0; x < image.Width; x++)
{
image[x, y] = imageFrame[x, y];
}
}
await using var memoryStream = new MemoryStream();
if (cancellationTokenSource != null)
await image.SaveAsBmpAsync(memoryStream, cancellationTokenSource.Token);
else
await image.SaveAsBmpAsync(memoryStream);
return memoryStream.ToArray();
}
private static void WriteBytesToImage(byte[] bytes, int width, Image<Rgba32> result)
{
for (int j = 0; j < result.Height; j++)
{
for (int i = 0; i < width; i++)
{
// TODO : Do the code
//result[j, i] = Rgba32(0, 0, 0, 0);
}
}
}
}
谢谢你
好的,我已经设法将 gif 动画压缩为单个图像。
这是完整的代码:
public static class GifToSpritesheet
{
public static async Task<Image> Convert(Image gif)
{
var size = gif.Size();
var nbFrames = gif.Frames.Count;
var resultWidth = size.Width * nbFrames;
var resultHeight = size.Height;
var result = new Image<Rgba32>(resultWidth, resultHeight);
int xOffset = 0;
foreach (var frame in gif.Frames.Cast<ImageFrame<Rgba32>>())
{
await Task.Run(() => WriteFrameToImage(frame, result, xOffset));
xOffset += frame.Width;
}
return result;
}
private static void WriteFrameToImage(ImageFrame<Rgba32> imageFrame, Image<Rgba32> result, int xOffset = 0, int yOffset = 0)
{
Parallel.For(0, imageFrame.Height, (y, state) =>
{
Parallel.For(0, imageFrame.Width, (x, stateCol) =>
{
result[x + xOffset, y + yOffset] = imageFrame[x, y];
});
});
}
}
我发现这段代码很有用,所以我尝试改进它。以下是根据 @JamesSouth 建议进行的更改。不提供任何保证。
private static void Extract(ImageFrame<Rgba32> sourceImage, Image<Rgba32> targetImage, int xOffset = 0)
{
sourceImage.ProcessPixelRows(targetImage.Frames[0], (sourceAccessor, targetAccessor) =>
{
for (int i = 0; i < sourceImage.Height; i++)
{
Span<Rgba32> sourceRow = sourceAccessor.GetRowSpan(i);
Span<Rgba32> targetRow = targetAccessor.GetRowSpan(i).Slice(xOffset);
sourceRow.CopyTo(targetRow);
}
});
}