最近在捣鼓一个图像显示的小玩意儿,遇到个不大不小的问题,就是怎么把内存里的一块图像数据,按我想要的大小给画到窗口上去。我琢磨着用那个老哥们常用的 `BitBlt`。
试试 `BitBlt`,感觉有点不对劲。这哥们好像更适合直接从一个地方(比如另一个窗口或者内存 DC)原封不动搬图片到另一个地方。我要的是能伸缩的,把内存里那坨原始像素数据,直接按比例放大或者缩小怼到屏幕上。而且 `BitBlt` 好像跟设备关系挺大的,处理我这种纯内存里的数据(就是所谓的 DIB ),感觉有点绕。
找到 `StretchDIBits`
后来翻翻,找到 `StretchDIBits` 这个函数。瞅着名字就带劲,“Stretch” 不就是拉伸嘛“DIBits” 指的应该就是我内存里那堆像素点。感觉这玩意儿就是为我这需求量身定做的。
说干就干,开始动手实践:
第一步,肯定得先拿到要画的目标窗口的设备上下文,就是那个 HDC。没这个,跟谁对话去。
得定好在窗口的哪个位置开始画,就是左上角的坐标,`XDest` 和 `YDest` 这俩参数。
然后是关键,指定画出来要多大,就是目标矩形的宽度 `nWidth` 和高度 `nHeight`。`StretchDIBits` 会根据这个尺寸,自动帮你把来源图片拉伸或者压缩。
再然后,得准备好我内存里的图像数据。这包括:
图像的像素数据本身,得拿到指向这块内存的指针。
还有个描述图像信息的结构体,好像叫 `BITMAPINFO` 啥的,里面有图像的宽、高、颜色位数这些信息。`StretchDIBits` 需要知道这些才能正确解析像素数据。
一步,就是调用 `StretchDIBits` 。把前面准备好的东西一股脑塞给它:目标 HDC、目标坐标 X 和 Y、目标宽高、源图像从哪里开始抠(一般是 0, 0)、源图像抠多大(通常是整个源图像的宽高)、指向像素数据的指针、指向图像信息结构体的指针、告诉它颜色怎么表示(比如 `DIB_RGB_COLORS`),还有一个参数,通常用 `SRCCOPY` 就行,表示直接复制过去。
实践中的小插曲
整个过程下来,还算顺利。一开始我还有点担心效率问题,毕竟是实时拉伸缩放,怕它卡。但实际跑起来,感觉还行,至少在我这 Win10 上没感觉跟 `BitBlt` 有啥明显差别,可能是现在的机器性能都上来。之前看网上有人说 XP、Win7 上效率也差不多,看来担心有点多余。
对,还有个事儿。虽然我没遇到,但听说有时候拉伸图像,特别是颜色渐变比较多的,可能会有点失真。好像有个 `SetStretchBltMode` 的函数可以设置拉伸模式,改善效果。虽然这是给 `StretchBlt` 用的,但原理应该差不多,如果以后遇到失真问题,可以往这个方向研究下 `StretchDIBits` 有没有类似的设置。
最终效果:
捣鼓完之后,成!内存里的图像数据,想多大就多大,乖乖地显示在窗口指定的位置上。用 `StretchDIBits` 来处理这种直接操作内存位图并进行缩放的需求,确实方便多,省去很多中间转换的麻烦。
这回用 `StretchDIBits` 的实践还是挺成功的,解决我的实际问题。如果你也有类似的需求,不妨试试看。