浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第15章 设备无关位图_15.3 DIB和DDB的结合

Posted on 2015-08-03 13:16  浅墨浓香  阅读(637)  评论(0编辑  收藏  举报
第15章 设备相关位图_15.3 DIB和DDB的结合
15.3.1 从DIB创建DDB

(1)hBitmap =CreateDIBitmap(…)——注意这名称会误导,实际上创建的是DDB

参数

说明

hdc

设备环境句柄,可以为NULL。

pInfoHdr

指向DIB信息头的指针,即BITMAPINFOHEADER

fInit

0或CBM_INIT。CBM_INIT指定用后面3个参数来初始化DDB中的像素位

pBits

DIB像素位的指针

pInfo

DIB信息指针,即BITMAPINFO(包含BITMAPINFOHEADER和颜色表)

fClrUse

颜色使用标志位:DIB_RGB_COLORS(0)或DIB_PAL_COLORS(1)

注意:①返回值:跟CreateBitmap一样都返回一个GDI位图对象,可以直接选入DC中,并在上面绘图,以改变像素位。

     ②名称会误导,它是从一个DIB中创建一个DDB。

(2)CreateDIBitmap函数内部大致的代码实现

HBITMAP CreateDIBitmap(HDC hdc, CONST BITMAPINFOHEADER* pbmih,
     DWORD fInit, CONST VOID* pBits,CONST BITMAPINFO* pbmi, UINT fUsage)
{
     HBITMAP hBitmap;
     HDC     hdc,hdcMem;

     int     cx, cy, iBitCount;

     //从第2个参数读取DIB信息头的信息
     if (pbmih->biSize == sizeof(BITMAPCOREHEADER))
     {
         cx = ((PBITMAPCOREHEADER)pbmih)->bcWidth;
         cy = ((PBITMAPCOREHEADER)pbmih)->bcHeight;

         iBitCount = ((PBITMAPCOREHEADER)pbmih)->bcBitCount;
     }
     else
     {
         cx = pbmih->biWidth;
         cy = pbmih->biHeight;
         iBitCount = pbmih->biBitCount;
     }
 
     //创建DDB
     if (hdc)
         hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
     else
         hBitmap = CreateBitmap(cx, cy, 1, 1, NULL);//单色位图

     //后面4个参数是用来初始化DDB的
     if (fInit == CBM_INIT)
     {
         hdcMem = CreateCompatibleDC(hdc);
         SelectObject(hdcMem, hBitmap);

         //在内存位图表面绘图,注意这里是DDB位图,己被选入内存DC
         SetDIBitsToDevice(hdcMem, 0, 0, cx, cy, 0, 0, 0, cy, pBits, pbmi, fUsage);
         DeleteDC(hdcMem);
     }
     return hBitmap;
}

 

(3)应用举例

  ①创建单色GDI位图:hBitmap =CreateDIBitmap(NULL,pbmih,0,NULL,NULL,0);

  ②DC兼容位图:hBitmap = CreateDIBitmap(hdc,pbmih,0,NULL,NULL,0);//未初始化

(4)当CreateDIBitmap时未初始化像素位,后期可调用SetDIBits初始化DDB像素位

参数

说明

hdc

设备环境句柄,当DIB_PAL_COLORS时才需要设备hdc

hBitmap

要设置像素位的位图句柄

yScan

要转换的第一行扫描线

cyScans

扫描线的行数

pBits

将该像素位设给hBitmap的像素位。

pInfo

指向DIB信息头的指针

fClrUse

颜色使用标志

【DIBCONV程序】

/*------------------------------------------------------------
DIBCONV.C --Converts a DIB to a DDB
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static TCHAR szAppName[] = TEXT("DibConv");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX     wndclass;
    HACCEL       hAccel;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(hInstance, szAppName);
    wndclass.hIconSm = LoadIcon(hInstance, szAppName);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = szAppName;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClassEx(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
                   szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
                        TEXT("DIB to DDB Conversion"), // window caption
                        WS_OVERLAPPEDWINDOW,        // window style
                        CW_USEDEFAULT,              // initial x position
                        CW_USEDEFAULT,              // initial y position
                        CW_USEDEFAULT,              // initial x size
                        CW_USEDEFAULT,              // initial y size
                        NULL,                       // parent window handle
                        NULL,                       // window menu handle
                        hInstance,                  // program instance handle
                        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);
    hAccel = LoadAccelerators(hInstance, szAppName);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(hwnd, hAccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}
HBITMAP   CreateBitmapObjectFromDibFile(HDC hdc, PTSTR szFileName)
{
    HBITMAP hBitmap;
    BITMAPFILEHEADER* pbmfh;
    BOOL    bSuccess;
    HANDLE  hFile;
    DWORD   dwFileSize, dwHighSize, dwBytesRead;
    //打开位图,可读可写
    hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return NULL;
    //读入整个文件
    dwFileSize = GetFileSize(hFile, &dwHighSize);
    if (dwHighSize)
    {
        CloseHandle(hFile);
        return NULL;
    }
    pbmfh = malloc(dwFileSize);
    if (!pbmfh)
    {
        CloseHandle(hFile);
        return NULL;
    }
    bSuccess = ReadFile(hFile, pbmfh, dwFileSize, &dwBytesRead, NULL);
    CloseHandle(hFile);
    //检验是否是位图
    if (!bSuccess || (dwBytesRead != dwFileSize) || (pbmfh->bfType != *(WORD*)"BM"))
    {
        free(pbmfh);
        return NULL;
    }
    //创建DDB——注意,这里读入整个文件,并从该DIB文件读入信息头、像素位等信息
    hBitmap = CreateDIBitmap(hdc, (BITMAPINFOHEADER*)(pbmfh + 1),
                             CBM_INIT,
                             (BYTE*)pbmfh + pbmfh->bfOffBits,
                             (BITMAPINFO*)(pbmfh + 1),
                             DIB_RGB_COLORS);
    free(pbmfh);
    return hBitmap;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static TCHAR szFilter[] = TEXT("Bitmap File(*.bmp)\0*.bmp\0")
        TEXT("All Files(*.*)\0*.*\0\0");
    static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH];
    static cxClient, cyClient;
    static HBITMAP hBitmap;
    HDC         hdc, hdcMem;
    PAINTSTRUCT ps;
    BITMAP  bitmap;
    static OPENFILENAME  ofn;
    switch (message)
    {
    case WM_CREATE:
        memset(&ofn, 0, sizeof(OPENFILENAME));
        ofn.lStructSize = sizeof(OPENFILENAME);
        ofn.hwndOwner = hwnd;
        ofn.lpstrFilter = szFilter;
        ofn.lpstrFile = szFileName;
        ofn.nMaxFile = MAX_PATH;
        ofn.lpstrTitle = szTitleName;
        ofn.nMaxFileTitle = MAX_PATH;
        ofn.lpstrDefExt = TEXT("*.bmp");
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_FILE_OPEN:
            //显示打开对话框
            if (!GetOpenFileName(&ofn))
                return 0;
            //删除己经打开的位图
            if (hBitmap)
            {
                DeleteObject(hBitmap);
                hBitmap = NULL;
            }
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            ShowCursor(TRUE);
            //从DIB位图文件中创建DDB
            hdc = GetDC(hwnd);
            hBitmap = CreateBitmapObjectFromDibFile(hdc, szFileName);
            ReleaseDC(hwnd, hdc);
            ShowCursor(FALSE);
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            //更新客户区内容
            InvalidateRect(hwnd, NULL, TRUE);
            //错误提示
            if (hBitmap == NULL)
            {
                MessageBox(hwnd, TEXT("Cannot load DIB file"), szAppName, MB_OK | MB_ICONEXCLAMATION);
            }
            return 0;
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        if (hBitmap)
        {
            GetObject(hBitmap, sizeof(BITMAP), &bitmap);
            hdcMem = CreateCompatibleDC(hdc);
            SelectObject(hdcMem, hBitmap);
            BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
            DeleteDC(hdcMem);
        }
        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        if (hBitmap)
            DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 DIBConv.rc 使用
//
#define IDM_FILE_OPEN                   40001
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//DibConv.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
DIBCONV MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open...\tCtrl+O", IDM_FILE_OPEN
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
DIBCONV ACCELERATORS
BEGIN
"^O", IDM_FILE_OPEN, ASCII, NOINVERT
END
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

15.3.2 从DDB到DIB

(1)GetDIBits函数——将DDB转成DIB(即从hBitmap中获得pInfo,pBits等信息)

参数

说明

hdc

设备环境句柄

hBitmap

要获取像素位的位图句柄

yScan

要转换的第一行扫描线

cyScans

扫描线的行数

pBits

将hBitmap的像素位读入该内存中。

pInfo

指向DIB信息头的指针

fClrUse

颜色使用标志

注意:

①这个函数并不是SetDIBits的简单逆运算而己。因为DIB转为设备相关的格式时,会有信息的丢失。

②实际上很少用到这个函数,因为如果有能拿hBitmap句柄,却没有位图数据信息的情况很少(如剪贴板),但剪贴板提供了自动转换为DIB的功能。

15.3.3 DIB 区块(DIB Section)

(1)DDB和DIB的区别

区别

DDB(Windows内部结构)

DIB(数据结构是己公布的)

颜色组织

必须是黑白或与实际输出设备一样的格式(不含颜色表,,像素值表示索引或黑白,因数据结构未公布,不能直接访问像素位)

采用几种颜色格式之一(可以有自己的颜色表,像素位可直接访问)

每行像素数

2的倍数

4的倍数

对象构成

一个GDI位图对象,要通过选入内存dc来更改像素位或绘图.用一个hBitmap位图句柄来表示。

使用方法:

//加载或创建DDB,而资源位图本身是DIB

hBitmap =LoadBitmap(…)

//创建兼容DC,因为是设备相关的。

hdcMem = CreateCompatibleDC(hdc);

//选入dc

SelectObject(hdc,hBitmap);

//画图,改变hBitmap像素位

Rectange(hdcMem…)

//显示到设备环境中

BitBlt(hdc,…,hdcMem…,SRCCOPY);

//删除等

DeleteDC(hdcMem);

 

是个文件或内存区域,内存结构与文件结构类似,可直接通过pBits更改像素位。

使用方法:

//读入位图文件

hFile = CreateFile(…);

//从文件中DIB位图读入整个或部分DIB的信息

//分配文件头内存,读入信息

pbmhf =malloc(…);//文件头

pbmi = malloc(…);//信息头

pBits = malloc(…); //像素位数据

//读入各部分的信息

ReadFile(hFile,&pbmhf,….)

//显示

SetDIBtoDevice(hdc,…);//输出,可以不需要内存兼容DC(因为设备无关)

存在形式

只能存在于内存中

可存在于内存或文件中

转换

可相互转换,但涉及设备无关的像素位与设备相关像素位的转换

 

(2)CreateDIBSection函数——返回的对象是个“杂种”:即是GDI位图对象(DDB),又更像DIB(有pBits和pInfo),意味着可以选入设备环境修改像素位,也可以直接修改像素位。

参数

说明

hdc

设备环境句柄,只有在fClrUse设为DIB_PAL_COLORS时才可以要设置hdc。当fClrUse设为DIB_RGB_COLORS时,hdc将被忽略。

pInfo

指向DIB信息的指针

fClrUse

颜色使用标志——DIB_RGB_COLORS或DIB_PAL_COLORS。

ppBits

指向像素位指针的指针。

1、该函数调用时,会把pBits设置成指向某个内存块的指针(该内存由系统管理,DeleteObject(hBitmap)时自动释放,调用函数时,实际上并未为该空间预留大小,只有在赋值时,才会占用空间,我们的程序也不需要自己为pBits分配空间),在调用该函数完后,程序可以从DIB文件中读入像素位到pBits指向的这块内存里。

2、可以将位图选入内存设备中,然后用GDI在上面绘图,其结果反映在由pBits指向的DIB像素位中,但因GDI采用批量处理,所以绘图结束后,要调用GDIFlush,以便真正的保存起来,也可以“手工”地访问像素位。

hSection

文件映射对象句柄

dwOffSet

像素位数据在上述对象中的偏移。MSDN指出,这个参数必须是4的倍数。

注意:创建出来的位图对象,是每行的字节数是4的倍数,也就是更像DIB。

 

(3)应用举例

 

   /*创建384*256像素的DIB,每个像素为24位*/   
     BITMAPINFOHEADER bmih;
     BYTE*            pBits;
     HBITMAP          hBitmap;
 
     bmih.biSize = sizeof(BITMAPINFOHEADER);
     bmih.biWidth = 384;
     bmih.biHeight = 256;
     bmih.biPlanes = 1;
     bmih.biBitCount = 24;
     bmih.biCompression = BI_RGB;
     bmih.biSizeImage = 0;
     bmih.biXPelsPerMeter = 0;
     bmih.biYPelsPerMeter = 0;
     bmih.biClrUsed = 0;
     bmih.biClrImportant = 0;
/* 1、因24位,没颜色表,所以可以直接将bmih指针转为BITMAPINFO指针。 2、初始时pBits等于NULL,该函数会把pBits指针掰过来指向由系统维护的某内存块,我们的程序不必为pBits分配空间大小,在赋值是操作系统会自行分配,删除位图对象后该空间也会自行释放,程序不必自己释放。 */ hBitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, 0, &pBits, NULL, 0);
//从DIB文件中读入像素位到pBits中。 ReadFile(hFile, &pBits, ...);

