目前共有13篇帖子。 字體大小:較小 - 100% (默認)▼  內容轉換:港澳繁體▼
 
點擊 回復
1060 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)
 

本帖信息

點擊數:1060 回複數:12
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-6-25 16:34
 
©2010-2025 Purasbar Ver2.0
除非另有聲明,本站採用共享創意姓名標示-相同方式分享 3.0 Unported許可協議進行許可。