实现 APNG 渲染函数
Posted
技术标签:
【中文标题】实现 APNG 渲染函数【英文标题】:Implementing the APNG Render Function 【发布时间】:2011-09-07 03:34:47 【问题描述】:大家好, 所以,我目前正在尝试实现APNG Specification,但在帧渲染方面遇到了一些问题。我的功能是
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;
我认为这基本上是正确的,因为我已经针对APNG Gallery 中的所有图像运行了它。但是,其中一些渲染不正确(This 一个有伪影问题,this 一个没有始终如一地保留左侧的红色条)。请注意,您可以在 Firefox 3 或更高版本中查看页面以查看动画。
我相信这个问题与我处理 DISPOSE_OP_PREVIOUS 的方式有关,但我不知道我做错了什么。谁能建议我可能缺少什么?
【问题讨论】:
【参考方案1】:所以,我最终能够弄清楚,并将在此处发布,以防将来有人遇到类似问题。事实证明,这个问题主要有三个方面:
-
如果帧类型为“上一个”,则不应更改“上一个缓冲区”
应该使用前一帧的 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。谢谢。 自从我上次从事这些工作已经有一段时间了,但我在这里编写了一些演示代码:github.com/murrple-1/APNGManagement。另外,我很确定我当时是 .net/C# 的新手,所以没有 NuGet 的依赖项(我应该在某个时候这样做) @dede 刚刚添加了 NuGet 支持。 APNGViewer 应用程序应该对您有用以上是关于实现 APNG 渲染函数的主要内容,如果未能解决你的问题,请参考以下文章