如何将两个位图帧合并为一个?

时间:2013-11-19 01:17:30

标签: wpf gif

我之前的问题是     Why GifBitmapDecoder play incomplete gif     ,现在我想在使用之前完成gif帧,如下所示:

GifBitmapDecoder _gifDecoder = new GifBitmapDecoder(
        new Uri("pack://application:,,,/Expression/f006.gif"), 
        BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

BitmapFrame preBF = null;
FrameInfo preFI = null;
foreach (BitmapFrame bf in gifDecoder.Frames)
{
    FrameInfo fi = GetFrameInfo(bf);

    if (fi.DisposalMethod == FrameDisposalMethod.Combine && preBF != null)
    {
        // TODO Find a way to combine bf and preBF to makeup a complete gif frame
    }

    preBF = bf;
    preFI = fi;

}

类FrameInfo和方法GetFrameInfo如下(感谢http://www.thomaslevesque.com/tag/gif/     ,但它在我的工作中效果不好,因为框架仍然不完整,而且WpfAnimatedGif.dll占用太多内存):

#region Get the frame information
private class FrameInfo
{
    public TimeSpan Delay { get; set; }
    public FrameDisposalMethod DisposalMethod { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }

    public Rect Rect
    {
    get { return new Rect(Left, Top, Width, Height); }
    }
}

private enum FrameDisposalMethod
{
    Replace = 0,
    Combine = 1,
    RestoreBackground = 2,
    RestorePrevious = 3
}

private static FrameInfo GetFrameInfo(BitmapFrame frame)
{
    var frameInfo = new FrameInfo
    {
    Delay = TimeSpan.FromMilliseconds(100),
    DisposalMethod = FrameDisposalMethod.Replace,
    Width = frame.PixelWidth,
    Height = frame.PixelHeight,
    Left = 0,
    Top = 0
    };

    BitmapMetadata metadata;
    try
    {
    metadata = frame.Metadata as BitmapMetadata;
    if (metadata != null)
    {
        const string delayQuery = "/grctlext/Delay";
        const string disposalQuery = "/grctlext/Disposal";
        const string widthQuery = "/imgdesc/Width";
        const string heightQuery = "/imgdesc/Height";
        const string leftQuery = "/imgdesc/Left";
        const string topQuery = "/imgdesc/Top";

        var delay = GetQueryOrNull<ushort>(metadata, delayQuery);
        if (delay.HasValue)
        frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);

        var disposal = GetQueryOrNull<byte>(metadata, disposalQuery);
        if (disposal.HasValue)
        frameInfo.DisposalMethod = (FrameDisposalMethod)disposal.Value;

        var width = GetQueryOrNull<ushort>(metadata, widthQuery);
        if (width.HasValue)
        frameInfo.Width = width.Value;

        var height = GetQueryOrNull<ushort>(metadata, heightQuery);
        if (height.HasValue)
        frameInfo.Height = height.Value;

        var left = GetQueryOrNull<ushort>(metadata, leftQuery);
        if (left.HasValue)
        frameInfo.Left = left.Value;

        var top = GetQueryOrNull<ushort>(metadata, topQuery);
        if (top.HasValue)
        frameInfo.Top = top.Value;
    }
    }
    catch (NotSupportedException)
    {
    }

    return frameInfo;
}

private static T? GetQueryOrNull<T>(BitmapMetadata metadata, string query)
    where T : struct
{
    if (metadata.ContainsQuery(query))
    {
    object value = metadata.GetQuery(query);
    if (value != null)
        return (T)value;
    }
    return null;
}
#endregion

那么,有没有办法实现TODO?

2 个答案:

答案 0 :(得分:1)

我不确定我是否理解你,尝试将图像绘制到DrawingVisual组件中,然后 将其转换为BitmapSource。然后创建编码器PngBitmapEncoder, 将BitmapSource添加到编码器。 当创建Rect和RenderTargetBitmap时,设置图像的高度和宽度。最后将结果保存到文件。

int imageWidth = bf.PixelWidth;
int imageHeight = bf.PixelHeight;

DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
    drawingContext.DrawImage(bf, new Rect(...));
    drawingContext.DrawImage(preBF , new Rect(...));
}

RenderTargetBitmap bmp = new RenderTargetBitmap(yourSize, yourSize, yourSize, yourSize, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);

PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));

using (Stream stream = File.Create(pathTileImage))
    encoder.Save(stream);

答案 1 :(得分:0)

我已实现它,但这种方式会导致太多内存:

private GifBitmapEncoder MakeupFrames(GifBitmapDecoder gifDecoder)
{
    BitmapFrame preBF = null;
    FrameInfo preFI = null;

    GifBitmapEncoder encoder = new GifBitmapEncoder();
    for (int i = 0; i < gifDecoder.Frames.Count; i++)
    {
    FrameInfo fi = GetFrameInfo(gifDecoder.Frames[i]);

    if (preBF == null)
        encoder.Frames.Add(gifDecoder.Frames[i]);

    else
    {
        DrawingVisual visual = new DrawingVisual();
        using (var context = visual.RenderOpen())
        {
        if (preBF != null && gifDecoder.Frames[i] != null &&
            fi.DisposalMethod != FrameDisposalMethod.Replace)
        {
            var fullRect = new Rect(0, 0, preBF.PixelWidth, preBF.PixelHeight);
            context.DrawImage(preBF, fullRect);
        }

        context.DrawImage(gifDecoder.Frames[i], 
            new Rect(fi.Left, fi.Top, fi.Width, fi.Height));
        }
        var bitmap = new RenderTargetBitmap(
        preBF.PixelWidth, preBF.PixelHeight,
        preBF.DpiX, preBF.DpiY,
        PixelFormats.Pbgra32);
        bitmap.Render(visual);
        encoder.Frames.Add(BitmapFrame.Create(bitmap));
        bitmap = null;
    }

    preBF = encoder.Frames[i];
    preFI = fi;
    }
    return encoder;
}