21. 显示BMP图片¶
emXGUI 支持的 BMP 图片显示,有两种方式:
一、从内部存储器读取数据来显示图片,显示速度较快,但需要的内存空间较多;
二、直接从外部存储器(SD卡)读取数据并显示。
图 21‑1 BMP图片
图21_1 的BMP 图片0.bmp存放在工程目录下的UserappGUI_Demofish中,文件大小为66.1KB,颜色格式为32位,大小是92*184。这些图片参数,是如何在BMP 图片文件中体现的?在学习如何显示BMP 图片之前,我们需要了解一下BMP 图片文件的格式。
21.1. BMP图片格式¶
BMP文件格式,又称为位图(Bitmap)或是DIB(Device-Independent Device,设备无关位图),是Windows系统中广泛使用的图像文件格式。BMP文件保存了一幅图像中所有的像素。
BMP格式可以保存单色位图、16色或256色索引模式像素图、24位真彩色图象,每种模式中单一像素点的大小分别为1/8字节,1/2字节,1字节和32字节。目前最常见的是256位色BMP和24位色BMP。
BMP文件格式还定义了像素保存的几种方法,包括不压缩、RLE压缩等。常见的BMP文件大多是不压缩的。
BMP文件的数据可以分为四个部分:
bmp文件头:存放了有关文件的格式、大小等信息;我们仅讨论24位色不压缩的BMP,所以文件头中的信息基本不需要注意,
位图信息头:包括图像大小、位平面数、压缩方式、颜色索引等信息。在位图信息头之中,只有“大小”这一项对我们比较有用。图像的宽度和高度都是一个32位整数,在文件中的地址分别为0x0012和0x0016。
调色板:索引与其对应的颜色的映射表。16色或256色BMP有颜色表,但在24位色BMP文件则没有,我们这里不考虑。
位图数据:实际的像素数据。
因此总的来说BMP图片的优点是简单。下面来用WinHex软件(跟UltraEdit软件功能类似)来分析一下BMP图像的文件内容。
21.1.1. bmp文件头¶
见 图21_2,阴影部分处是文件头部信息,具体说明参考 表格21_1。
图 21‑2文件头部信息
表格 21‑1 bmp文件头说明
变量名  | 
地址  | 
作用  | 
阴影部分处  | 
|
|---|---|---|---|---|
值  | 
参数说明  | 
|||
bfType  | 
00~01h  | 
文件类型  | 
42 4D  | 
如果是位图文件类型,必须分别为0x42 和0x4D ,0x424D=’BM’。  | 
bfSize  | 
02~05h  | 
文件大小  | 
B6 08 01 00  | 
0x000108B6 = 67766B 约等于 67k,和前面提到的文件大小一致  | 
bfReserved1  | 
06~07h  | 
保留字  | 
00 00  | 
不考虑  | 
bfReserved2  | 
08~09h  | 
保留字  | 
00 00  | 
同上  | 
bfOffBits  | 
0a~0dh  | 
实际位图数据的偏移字节数,即bmp文件头,位图信息头与调色板之和  | 
36 00 00 00  | 
00000036h = 54,刚刚好等于我们文件头部信息(BMP文件头和位图信息头),因为我们使用的是24位位图,所以调色板的大小为0,  | 
3到14字节的意义可以用一个结构体来描述,见 代码清单21_1 。
1 2 3 4 5 6 7 8  |  typedef struct tagBITMAPFILEHEADER
 {
    //attention: sizeof(DWORD)=4 sizeof(WORD)=2
    DWORD bfSize; //文件大小
    WORD bfReserved1; //保留字,不考虑
    WORD bfReserved2; //保留字,同上
    DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和
 } BITMAPFILEHEADER,tagBITMAPFILEHEADER;
 | 
