比较 16 bpp 到 32 bpp 位图转换

Compare 16 bpp to 32 bpp bitmap conversions

我得到了一个 16 bpp 的位图 ,我通过以下代码将其转换为 32 bpp:

void Rgb555ToRgb8(const UChar* bitmapData, UInt32 width, UInt32 height, UChar* buf)
{

    UInt32 dst_bytes_per_row = width * 4;
    UInt32 src_bytes_per_row = ((width * 16 + 31) / 32) * 4;

    UInt16 red_mask = 0x7C00;
    UInt16 green_mask = 0x3E0;
    UInt16 blue_mask = 0x1F;

    for (UInt32 row = 0; row < height; ++row)
    {
        UInt32 dstCol = 0, srcCol = 0;

        do
        {
            UInt16 rgb = *(UInt16*)(bitmapData + row * src_bytes_per_row + srcCol);

            UChar red_value = (rgb & red_mask) >> 10;
            UChar green_value = (rgb & green_mask) >> 5;
            UChar blue_value = (rgb & blue_mask);

            buf[row*dst_bytes_per_row + dstCol] = blue_value << 3;
            buf[row*dst_bytes_per_row + dstCol + 1] = green_value << 3;
            buf[row*dst_bytes_per_row + dstCol + 2] = red_value << 3;
            buf[row*dst_bytes_per_row + dstCol + 3] = rgb >> 15;

            srcCol += 2;
            dstCol += 4;

        } while (srcCol < src_bytes_per_row);
    }
}

转换结果如下:[2]: https://i.stack.imgur.com/1ajO7.png

我还尝试通过 GdiPlus 转换此图像:

Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(w,h,PixelFormat32bppRGB);

生成的图像是

请注意,这 2 个结果看起来并不完全相同(例如,GdiPlus 结果中的背景是白色的)。如何修改我的代码以匹配 GdiPlus 结果?

有两个问题需要解决:

未使用的位

当从 5 位信息移动到 8 位信息时,您将获得额外的 3 位信息。实施时,代码不使用该额外范围,并且偏向于较暗的颜色分量。这是 blue_value << 3 实际作用的说明:

5 bits per channel    8 bits per channel

bbbbb              -> bbbbb000

为了解决这个问题,最低有效的 3 位需要随着通道值变高而增长。一个简单的(但 somewhat inaccurate)就是将最高有效的 3 位复制到最低有效的 3 位,即

buf[row*dst_bytes_per_row + dstCol]     = (blue_value  << 3) | (blue_value  >> 2);
buf[row*dst_bytes_per_row + dstCol + 1] = (green_value << 3) | (green_value >> 2);
buf[row*dst_bytes_per_row + dstCol + 2] = (red_value   << 3) | (red_value   >> 2);

确切的映射会更复杂一些,比如

blue_value = static_cast<UChar>((blue_value * 255.0) / 31.0 + 0.5);

从 5 位转换为最接近理想值的相应 8 位值,包括在上述位移解决方案中相差 1/255 的 4 个值。

如果您选择后者,您可以构建一个存储映射值的查找 table。这个 table 只有 32 个条目,每个条目一个字节,所以它适合一个缓存行。

Alpha 通道

假设源图像的 MSB 确实被解释为 alpha 值,您也将把它移到目标中。由于源只有 1 位信息,原始转换很简单:

buf[row*dst_bytes_per_row + dstCol + 3] = rgb & (1 << 15) ? 255 : 0;

这可能是也可能不是所需要的全部。 Windows 假定预乘 alpha,即颜色通道的存储值必须预乘 alpha 值(请参阅 BLENDFUNCTION 以供参考)。

如果 alpha 值为 255,则颜色通道值已经正确。如果 alpha 值为 0,则所有颜色通道都需要乘以零(或简单地设置为 0)。翻译不会产生任何其他 alpha 值。