MonoMac System.Drawing.Image.GetPropertyItem(0x5100)
最近,我试图回答关于加载动画GIFs
的帧(位图和持续时间)的另一个SO问题。 代码可以在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;
当使用这个(例如GIF)在Windows.Net上运行时,该数组的大小与动画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的这一行代码返回一个仅转换为一个持续时间(第一个)的byte[4]
:
[75,0,0,0]
我测试了10个不同的GIF's
,结果总是一样的。 在Windows上,所有持续时间都在字节[]中,而MonoMac仅列出第一个持续时间:
[x,0,0,0]
[75,0,0,0]
[50,0,0,0]
[125,0,0,0]
查看单声道System.Drawing.Image
源代码,长度似乎是在这个方法中设置的,它是一个GDI包装器:
status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,out propSize);
然而,我并没有真正看到任何问题,与我的实施不一样。 我错过了什么或者这是一个错误?
如果你看看libgdiplus,你会看到这些属性总是从活动位图中读取:
if (gdip_bitmapdata_property_find_id(image->active_bitmap, propID, &index) != Ok) {
您可以通过调用Image.SelectActiveFrame来设置活动位图,然后mono将逐个返回正确的持续时间。 由于这与Windows不兼容,我称它为单声道错误。 作为一个简单的解决方法,您当然可以检查数组长度并处理这两种情况。 这将比单声道检查更好,因为如果单声道被固定,这将继续工作。
我在单声道源中看不到任何错误。 如果您发布了您尝试过的示例图像之一,这将会很有帮助。 关于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需要死亡。
链接地址: http://www.djcxy.com/p/14741.html