21.1.2. 位图信息头¶
剩下的部分就是位图信息头,也就是 图21_3 的红标处,具体说明参考 表格21_2 。
图 21‑3 位图信息头
表格 21‑2 位图信息头
变量名  | 
地址  | 
作用  | 
阴影部分处  | 
|
|---|---|---|---|---|
值  | 
参数说明  | 
|||
biSize  | 
0e~11h  | 
指定结构体BITMAPINFOHEADER的长度,为40  | 
28 00 00 00  | 
00000028h = 40,就是说这个位图信息头的大小为40个字节。  | 
biWidth  | 
12~15h  | 
位图宽,以像素为单位  | 
5C 00 00 00  | 
0000005Ch = 92像素  | 
biHeight  | 
16~19h  | 
位图高,以像素为单位  | 
B8 00 00 00  | 
000000B8h = 184像素  | 
biPlanes  | 
1A~1Bh  | 
平面数,该值总为1  | 
00 01  | 
平面数:1  | 
biBitCount  | 
1C~1Dh  | 
采用颜色位数,可以是1,2,4,8,16,24或 32  | 
20 00  | 
0020h=32位颜色格式  | 
biCompression  | 
1E~21h  | 
压缩方式,可以是0,1,2,其中0表示不压缩  | 
00 00 00  | 
不压缩  | 
biSizeImage  | 
22~25h  | 
实际位图数据占用的字节数  | 
00 00 00 00  | 
图像不压缩,所以设置为0。  | 
biXPelsPerMeter  | 
26~29h  | 
X方向分辨率  | 
13 0B 00 00  | 
00000B13h=2835像素/米  | 
biYPelsPerMeter  | 
2A~2Dh  | 
Y方向分辨率  | 
13 0B 00 00  | 
00000B13h=2835像素/米  | 
biClrUsed  | 
2E~31h  | 
使用的颜色数,如果为0,则表示默认值(2^颜色位数)  | 
00 00 00 00  | 
默认值  | 
biClrImportant  | 
32~35h  | 
重要颜色数,如果为0,则表示所有颜色都是重要的  | 
00 00 00 00  | 
所有颜色都是重要的  | 
位图信息头结构体,见 代码清单21_2 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  |  typedef struct tagBITMAPINFOHEADER
 {
    //attention: sizeof(DWORD)=4 sizeof(WORD)=2
    DWORD biSize; //指定此结构体的长度,为40
    LONG biWidth; //位图宽,说明本图的宽度,以像素为单位
    LONG biHeight; //位图高,指明本图的高度,像素为单位
    WORD biPlanes; //平面数,为1
    WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24新的可以是32
    DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩
    DWORD biSizeImage; //实际位图数据占用的字节数
    LONG biXPelsPerMeter; //X方向分辨率
    LONG biYPelsPerMeter; //Y方向分辨率
    DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)
    DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的
 } BITMAPINFOHEADER,tagBITMAPINFOHEADER;
 | 
由于使用的是24位的位图,所以没有调色板。而且位图的大小为92*184,和开头提到的一致。
21.2. 生成图片数组¶
上面的图片都是b i n文件格式,如何转换成 C数组。这就需要我们的工具:bin2c(工程目录\ emxguitools中),界面图如 图21_5 所示。
图 21‑5 软件界面
它的使用方法如 图21_6 所示,非常的简单。
图 21‑6 使用方法
单击处的按钮,选择图片所在的路径;生成图片C数组,是一个.c文件,单击处的按钮,选择文件存放的位置。最后单击处的按钮,等待文件生成,如 图21_7 。
图 21‑7 生成文件的内容
21.3. tagBITMAP结构体¶
emXGUI使用tagBITMAP结构体来存放位图的相关信息,见 代码清单21_3 。
1 2 3 4 5 6 7 8 9  |  typedef struct tagBITMAP
 {
    U32 Format; // 位图格式。
    U32 Width; // 位图宽度(行)。
    U32 Height; // 位图高度(列)。
    U32 WidthBytes;// 位图图像每一行的字节数。
    LPVOID Bits; // 指向位图数据。
    COLORREF *LUT; // 颜色表,只有索引位图,BM_ALPHA4,BM_ALPHA8格式时才用到。
 } BITMAP;
 | 
