7

最近,我试图回答另一个关于加载动画帧(位图和持续时间)的问题GIFs。代码可以在Pastenbin上找到。

在将此代码移入我的开发库之前对此代码进行额外测试时,我注意到这行代码存在问题:

//Get the times stored in the gif
//PropertyTagFrameDelay ((PROPID) 0x5100) comes from gdiplusimaging.h
//More info on http://msdn.microsoft.com/en-us/library/windows/desktop/ms534416(v=vs.85).aspx
var times = img.GetPropertyItem(0x5100).Value;

当在 Windows .Net 上使用它(例如 GIF)运行它时,数组的大小与动画中的帧数量相同,GIF并且填充了帧的持续时间。在这种情况下,一个字节 [20] 转换为 (BitConverter.ToInt32()) 5 个持续时间:

[75,0,0,0,125,0,0,0,125,0,0,0,125,0,0,0,250,0,0,0]

然而,在 MonoMac 上,同一示例 GIF 的这行代码返回 a byte[4],它仅转换为一个持续时间(第一个持续时间):

[75,0,0,0]

我对此进行了 10 种不同GIF's的测试,结果始终相同。在 Windows 上,所有持续时间都在 byte[] 中,而 MonoMac 只列出第一个持续时间:

[x,0,0,0]
[75,0,0,0]
[50,0,0,0]
[125,0,0,0]

查看 MonoSystem.Drawing.Image 源代码,长度似乎是在这个方法中设置的,它是一个GDI包装器:

status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,out propSize);

但是,我真的没有看到任何问题,而不是源代码和我的实现。我错过了什么还是这是一个错误?

4

2 回答 2

2

我也没有在单声道源中看到任何问题。如果您发布您尝试过的示例图像之一,那将会很有帮助。关于 GIF 图像格式的一个怪癖是包含帧时间的图形控制扩展块是可选的,并且可以在图像描述符之前省略。非零几率因此您的 GIF 文件只有一个适用于所有帧的 GCE,您应该对每一帧应用相同的帧时间。

请注意,您没有得到 4 个值,帧时间被编码为 32 位值,并且您在字节 [] 中看到了它的小端编码。您应该使用 BitConverter.ToInt32(),正如您在示例代码中所做的那样。

因此,我认为您可能应该改用它:

//convert 4 bit value to integer
var duration = BitConverter.ToInt32(times, 4*i % times.Length);

请注意,关于 GIF 帧还有另一个令人讨厌的实现细节,第 2 帧及更高帧不必与第 1 帧大小相同。每个帧都有一个元数据字段,描述应该对前一帧执行什么操作以将其与下一帧合并。我知道没有属性 ID 可以获取每个帧的帧偏移、大小和取消绘制方法。我认为您需要自己将每一帧渲染为位图以获得正确的图像序列。非常丑陋的细节,GIF需要死。

于 2013-12-24T17:26:09.190 回答
1

如果您查看 libgdiplus,您会看到属性总是从活动位图中读取:

if (gdip_bitmapdata_property_find_id(image->active_bitmap, propID, &index) != Ok) {

您可以通过调用Image.SelectActiveFrame设置活动位图,然后单声道将一一返回正确的持续时间。由于这与 Windows 不兼容,我将其称为单声道错误。作为一个简单的解决方法,您当然可以只检查数组长度并处理这两种情况。这比检查单声道要好,因为如果单声道得到修复,这将继续工作。

于 2014-06-10T14:30:33.983 回答