作者共发了13篇帖子。 字体大小:较小 - 100% (默认)▼  内容转换:不转换▼
 
点击 回复
556 12
【解决方案】SetPixel函数绘制速度太慢
一派护法 十九级
1楼 发表于:2016-6-25 15:44
【原程序】
void OnPaint(HDC hdc, LPPAINTSTRUCT lpps)
{
    int i, j;
    for (i = lpps->rcPaint.left; i <= lpps->rcPaint.right; i++)
    {
        for (j = lpps->rcPaint.top; j <= lpps->rcPaint.bottom; j++)
            SetPixel(hdc, i, j, i * j * i * j);
    }
}
该程序需要3秒左右才能执行完。
一派护法 十九级
2楼 发表于:2016-6-25 15:47
【解决方案1:CreateCompatibleBitmap + SetDIBits】
LPBYTE AllocateBits(int nWidth, int nHeight, int nBitCount, int *pSize)
{
    *pSize = ((nWidth * nBitCount + 31) / 32) * 4 * nHeight;
    if (*pSize < 0)
        *pSize = -*pSize;
    return (LPBYTE)malloc(*pSize);
}

void OnPaint(HDC hdc, LPPAINTSTRUCT lpps)
{
    BITMAPINFOHEADER bmh;
    COLORREF color;
    HBITMAP hbmp;
    HDC hdcMem;
    LPBYTE lpBits, p;
    int i, j;
    int padding, size;

    ZeroMemory(&bmh, sizeof(bmh));
    bmh.biSize = sizeof(bmh);
    bmh.biBitCount = 24; // 真彩色
    bmh.biPlanes = 1; // 颜色平面数必须为1
    bmh.biWidth = lpps->rcPaint.right - lpps->rcPaint.left;
    bmh.biHeight = lpps->rcPaint.top - lpps->rcPaint.bottom; // 使height为负数的目的是使位图数据的方向为从上到下

    // 生成DIB位图数据
    lpBits = AllocateBits(bmh.biWidth, bmh.biHeight, bmh.biBitCount, &size); // 为位图分配内存
    padding = (4 - (bmh.biWidth * bmh.biBitCount / 8) % 4) % 4; // 计算每行后的保留字节
    p = lpBits;
    for (j = lpps->rcPaint.top; j < lpps->rcPaint.bottom; j++)
    {
        for (i = lpps->rcPaint.left; i < lpps->rcPaint.right; i++)
        {
            color = i * j * i * j; // 算法和之前一样
            *p++ = (color >> 16) & 0xff;
            *p++ = (color >> 8) & 0xff;
            *p++ = color & 0xff;
        }
        p += padding;
    }

    hbmp = CreateCompatibleBitmap(hdc, bmh.biWidth, -bmh.biHeight); // 创建DDB位图
    SetDIBits(hdc, hbmp, 0, -bmh.biHeight, lpBits, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS); // DIB转换为DDB
    free(lpBits);

    hdcMem = CreateCompatibleDC(hdc);
    SelectObject(hdcMem, hbmp);
    BitBlt(hdc, lpps->rcPaint.left, lpps->rcPaint.top, bmh.biWidth, -bmh.biHeight, hdcMem, 0, 0, SRCCOPY); // DDB输出到显示器
    DeleteDC(hdcMem);
    DeleteObject(hbmp);
}
一派护法 十九级
3楼 发表于:2016-6-25 15:52
【解决方案2:CreateDIBitmap】
由于CreateDIBitmap = CreateCompatibleBitmap + SetDIBits,所以可以把上述代码中的:

hbmp = CreateCompatibleBitmap(hdc, bmh.biWidth, -bmh.biHeight); // 创建DDB位图
SetDIBits(hdc, hbmp, 0, -bmh.biHeight, lpBits, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS); // DIB转换为DDB