Format:位图的格式,对应位图文件的biBitCount(1C~1Dh),可以是BM_ARGB8888、BM_RGB888、BM_RGB565等等。
Width:位图的宽度,对应位图文件的biWidth(12~15h)
Height:位图的高度,对应位图文件的biHeight(16~19h)
WidthBytes:位图图像每一行的字节数,该值与位图的宽度和颜色格式有关系。假设位图使用的颜色格式为BM_ARGB8888,也就是说一个像素是占4个字节,乘上图片的宽度,就是图像每一行的字节数。
Bits:指向位图像素数据
LUT:颜色查找表,本章节没有使用到,赋值为NULL即可。
21.4. DrawBitmap函数¶
emXGUI使用DrawBitmap函数可以在当前窗口中的指定位置绘制位图图像。函数的原型见 代码清单21_4。
1  |  BOOL DrawBitmap(HDC hdc,int x,int y,const BITMAP *bitmap,const RECT *lpRect);
 | 
hdc:绘图上下文;
x,y:绘制图片的起始坐标;
bitmap:BITMA位图数据结构体参数,存放位图的大小,格式等信息;
lpRect:要绘制的位图区域,如果该值为NULL, 则绘制整个位图。
21.5. 显示位图实验(图片在内部FLASH)¶
下面介绍emXGUI第一种显示图片的方式:从内部存储器中读取数据来显示图片。
21.5.2. 代码分析¶
创建父窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  |  void GUI_DEMO_Drawbitmap(void)
 {
    HWND hwnd;
    WNDCLASS wcex;
    MSG msg;
    /////
    wcex.Tag = WNDCLASS_TAG;
    wcex.Style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WinProc; //设置主窗口消息处理的回调函数.
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = NULL;//hInst;
    wcex.hIcon = NULL;//LoadIcon(hInstance, (LPCTSTR)IDI_WIN32_APP_TEST);
    wcex.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW);
    //创建主窗口
    hwnd =CreateWindowEx( NULL,
    &wcex,
    _T("DrawBitmap(ARGB8888 Format)"),
    WS_CLIPCHILDREN,
    0,0,GUI_XSIZE,GUI_YSIZE,
    NULL,NULL,NULL,NULL);
    //显示主窗口
    ShowWindow(hwnd,SW_SHOW);
    //开始窗口消息循环(窗口关闭并销毁时,GetMessage将返回FALSE,退出本消息循环)。
    while(GetMessage(&msg,hwnd))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 }
 | 
创建父窗口,标题栏为“DrawBitmap(ARGB8888 Format)”,设置winProc作为窗口回调函数。
窗口回调函数
WM_CREATE
1 2 3 4 5 6 7 8 9 10 11 12 13  |  static BITMAP bm_0;
 case WM_CREATE: //窗口创建时,会自动产生该消息,在这里做一些初始化的操作或创建子窗口
 {
    //设置位图结构参数
    bm_0.Format = BM_ARGB8888; //位图格式
    bm_0.Width = 92; //宽度
    bm_0.Height = 184; //高度
    bm_0.WidthBytes =bm_0.Width*4; //每行字节数
    bm_0.LUT =NULL; //查找表(RGB/ARGB格式不使用该参数)
    bm_0.Bits =(void*)gImage_0; //位图数据
    return TRUE;
 }
 | 
定义一个BITMAP 类型的结构体变量bm_0,前面我们讲过 图21_1 是32位的位图,因此,使用的颜色格式为BM_ARGB8888,宽和高为92和184,每行的字节数为宽度*4。32位色的位图,没有调试板,所以不使用查找表参数。 位图像素数据则是采用之前软件生成图片数组。注意,Bits存放的是位图的像素数据,也就是54个字节后的内容。因此,生成图像数组需要去掉前54个字节的数据。
WM_ERASEBKGND
1 2 3 4 5 6 7 8 9  |  //清除背景
 case WM_ERASEBKGND:
 {
    HDC hdc=(HDC)wParam;
    GetClientRect(hwnd,&rc);
    SetBrushColor(hdc,MapRGB(hdc,0,30,130));
    FillRect(hdc,&rc);
    return TRUE;
 }
 | 
