Windows Runtime中的WriteableBitmap小Bug

如果你在WPF、Silverlight、Windows Store App或Windows Phone中开发过图形处理相关的功能,那么一定不会对WriteableBitmap感到陌生。WriteableBitmap允许你通过PixelBuffer属性来手工操作位图的所有像素,你可以在上面绘图,也可以加载一张图片,然后修改它的像素颜色。

最近一段时间一直在维护一个Windows Phone 8.1的应用,基于Windows Runtime SDK。某日在解决错误的时候发现了一个Bug,虽然这个Bug在一定程度上和我的错用也有关系,但从API设计的角度而言,它确确实实应当被视作Bug。

下面来重现一下这个Bug。

先实例化一个WriteableBitmap,稍后会为其设置图片源,其时由于并不知道图片源的尺寸,所以预估了一个尺寸来实例化:

var wbmp = new WriteableBitmap(480,640); 

此时它的PixelWidth和PixelHeight分别为480和640。

接着获取到了图片源的流,将其填入刚才实例化的WriteableBitmap:

await wbmp.SetSourceAsync(stream);

需要注意的是,此时stream所表示的图片源的尺寸并非480x640,这里就假设它是960x1280。理论上这样的填充是不会成功的,因为该WriteableBitmap的预设尺寸不足以容纳图片源的所有数据。大抵是基于使用方便的考虑,WriteableBitmap允许这样的操作,图片源会顺利填充进去,WriteableBitmap的PixelWidth和PixelWidth也会分别更新为图片源的实际尺寸,既960x1280。并且将该WriteableBitmap指派给Image的话,图片也会正常展示出来。

显然WriteableBitmap旨在提供一定程度上的宽容度,以方便那些不太方便提前获取图片尺寸的使用场景(比如直接展示来自互联网的图片),然而WriteableBitmap有一点小纰漏,也正是这个小纰漏导致了我所遇到的问题。

前边提到过我们可以通过WriteableBitmap的PixelBuffer来操作它的像素数据,然而通过以上步骤填充了非预设尺寸的图片源之后,我们期望PixelBuffer里包含的是新图片源的所有数据,毕竟它已经根据新图片源更新了自身的尺寸,而且也能正常展示,我们没有理由怀疑PixelBuffer的数据是错误的,然而偏偏Bug就藏在这里。

PixelBuffer是一个Byte数组,里边存储了每一个像素的颜色信息,每个像素会通过四个byte来表示,分别对应于该像素颜色的A、R、G、B信息。所以PixelBuffer的大小就可以通过下面的公式计算出来:

length = width x height x 4

那么经过上面的填充步骤之后,我们期望的PixelBuffer大小就应当是:

960 * 1280 * 4 = 4915200

然而实际上该WriteableBitmap的PixelBuffer却只有1228800的大小,这个大小是如何产生的呢?

1228800 = 480 * 640 * 4

其实我相信WriteableBitmap内部存储的PixelBuffer是足量的,否则它没有办法正常展示完整的图片,这个Bug的产生大概是因为PixelBuffer的尺寸并不是实时计算返回的,而是在实例化的时候一次性计算好,存储在一个私有变量里。而在填充图片源时,应当像更新PixelWidth一样更新这个尺寸信息,然而程序员一时粗心,忘记了。

衷心希望他能在未来某一天喝咖啡的时候想起这件事情来。

发表评论

电子邮件地址不会被公开。 必填项已用*标注