(4)获取DIB区块信息——GetObject(hBitmap,sizeof(DIBSECTION),&dibsection);

DIBSECTION字段

含义

BITMAP dsBm

BITMAP结构

BITMAPINFOHEADER dsBmih

DIB信息头

DWORD    dsBitfields[3]

颜色遮罩

HANDLE   dshSection

文件映射对象句柄

DWORD    dsOffset

像素位数据的位移

注意:

1、该函数只能用在CreateDIBSection创建的位图对象,不能用在其他创建位图的函数返回的句柄上。

2、CreateDIBSection也可以通过GetObject(hBitmap,sizeof(BITMAP),&bm)返回位图信息。

3、该结构体不包括颜色表,如果用获得颜色表,可以将位图选入内存DC后,调用GetDIBColorTable来获得。

 

(5)DIB显示到屏幕——必须从设备无关的像素转换到设备相关的像素

函数

格式转换时机

SetDIBitsToDevice

StretchDIBits

发生在函数中

CreateDIBitmap

SetDIBits

1、未设置CBM_INIT时,发生在用 BitBlt或StretchBlt显示的时候或SetDIBits时

2、设置CBM_INIT时:发生在CreateDIBitmap函数中

CreateDIBSection创建区块

发生在用BitBlt或StretchBlt显示函数中。(注意:CreateDIBSection返回句柄实际上是DIB,但可以用BitBlt和StretchBlt函数显示),再次说明该返回值是个杂种,兼有DDB和DIB的特点。