这里使用WM_ERASEBKGND消息,来绘制窗口的背景:以RGB为(0,30,130)的颜色来填充背景。
WM_PAINT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  |  case WM_PAINT: //窗口需要绘制时,会自动产生该消息.
 {
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc0;
    int x,y;
    hdc =BeginPaint(hwnd,&ps);
    //获取客户区的位置和大小
    GetClientRect(hwnd,&rc0);
    SetPenColor(hdc,MapRGB(hdc,200,200,220));
    for(y=0; y<rc0.h; y+=bm_0.Height)
    {
        for(x=0; x<rc0.w; x+=bm_0.Width)
        {
            //绘制图片
            DrawBitmap(hdc,x,y,&bm_0,NULL);
            rc.x=x;
            rc.y=y;
            rc.w=bm_0.Width;
            rc.h=bm_0.Height;
            DrawRect(hdc,&rc);//绘制矩形
        }
    }
    EndPaint(hwnd,&ps);
    break;
 }
 | 
在WM_PAINT消息,调用BeginPaint函数开始绘图。变量x和y用来记录窗口可以显示的图片张数。利用DrawBitmap函数绘制图片,且使用DrawRect给图片绘制一个外边框。
最后,将GUI_DEMO_Drawbitmap函数加入到GUI_AppMain函数即可。
21.6. 显示外部BMP图片实验(图片在SD卡)¶
上一节,我们实现了将内部FLASH的图片数组显示到屏幕上, 92*184的32位图片,需要92*184*4=67712个字节的空间来存放,已经是相当大了。这一讲,我们介绍另一种方式:显示外部BMP图片,图片存放在SD卡中。
21.6.1. 绘制位图API¶
21.6.1.1. BMP_GetInfoEx¶
emXGUI提供一个API:BMP_GetInfoEx,用来读取BMP图片的信息,函数原型见 代码清单21_9 。
1  |  BOOL BMP_GetInfoEx(BITMAPINFO *bm_info,GUI_GET_DATA *read_data);
 | 
bm_info :输出BMP图片信息结构体,存放BMP图片的大小、格式;
read_data: GUI_GET_DATA结构体类型,该结构体有两个结构体成员,一个是 lParam,用户自定义的参数,该参数会作为实参传入pfReadData回调函数;另一个是pfReadData,存放用来读取数据的回调函数指针。
21.6.1.2. BMP_DrawEx¶
使用BMP_DrawEx函数来绘制BMP图像,函数原型见代码清单 21‑10。
代码清单 21‑10 BMP_DrawEx(文件emXGUI.h)
1 BOOL BMP_DrawEx(HDC hdc,int x,int y,GUI_GET_DATA *read_data,const RECT *lprc);
hdc:绘图上下文;
x, y:起始的绘制坐标;
read_data: 指向读取BMP数据源的回调函数;
lprc:要绘制的BMP图像矩形区域,如果设置该参数为NULL,则绘制整个BMP图像区域。
21.6.3. 代码分析¶
创建父窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34  |  void GUI_DEMO_Drawbitmap_Extern(void)
 {
    HWND hwnd;
    WNDCLASS wcex;
    MSG msg;
    wcex.Tag = WNDCLASS_TAG;
    wcex.Style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WinProc; //设置主窗口消息处理的回调函数.
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = NULL;//hInst;
    wcex.hIcon = NULL;//LoadIcon(hInstance, (LPCTSTR)IDI_WIN32_APP_TEST);
    wcex.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW);
    //创建主窗口
    hwnd =CreateWindowEx( NULL,
                            &wcex,
                            _T("DrawBitmap_Extern"),
                            /*WS_MEMSURFACE|*/WS_CAPTION|WS_BORDER|WS_CLIPCHILDREN,
                            0,0,GUI_XSIZE,GUI_YSIZE,
                            NULL,NULL,NULL,NULL);
    //显示主窗口
    ShowWindow(hwnd,SW_SHOW);
    //开始窗口消息循环(窗口关闭并销毁时,GetMessage将返回FALSE,退出本消息循环)。
    while(GetMessage(&msg,hwnd))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 }
 | 
