C# 实现APNG渲染功能

C# 实现APNG渲染功能,c#,winforms,render,apng,C#,Winforms,Render,Apng,大家好,, 因此,我目前正在尝试实现,但在帧渲染方面遇到了一些问题。我的职能是 private void UpdateUI() { foreach (PictureBox pb in pics) { APNGBox box = (APNGBox)pb.Tag; APNGLib.APNG png = box.png; if (box.buffer == null)

大家好,, 因此,我目前正在尝试实现,但在帧渲染方面遇到了一些问题。我的职能是

    private void UpdateUI()
    {
        foreach (PictureBox pb in pics)
        {
            APNGBox box = (APNGBox)pb.Tag;
            APNGLib.APNG png = box.png;
            if (box.buffer == null)
            {
                box.buffer = new Bitmap((int)png.Width, (int)png.Height);
            }
            APNGLib.Frame f = png.GetFrame(box.frameNum);
            using (Graphics g = Graphics.FromImage(box.buffer))
            {
                switch (f.DisposeOp)
                {
                    case APNGLib.Frame.DisposeOperation.NONE:
                        break;
                    case APNGLib.Frame.DisposeOperation.BACKGROUND:
                        g.Clear(Color.Transparent);
                        break;
                    case APNGLib.Frame.DisposeOperation.PREVIOUS:
                        if (box.prevBuffer != null)
                        {
                            g.DrawImage(box.prevBuffer, Point.Empty);
                        }
                        else
                        {
                            g.Clear(Color.Transparent);
                        }
                        break;
                    default:
                        break;
                }
                Bitmap read = png.ToBitmap(box.frameNum++);
                switch (f.BlendOp)
                {
                    case APNGLib.Frame.BlendOperation.OVER:
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
                        break;
                    case APNGLib.Frame.BlendOperation.SOURCE:
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                        break;
                    default:
                        break;
                }
                g.DrawImage(read, new Point((int)f.XOffset, (int)f.YOffset));
            }

            box.prevBuffer = box.buffer;
            pb.Image = box.buffer;

            if (box.frameNum >= box.png.FrameCount)
            {
                box.frameNum = 0;
                box.buffer = null;
                box.prevBuffer = null;
            }
        }
    }
APNGBox

internal class APNGBox
    {
        public int frameNum = 0;
        public APNGLib.APNG png;
        public Bitmap buffer;
        public Bitmap prevBuffer;
    }
我认为这基本上是正确的,因为我已经将它运行在所有的图像中。但是,其中一些渲染不正确(一个存在伪影问题,另一个没有始终保留左侧的红色条)。请注意,您可以在Firefox3或更高版本中查看页面以查看动画


我相信这个问题与我如何处理之前的处置有关,但我不知道我做错了什么。有人能建议我可能遗漏了什么吗?

因此,我最终能够找到答案,并将在这里发布,以防将来有人遇到类似问题。事实证明,问题主要有三个方面:

  • 如果帧类型为“previous”,则不应更改“previous buffer”
  • 应该使用前一帧的dispose_op,而不是当前帧的dispose_op,来确定它是如何处理的
  • 应该确保只处理旧框架的区域,而不是整个框架
  • 新守则是:

        internal class APNGBox
        {
            public int frameNum { get; set; }
            public APNGLib.APNG apng { get; set; }
            public Bitmap buffer { get; set; }
            public Bitmap prevBuffer { get; set; }
    
            public APNGBox(APNGLib.APNG png)
            {
                frameNum = 0;
                apng = png;
                buffer = apng.ToBitmap(0);
                prevBuffer = null;
            }
        }
    
        private void UpdateUI()
        {
            foreach (PictureBox pb in pics)
            {
                APNGBox box = (APNGBox)pb.Tag;
                APNGLib.APNG png = box.apng;
                if (!png.IsAnimated)
                {
                    if (pb.Image == null)
                    {
                        pb.Image = png.ToBitmap();
                    }
                }
                else
                {
                    if (box.frameNum != png.FrameCount - 1)
                    {
                        Bitmap prev = box.prevBuffer == null ? null : new Bitmap(box.prevBuffer);
                        APNGLib.Frame f1 = png.GetFrame(box.frameNum);
                        if (f1.DisposeOp != APNGLib.Frame.DisposeOperation.PREVIOUS)
                        {
                            box.prevBuffer = new Bitmap(box.buffer);
                        }
                        DisposeBuffer(box.buffer, new Rectangle((int)f1.XOffset, (int)f1.YOffset, (int)f1.Width, (int)f1.Height), f1.DisposeOp, prev);
                        box.frameNum++;
                        APNGLib.Frame f2 = png.GetFrame(box.frameNum);
                        RenderNextFrame(box.buffer, new Point((int)f2.XOffset, (int)f2.YOffset), png.ToBitmap(box.frameNum), f2.BlendOp);
                    }
                    else
                    {
                        box.frameNum = 0;
                        box.prevBuffer = null;
                        ClearFrame(box.buffer);
                        RenderNextFrame(box.buffer, Point.Empty, png.ToBitmap(box.frameNum), APNGLib.Frame.BlendOperation.SOURCE);
                    }
                    pb.Invalidate();
                }
            }
        }
    
        private void DisposeBuffer(Bitmap buffer, Rectangle region, APNGLib.Frame.DisposeOperation dispose, Bitmap prevBuffer)
        {
            using (Graphics g = Graphics.FromImage(buffer))
            {
                g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
    
                Brush b = new SolidBrush(Color.Transparent);
                switch (dispose)
                {
                    case APNGLib.Frame.DisposeOperation.NONE:
                        break;
                    case APNGLib.Frame.DisposeOperation.BACKGROUND:
                        g.FillRectangle(b, region);
                        break;
                    case APNGLib.Frame.DisposeOperation.PREVIOUS:
                        if(prevBuffer != null)
                        {
                            g.FillRectangle(b, region);
                            g.DrawImage(prevBuffer, region, region, GraphicsUnit.Pixel);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    
        private void RenderNextFrame(Bitmap buffer, Point point, Bitmap nextFrame, APNGLib.Frame.BlendOperation blend)
        {
            using(Graphics g = Graphics.FromImage(buffer))
            {
                switch(blend)
                {
                    case APNGLib.Frame.BlendOperation.OVER:
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
                        break;
                    case APNGLib.Frame.BlendOperation.SOURCE:
                        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                        break;
                    default:
                        break;
                }
                g.DrawImage(nextFrame, point);
            }
        }
    
        private void ClearFrame(Bitmap buffer)
        {
            using(Graphics g = Graphics.FromImage(buffer))
            {
                g.Clear(Color.Transparent);
            }
        }
    

    您好,您是否有完整的工作源代码提供请?我想在我的winforms应用程序中显示apng。谢谢。我上次做这些已经有一段时间了,但我在这里编了一些演示:。另外,我很确定我当时是.net/C的新手,所以依赖项没有NuGet(不过我应该在某个时候这样做)@dede刚刚添加了NuGet支持。APNGViewer应用程序应该对您有用