直接换成:
hbmp = CreateDIBitmap(hdc, &bmh, CBM_INIT, lpBits, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS); // 根据DIB直接创建DDB位图
一派护法 十九级
4楼 发表于:2016-6-25 15:55
回复1楼 @巨大八爪鱼 的内容:
【原程序】
void OnPaint(HDC hdc, LPPAINTSTRUCT lpps)
{
    int i, j;...
如果显示器较大的话,执行完需要8秒!
一派护法 十九级
5楼 发表于:2016-6-25 16:01
【解决方案3:CreateDIBSection,自动分配和释放位图内存】
void OnPaint(HDC hdc, LPPAINTSTRUCT lpps)
{
    BITMAPINFOHEADER bmh;
    COLORREF color;
    HBITMAP hbmp;
    HDC hdcMem;
    LPBYTE lpBits, p; // CreateDIBSection自动分配内存
    int i, j;
    int padding;

    ZeroMemory(&bmh, sizeof(bmh));
    bmh.biSize = sizeof(bmh);
    bmh.biBitCount = 24; // 真彩色
    bmh.biPlanes = 1; // 颜色平面数必须为1
    bmh.biWidth = lpps->rcPaint.right - lpps->rcPaint.left;
    bmh.biHeight = lpps->rcPaint.top - lpps->rcPaint.bottom; // 使height为负数的目的是使位图数据的方向为从上到下

    hbmp = CreateDIBSection(hdc, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS, (LPVOID *)&lpBits, NULL, NULL);
    padding = (4 - (bmh.biWidth * bmh.biBitCount / 8) % 4) % 4; // 计算每行后的保留字节
    p = lpBits;
    for (j = lpps->rcPaint.top; j < lpps->rcPaint.bottom; j++)
    {
        for (i = lpps->rcPaint.left; i < lpps->rcPaint.right; i++)
        {
            color = i * j * i * j; // 算法和之前一样
            *p++ = (color >> 16) & 0xff;
            *p++ = (color >> 8) & 0xff;
            *p++ = color & 0xff;
        }
        p += padding;
    }

    hdcMem = CreateCompatibleDC(hdc);
    SelectObject(hdcMem, hbmp);
    BitBlt(hdc, lpps->rcPaint.left, lpps->rcPaint.top, bmh.biWidth, -bmh.biHeight, hdcMem, 0, 0, SRCCOPY);
    DeleteDC(hdcMem);
    DeleteObject(hbmp); // 删除位图时自动释放内存
}
一派护法 十九级
6楼 发表于:2016-6-25 16:08
【解决方案4:SetDIBitsToDevice,不创建位图对象,直接把位图数据往显示器上输出】
LPBYTE AllocateBits(int nWidth, int nHeight, int nBitCount, int *pSize)
{
    *pSize = ((nWidth * nBitCount + 31) / 32) * 4 * nHeight;
    if (*pSize < 0)
        *pSize = -*pSize;
    return (LPBYTE)malloc(*pSize);
}

void OnPaint(HDC hdc, LPPAINTSTRUCT lpps)
{
    BITMAPINFOHEADER bmh;
    COLORREF color;
    LPBYTE lpBits, p;
    int i, j;
    int padding, size;

    ZeroMemory(&bmh, sizeof(bmh));
    bmh.biSize = sizeof(bmh);
    bmh.biBitCount = 24; // 真彩色
    bmh.biPlanes = 1; // 颜色平面数必须为1
    bmh.biWidth = lpps->rcPaint.right - lpps->rcPaint.left;
    bmh.biHeight = lpps->rcPaint.top - lpps->rcPaint.bottom; // 使height为负数的目的是使位图数据的方向为从上到下

    // 生成DIB位图数据
    lpBits = AllocateBits(bmh.biWidth, bmh.biHeight, bmh.biBitCount, &size); // 为位图分配内存
    padding = (4 - (bmh.biWidth * bmh.biBitCount / 8) % 4) % 4; // 计算每行后的保留字节
    p = lpBits;
    for (j = lpps->rcPaint.top; j < lpps->rcPaint.bottom; j++)
    {
        for (i = lpps->rcPaint.left; i < lpps->rcPaint.right; i++)
        {
            color = i * j * i * j; // 算法和之前一样
            *p++ = (color >> 16) & 0xff;
            *p++ = (color >> 8) & 0xff;
            *p++ = color & 0xff;
        }
        p += padding;
    }

    SetDIBitsToDevice(hdc, lpps->rcPaint.left, lpps->rcPaint.top, bmh.biWidth, -bmh.biHeight, 0, 0, 0, -bmh.biHeight, lpBits, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS);
    free(lpBits);
}
一派护法 十九级
7楼 发表于:2016-6-25 16:09
【消息处理部分】
case WM_ERASEBKGND:
    // 防止改变窗口大小时窗口内容被擦除导致闪烁
    break;

case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    OnPaint(hdc, &ps);
    EndPaint(hWnd, &ps);
    break;
一派护法 十九级
8楼 发表于:2016-6-25 16:10
一派护法 十九级
9楼 发表于:2016-6-25 16:14
本人推荐使用方案3。因为方案3创建位图时是系统自动分配和释放内存,此外,不但可以直接操作DIB位图数据,还可以将该位图选入HDC进行图形绘制,非常方便。
不过如果是纯裸位图数据,可以考虑使用方案4的方法不创建位图直接输出到显示器。
一派护法 十九级
10楼 发表于:2016-6-25 16:16
方案4所说的位图对象,是指HBITMAP,不是指位图数据。
方案4的代码里完全没有出现hdcMem和HBITMAP,以及BitBlt(也就是不使用双缓冲技术)
一派护法 十九级
11楼 发表于:2016-6-25 16:21
方案1其实就是普通的使用双缓冲技术的代码,只不过创建CompatibleBitmap(也称DDB)后不是用GDI函数来绘图,而是把绘制好的DIB位图数据转换成DDB后放进去。
一派护法 十九级
12楼 发表于:2016-6-25 16:22
至于方案2,要特别注意CreateDIBitmap函数,这个函数名字没取好,应该叫CreateBitmapFromDIB或CreateDIBGeneratedBitmap差不多。因为这个函数最终创建的是DDB,根本就不是DIB。
一派护法 十九级
13楼 发表于:2016-6-25 16:34
SetDIBits是把DIB转换为DDB,
与之相反的是GetDIBits函数,该函数将DDB转换为DIB,具体用法请参考:
https://zh.arslanbar.net/post.php?t=24202
屏幕截图的时候就要将屏幕上的DDB转换为DIB,然后写入文件。因为位图文件始终保存的是与设备无关的位图。

一般情况下HBITMAP句柄指向的位图都是DDB位图。
至于CreateDIBSection函数,则是系统自动完成了DDB和DIB的相互转换,因为创建DIBSection的时候就指定了hdc,系统当然就知道DDB是什么格式的。更新了DIB数组中的数据后,再调用GDI函数,系统会自动将DIB转换为DDB(HBITMAP那边)供GDI使用。当用GDI函数绘完图后,调用GDIFlush函数刷完成GDI的所有操作后,要想看DIB数组中的数据,系统又自动把DDB转换回DIB,达到两边同步的目的。这就是DIB"Section”的作用,它终究只是一个Section。

理论上来说DIB是不需要对应hdc的,因为是“设备无关”的位图。(方案4可体现)
凡是跟hdc扯上关系了的那肯定就是DDB位图,因为是“设备相关”的位图。

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:556 回复数:12
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2016-6-25 16:34
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。