创建父窗口,标题栏为“DrawBitmap_Extern”,设置winProc作为窗口回调函数。
窗口回调函数
WM_CREATE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29  |  case WM_CREATE: //窗口创建时,会自动产生该消息,在这里做一些初始化的操作或创建子窗口
 {
    HWND wnd;
    GetClientRect(hwnd,&rc); //获得窗口的客户区矩形
    /* 读取文件系统中的图片信息*/
    PIC_BMP_GetInfo_FS(&bm_0, DEMO_BMP_NAME);
    CreateWindow(BUTTON,L"OK",WS_VISIBLE,
    rc.w-70,rc.h-40,68,32,hwnd,ID_OK,NULL,NULL);
    /* 创建内存对象 */
    hdc_mem =CreateMemoryDC(SURF_SCREEN,bm_0.Width,bm_0.Height);
    /* 绘制至内存对象 */
    PIC_BMP_Draw_FS(hdc_mem,0,0,DEMO_BMP_NAME,NULL);
    return TRUE;
 }
 | 
在WM_CREATE消息中,创建了一个BUTTON按键。创建MemoryDC,大小为图片的大小。使用MemoryDC,可以绘制图片到缓冲区,肉眼看不到绘制的过程,不会出现“闪屏”。调用PIC_BMP_GetInfo_FS函数来获取图片的消息,存放在bm_0结构体中,具体的实现方式,见 代码清单21_13 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38  |  /**
 * @brief 获得BMP图像的信息(文件系统)
 * @param bm_info(输出):存储得到的图像信息
 * @param file_name(输入): 绘制到目标的坐标
 * @retval FALSE:失败; TRUE:成功
 */
 BOOL PIC_BMP_GetInfo_FS(BITMAPINFO *bm_info, char *file_name)
 {
    /* file objects */
    FIL *file;
    FRESULT fresult;
    BOOL res = TRUE;
    GUI_GET_DATA get_data;
    file =(FIL*)GUI_VMEM_Alloc(sizeof(FIL));
    /* 打开文件 */
    fresult = f_open(file, file_name, FA_OPEN_EXISTING | FA_READ );
    if (fresult != FR_OK)
    {
        GUI_ERROR("Open Pic failed!");
        GUI_VMEM_Free(file);
        return FALSE;
    }
    /* 把文件指针作为lParam参数*/
    get_data.lParam = (LPARAM)file;
    /* 读取数据的回调函数 */
    get_data.pfReadData = bmp_read_data_fs;
    /* 获取图片信息 */
    res = BMP_GetInfoEx(bm_info,&get_data);
    f_close(file);
    /* 释放空间 */
    GUI_VMEM_Free(file);
    return res;
 }
 | 
调用GUI_VMEM_Alloc函数,在VMEM申请内存,并将申请到内存地址转换为FIL指针类型。使用文件之前,必须使用f_open函数打开文件,不再使用文件必须使用f_close函数关闭文件,f_close函数运行可以确保缓冲区完全写入到文件内。定义一个GUI_GET_DATA类型的结构体变量,把文件指针作为lParam参数,读取数据的回调函数设置为bmp_read_data_fs。 最后调用emXGUI提供的API:BMP_GetInfoEx来获取图片信息。bmp_read_data_fs函数,代码清单21_14。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  |  /**
 * @brief 从流媒体加载内容的回调函数(文件系统)
 * @param buf[out] 存储读取到的数据缓冲区
 * @param offset 要读取的位置
 * @param size 要读取的数据大小
 * @param lParam 调用函数时的自定义参数(用户参数)
 * @retval 读取到的数据大小
 */
 static int bmp_read_data_fs(void *buf,int offset,int size,LPARAM lParam)
 {
    int rw;
    /* 本回调函数中lParam是对应的文件指针*/
    FIL * p_file = (FIL*)lParam;
    /* 偏移到指定位置 */
    f_lseek(p_file, offset);
    /* 读取数据到缓冲区 */
    f_read(p_file, buf, (UINT)size, (UINT *)&rw);
    /* 返回读取到的数据大小 */
    return rw;
 }
 | 