(6)文件映射选项——CreateDIBSection的最后两个参数

①文件映射:把文件当作是己经存在于内存中,可以通过内存指针来访问,但并不需要把文件完全放到内存中。

②这项技术可减少内存需要。DIB像素位可保存在磁盘上,但却可以像内存一样的访问。但像素位必须保存在另外的文件中,不能是实际DIB文件的一部分。

  hFile = CreateFile(szFileName,…);

  …… //读入文件头(BITMAPFILEINFO)和信息头(BITMAPINFOHEADER)

  hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,0,NULL);

  hBitmap = CreateDIBSection(NULL,pbmi,DIB_RGB_COLORS,&pBits,

  hFileMap,bmfh.bfOffBits);//bfOffBits永远不会是4的倍数!

                                         //与MSDN要求不符,该代码不能用。


【DIBSecion程序】
效果图

/*------------------------------------------------------------
DIBSECTION.C --Displays a DIB Section in the Client area
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static TCHAR szAppName[] = TEXT("DibSection");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX     wndclass;
    HACCEL       hAccel;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(hInstance, szAppName);
    wndclass.hIconSm = LoadIcon(hInstance, szAppName);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = szAppName;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClassEx(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
                   szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
                        TEXT("DIB Section Display"), // window caption
                        WS_OVERLAPPEDWINDOW,        // window style
                        CW_USEDEFAULT,              // initial x position
                        CW_USEDEFAULT,              // initial y position
                        CW_USEDEFAULT,              // initial x size
                        CW_USEDEFAULT,              // initial y size
                        NULL,                       // parent window handle
                        NULL,                       // window menu handle
                        hInstance,                  // program instance handle
                        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);
    hAccel = LoadAccelerators(hInstance, szAppName);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(hwnd, hAccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}
HBITMAP   CreateDibSectionFromDibFile(PTSTR szFileName)
{
    HBITMAP hBitmap;
    BITMAPFILEHEADER  bmfh;
    BITMAPINFO*       pbmi;
    BYTE*             pBits;
    BOOL    bSuccess;
    HANDLE  hFile;
    DWORD   dwInfoSize, dwBytesRead;
    //打开位图,可读可写
    hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return NULL;
    //与前一个程序(DIBConv)不同,这里不读入整个文件,而是只读取文件头
    bSuccess = ReadFile(hFile, &bmfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL);
    if (!bSuccess || (dwBytesRead != sizeof(BITMAPFILEHEADER))
        || (bmfh.bfType != *(WORD*)"BM"))
    {
        CloseHandle(hFile);
        return NULL;
    }
    //为BITMAPINFO分配内存,并读入数据
    dwInfoSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);
    pbmi = malloc(dwInfoSize);
    bSuccess = ReadFile(hFile, pbmi, dwInfoSize, &dwBytesRead, NULL);
    //创建DDB——函数调用后,pBits指针会被掰向,一个存放像素数的内存,该内存由系统维护。
    hBitmap = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0);
    //为像素位内存写入像素数据
    ReadFile(hFile, pBits, bmfh.bfSize - bmfh.bfOffBits, &dwBytesRead, NULL);

    free(pbmi);
    CloseHandle(hFile);
    return hBitmap;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static TCHAR szFilter[] = TEXT("Bitmap File(*.bmp)\0*.bmp\0")
        TEXT("All Files(*.*)\0*.*\0\0");
    static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH];
    static cxClient, cyClient;
    static HBITMAP hBitmap;
    HDC         hdc, hdcMem;
    PAINTSTRUCT ps;
    BITMAP  bitmap;
    static OPENFILENAME  ofn;
    switch (message)
    {
    case WM_CREATE:
        memset(&ofn, 0, sizeof(OPENFILENAME));
        ofn.lStructSize = sizeof(OPENFILENAME);
        ofn.hwndOwner = hwnd;
        ofn.lpstrFilter = szFilter;
        ofn.lpstrFile = szFileName;
        ofn.nMaxFile = MAX_PATH;
        ofn.lpstrTitle = szTitleName;
        ofn.nMaxFileTitle = MAX_PATH;
        ofn.lpstrDefExt = TEXT("*.bmp");
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_FILE_OPEN:
            //显示打开对话框
            if (!GetOpenFileName(&ofn))
                return 0;
            //删除己经打开的位图
            if (hBitmap)
            {
                DeleteObject(hBitmap);
                hBitmap = NULL;
            }
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            ShowCursor(TRUE);
            //从DIB位图文件中创建DDB(或DIB区块)
            hdc = GetDC(hwnd);
            hBitmap = CreateDibSectionFromDibFile(szFileName);
            ReleaseDC(hwnd, hdc);
            ShowCursor(FALSE);
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            //更新客户区内容
            InvalidateRect(hwnd, NULL, TRUE);
            //错误提示
            if (hBitmap == NULL)
            {
                MessageBox(hwnd, TEXT("Cannot load DIB file"), szAppName, MB_OK | MB_ICONEXCLAMATION);
            }
            return 0;
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        if (hBitmap)
        {
            GetObject(hBitmap, sizeof(BITMAP), &bitmap);
            hdcMem = CreateCompatibleDC(hdc);
            SelectObject(hdcMem, hBitmap);
            BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
            DeleteDC(hdcMem);
        }

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        if (hBitmap)
            DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 DIBSection.rc 使用
//
#define IDM_FILE_OPEN                   40001
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//DibSection.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
DIBSECTION MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open...\tCtrl+O", IDM_FILE_OPEN
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
DIBSECTION ACCELERATORS
BEGIN
"^O", IDM_FILE_OPEN, ASCII, NOINVERT
END
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED