判断打印DIB图像像素值的函数是否正确且最优
问题
MS设备无关位图(DIB)的像素数组中,每行像素会被填充至4字节的倍数。我编写了如下函数,可在给定任意BitsPerPixel(1-32)、Width(>0)、Height(>0)及有效像素数组地址的情况下,打印每个像素的值(不含填充部分且不分离单个RGBA通道)。请问这是否是正确且最优的实现方式?
附:使用24bpp且Width==3n的情况对该函数压力最大。
#include <stdio.h> #include <windows.h> #define QWORD unsigned _int64 void PrintPixels(PDWORD pPixelArray, UINT Width, UINT Height, UINT BitsPerPixel) { UINT DwordsInStride = (BitsPerPixel * Width + 31) >> 5; QWORD mask = 0xFFFFFFFF >> (32 - BitsPerPixel); INT PixelBitsPerRow = Width * BitsPerPixel; for (UINT h = 0; h < Height; h++) { INT PixelBitsRemaining = PixelBitsPerRow; BYTE QwordBitsRemaining = 0; QWORD val = 0; do { val = ((QWORD)(*pPixelArray))<<QwordBitsRemaining | val; QwordBitsRemaining = (PixelBitsRemaining < 32+QwordBitsRemaining) ? PixelBitsRemaining : 32 + QwordBitsRemaining; do { printf("%0*llX ",(BitsPerPixel+3)>>2,val & mask ); //Don't account for the performance of this statement when judging the optimality of this function. Assume that the entire printf() statement executes infinitely quickly. val >>= BitsPerPixel; QwordBitsRemaining -= BitsPerPixel; PixelBitsRemaining -= BitsPerPixel; } while (QwordBitsRemaining >= BitsPerPixel); pPixelArray++; } while (PixelBitsRemaining > 0); printf("\n"); //Don't account for the performance of this statement when judging the optimality of this function. Assume that the entire printf() statement executes infinitely quickly. } }
分析与结论
正确性判断
你的实现存在明显错误,核心问题如下:
- Stride处理完全缺失:函数计算了
DwordsInStride但未使用。DIB每行实际长度是4字节对齐的,处理完一行有效像素后,必须跳过填充的字节(即让pPixelArray移动DwordsInStride个DWORD,而非仅移动处理过的像素对应DWORD数)。当前代码会在处理完一行有效像素后,继续读取下一行的开头数据当作当前行剩余部分,直接导致跨行读取错误。 - 掩码生成有未定义风险:当
BitsPerPixel=32时,0xFFFFFFFF >> (32 - 32)即右移32位,C标准中这种移位操作属于未定义行为。更安全的掩码生成方式是((QWORD)1 << BitsPerPixel) - 1。 - 跨DWORD像素的拼接顺序存疑:DIB采用小端存储,对于24bpp这类跨DWORD的像素格式,你的QWORD拼接逻辑是否符合像素实际字节顺序需要验证。比如24bpp时,第一个DWORD包含完整的像素1和像素2的第一个字节,第二个DWORD包含像素2的后两个字节和像素3的第一个字节,当前拼接逻辑可能无法正确提取像素值。
最优性判断(假设printf无性能损耗)
即便修正正确性问题,当前实现也绝非最优,原因包括:
- 双层循环冗余:内层do-while循环增加了分支判断开销,对于多数常见格式(8/16/24/32bpp),可以按固定字节/双字批量处理,大幅减少循环次数。
- 统一QWORD处理的浪费:对于32bpp、宽度为偶数的16bpp这类能对齐到DWORD的格式,完全不需要QWORD拼接,直接读取DWORD即可,当前统一逻辑会增加不必要的移位和或操作。
- 高压力场景(24bpp,Width=3n)适配差:这种场景下每行刚好是3n字节(无填充或填充1字节),按字节直接组合像素值的效率远高于当前的QWORD拼接方式。
修正与优化方向
- 修复Stride处理:每行处理完有效像素后,将
pPixelArray移动DwordsInStride个DWORD,比如在每行循环末尾执行:UINT processedDwords = (PixelBitsPerRow + 31) >> 5; pPixelArray += DwordsInStride - processedDwords; - 修正掩码生成:替换为
QWORD mask = ((QWORD)1 << BitsPerPixel) - 1; - 分格式优化:
- 32bpp:直接遍历每个DWORD打印,无需任何拼接操作。
- 16bpp:宽度为偶数时直接读取WORD;奇数时单独处理最后一个像素。
- 24bpp:按字节读取,每3字节组合成一个像素值(注意小端存储的字节顺序)。
- 1/4/8bpp:按字节或DWORD批量读取,一次性提取多个像素,减少循环次数。
内容的提问来源于stack exchange,提问作者George Robinson
相关产品推荐
相关产品推荐

