版权
本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/128082389.
内容
《Programming Windows》(Charles Petzold, 第五版) 第15章的SetDIBitsToDevice 函数:
SetDIBitsToDevice (
hdc,
xDst, // xDst
yDst, // yDst
cxSrc, // cxSrc
cySrc, // cySrc
xSrc, // xSrc
ySrc, // ySrc
yScan, // first scan line
cyScans, // number of scan lines
pBits,
pInfo,
fClrUse );
Src 是图像数据里的坐标: Bottom-up 的坐标原点在左上角(数据首行); Top-down 的原点在左下角(数据末行).
Bottom-up
一般是Bottom-up 的. Bottom-up 数据(即存储) 与展示的方向相反:
(0,0) -------- 首行数据(显示为图片末行)
--------
.
.
.
-------- 末行数据(显示为图片首行)
原点在左上角, 即(xSrc, ySrc) 是从存储开始往下ySrc 行再往右xSrc 列.
Top-down
Top-down 数据与展示的方向一致:
-------- 首行数据(显示为图片首行)
--------
.
.
.
(0,0) -------- 末行数据(显示为图片末行)
原点在左下角, 即(xSrc, ySrc) 是存储末尾往上ySrc 行再往左"行宽-xSrc" 列.
yScan
逐行展示见"Sequential Display" 小节的SEQDISP.C:
for (y = 0; y < cyDib; y++) {
......
SetDIBitsToDevice(hdc,
0, // xDst
0, // yDst
cxDib, // cxSrc
cyDib, // cySrc
0, // xSrc
0, // ySrc
// first scan line
bTopDown ? cyDib - y - 1 : y,
1, // number of scan lines
pBits + y * iRowLength,
pbmi,
DIB_RGB_COLORS );
Sleep(10); // (Sleep - 本文增加的)
}
Bottom-up (即 !bTopDown) 时yScan 从0 递增: 读首行数据->展示到屏幕末行->读第二行数据->展示到屏幕次末行->… .
Top-down 时yScan 初值=cyDib-1, 因为数据原点在左下角, 所以读取的是存储开始位置的首行.
由此推测, 不论Bottom-up 或Top-down, 为了实现的效率数据总是从头开始往后顺序读取.
增加Sleep 后实测可看到: Bottom-up 图是从下往上显示的, Top-down 图是从上往下.
总结
只需记住: Bottom-up 数据原点在左上角, Top-down 数据原点在左下角, 所有Src 参数都是相对于此原点.
分析
Bottom-up 时(xSrc,ySrc) 数据区域实际是颠倒后再显示到(xDst,yDst) 屏幕区域(因为反向). 这就是"The Topsy−Turvy World of DIBs" 小节里的:
Source => Destination
(xSrc,ySrc) => (xDst,yDst+cySrc-1)
(xSrc,ySrc+cySrc-1) => (xDst,yDst)
即Src 矩形上下翻转后的左上角 对应Dst 矩形的左上角(xDst,yDst).
这个反向就是造成chaos 的主因.
Top-down 时顺序一致: Src 矩形的左上角 对应Dst 矩形的左上角(xDst,yDst). 但参数需指定Src 矩形的左下而非左上角. 原作者认为将Src 原点定在最末行首(存储的既非开始也非结束位置) 也不常见.
SetDIBitsToDevice 好处是参数不受数据方向的影响: “you can select the same part of the image to display using identical arguments”.
在该小节末尾原作者推荐统一将数据原点对应到显示区域的左上角.
附录
如何生成Top-down DIB
将原来的Bottom-up 文件在小画家里上下颠倒后另存; 将另存文件的biHeight 改为负值即可.
例如高度240 在原文件里4字节Hex 依次是: F0 0 0 0.
-240 的Hex 是FFFFFF10, 4字节依次替换为Hex: 10 FF FF FF.