bmp_read_data_fs函数的形参lParam是用户自定义的参数,这里传递的是文件指针。使用文件系统函数f_lseek偏移到指定位置offset,从SD卡中读取数据到缓冲区,最后返回读取到的数据大小。
调用PIC_BMP_Draw_FS函数将图片绘制到MemoryDC,见 代码清单21_15。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43  |  /**
 * @brief 显示文件系统中的BMP图片(文件系统)
 * @param hdc(输入):绘图上下文
 * @param x,y(输入): 绘制到目标的坐标
 * @param lprc(输入):
 要绘制的BMP图像矩形区域,如果设置该参数为NULL,则绘制整个BMP图像区域。
 * @retval FALSE:失败; TRUE:成功
 */
 BOOL PIC_BMP_Draw_FS(HDC hdc, int x, int y, char *file_name, const RECT *lprc)
 {
 /* file objects */
 FIL *file;
 FRESULT fresult;
 BOOL res = TRUE;
 GUI_GET_DATA get_data;
 file =(FIL*)GUI_VMEM_Alloc(sizeof(FIL));
 /* 打开文件 */
 fresult = f_open(file, file_name, FA_OPEN_EXISTING | FA_READ );
 if (fresult != FR_OK)
 {
    GUI_ERROR("Open Pic failed!");
    GUI_VMEM_Free(file);
    return FALSE;
 }
 /* 把文件指针作为lParam参数 */
 get_data.lParam = (LPARAM)file;
 /* 读取数据的回调函数 */
 get_data.pfReadData = bmp_read_data_fs;
 /* 显示图片 */
 res = BMP_DrawEx(hdc,x,y,&get_data,lprc);
 /* 关闭文件 */
 f_close(file);
 /* 释放空间 */
 GUI_VMEM_Free(file);
 return res;
 }
 | 
在PIC_BMP_Draw_FS函数中,在VMEM申请内存,并将申请到内存地址转换为FIL指针类型。调用BMP_DrawEx函数来显示图片,BMP数据源通过bmp_read_data_fs回调函数获得,最后释放申请的内存。
WM_NOTIFY
1 2 3 4 5 6 7 8 9 10 11 12  |  /* wParam低16位为发送该消息的控件ID,高16位为通知码;lParam指向了一个NMHDR结构体 */
 case WM_NOTIFY:
 {
    u16 code,id;
    code =HIWORD(wParam); //获得通知码类型.
    id =LOWORD(wParam); //获得产生该消息的控件ID.
    if(id==ID_OK && code==BN_CLICKED)
    {
        PostCloseMessage(hwnd); //产生WM_CLOSE消息关闭窗口
    }
    break;
 }
 | 
WM_NOTIFY消息中的wParam低16位为发送该消息的控件ID,高16位为通知码。单击OK按键,则发送WM_CLOSE消息关闭窗口。
WM_ERASEBKGND
1 2 3 4 5 6 7 8  |  case WM_ERASEBKGND:
 {
    HDC hdc=(HDC)wParam;
    GetClientRect(hwnd,&rc);
    SetBrushColor(hdc,MapRGB(hdc,0,30,130));
    FillRect(hdc,&rc);
    return TRUE;
 }
 | 
在客户区绘制一个矩形,大小为整个客户区的大小,填充颜色设置为RGB(0,30,130)。
WM_PAINT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27  |  case WM_PAINT: //窗口需要绘制时,会自动产生该消息.
 {
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc0;
    int x,y;
    hdc =BeginPaint(hwnd,&ps);//开始绘制
    ////用户的绘制内容...
    GetClientRect(hwnd,&rc0);
    SetPenColor(hdc,MapRGB(hdc,200,200,220));
    for(y=0; y<rc0.h; y+=bm_0.Height)
    {
        for(x=0; x<rc0.w; x+=bm_0.Width)
        {
            /* 显示文件系统中的图片文件 */
            BitBlt(hdc,x,y,bm_0.Width,bm_0.Height,hdc_mem,0,0,SRCCOPY); //将MEMDC输出到窗口中。
            rc.x=x;
            rc.y=y;
            rc.w=bm_0.Width;
            rc.h=bm_0.Height;
            DrawRect(hdc,&rc);
        }
    }
    EndPaint(hwnd,&ps);
    break;
 }
 | 
在WM_CREATE中,已经将图片绘制到MemoryDC中,因此,我们只需要将MemoryDC中的图形拷贝到hdc中即可。调用BitBlt函数将MEMDC的(0,0)处的内容输出到窗口的(x,y)中,数据的数目由图片的大小决定。使用DrawRect函数给图片画个“画框”。