C++ Programmer's Cookbook

{C++ 基础} {C++ 高级} {C#界面,C++核心算法} {设计模式} {C#基础}

GDI Bitmaps and Device-Independent Bitmaps 操作

DIB(Device-Independent Bitmap,设备无关位图)小知识

            作者:顾胜元 汤泽江 陈载春

 

位图简介

 

位图一共有两种类型,分别为:设备相关位图(DDBDevice-Dependent Bitmap)和设备无关位图(DIBDevice-Independent Bitmap)。GDI bitmap objects (设备相关位图)are represented by the MFC library CBitmap class.

 

BMP文件是Windows保存图像的一种通用文件格式,在数字图像处理方面占有重要的地位。BMP文件中保存的图像数据就是一种DIB(Device-Independent Bitmap,即设备无关位图)DIB是标准的Windows位图格式,它自带颜色信息,因此调色板管理非常容易。

 

BMP文件的结构

 

DIB位图包含下列的颜色和尺寸信息:

     原始设备(即创建图片的设备)的颜色格式。

     原始设备的分辩率。

     原始设备的调色板(仅用于256色)

     图像像素数据,由红、绿、蓝(RGB)三个值代表一个像素。

     一个数组压缩标志,用于表明数据的压缩方案(如果需要的话)。

以上这些信息保存在BITMAPINFO结构中,该结构由BITMAPINFOHEADER结构和两个或更多个RGBQUAD结构所组成。BITMAPINFOHEADER结构所包含的成员表明了图像的尺寸、原始设备的颜色格式、以及数据压缩方案等信息。RGBQUAD结构标识了像素所用到的颜色数据。

 

DIB位图也有两种形式,分为:bottom-up DIBtop-down DIBBottom-up DIB的原点(origin)在图像的左下角,而top-down DIB的原点在图像的左上角。如果DIB的高度值(由BITMAPINFOHEADER结构中的biHeight成员标识)是一个正值,那么就表明这个DIB是一个bottom-up DIB,如果高度值是一个负值,那么它就是一个top-down DIB。注意:top-down DIB位图是不能被压缩的。

 

一般来说,BMP文件均为bottom-up DIB形式,即像素数据从下到上,从左到右存储于文件中。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。

位图文件结构概观分析:

位图文件可看成由4个部分组成:位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节阵列,它具有如下所示的形式。

位图文件的组成

结构名称

符号

位图文件头(bitmap-file header)

BITMAPFILEHEADER

bmfh

位图信息头(bitmap-information header)

BITMAPINFOHEADER

bmih

彩色表(color table)/调色板(Palette

RGBQUAD

aColors[]

图象数据阵列

BYTE

aBitmapBits[]

 

struct BITMAPFILEHEADER {

WORD           bfType;

DWORD        bfSize;

WORD           bfReserved1;

WORD           bfReserved2;

DWORD        bfOffBits;

};

 

struct BITMAPINFOHEADER{

DWORD        biSize;

LONG            biWidth;

LONG            biHeight;

WORD           biPlanes;

WORD           biBitCount;

DWORD        biCompression;

DWORD        biSizeImage;

LONG            biXPelsPerMeter;

LONG            biYPelsPerMeter;

DWORD        biClrUsed;

DWORD        biClrImportant;

};

 

struct RGBQUAD {

BYTE             rgbBlue;

BYTE             rgbGreen;

BYTE             rgbRed;

BYTE             rgbReserved;

};

位图文件结构详细分析:

 

偏移量

域的名称

大小

内容

 图象文件头

0000h

bfType = “BM”(0x4D42)

2 bytes

两字节的内容用来识别位图的类型:

‘BM’ Windows 3.1x, 95, NT, …

‘BA’ OS/2 Bitmap Array

‘CI’ OS/2 Color Icon

‘CP’ OS/2 Color Pointer

‘IC’ OS/2 Icon

‘PT’ OS/2 Pointer

 

注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行。

 

0002h

bfSize

1 dword

用字节表示的整个文件的大小

 

0006h

bfReserved1-2

1 dword

保留,必须设置为0

 

000Ah

bfOffBits

1 dword

从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量,即表前三个部分的长度之和

位图信息头

000Eh

biSize

1 dword

位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:

28h - Windows 3.1x, 95, NT, …

0Ch - OS/2 1.x

F0h - OS/2 2.x

 

注:在Windows95982000等操作系统中,位图信息头的长度并不一定是28h,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数28h,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。

 

0012h

biWidth

1 dword

位图的宽度,以象素为单位

 

0016h

biHeight

1 dword

位图的高度,以象素为单位

 

001Ah

biPlance

1 word

位图的位面数(注:该值将总是1

 

001Ch

biBitCount

1 word

每个象素的位数

1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色)

4 - 16 色位图

8 - 256 色位图

16 - 16bit 高彩色位图

24 - 24bit 真彩色位图

32 - 32bit 增强型真彩色位图

 

001Eh

biCompression

1 dword

压缩说明:

0 - 不压缩 (使用BI_RGB表示)

1 - RLE 8-使用8RLE压缩方式(BI_RLE8表示)

2 - RLE 4-使用4RLE压缩方式(BI_RLE4表示)

3 - Bitfields-位域存放方式(BI_BITFIELDS表示)

 

0022h

biSizeImage

1 dword

用字节数表示的位图数据的大小。该数必须是4的倍数

 

0026h

biXPelsPerMeter

1 dword

用象素/米表示的水平分辨率

 

002Ah

biYPelsPerMeter

1 dword

用象素/米表示的垂直分辨率

 

002Eh

biClrUsed

1 dword

位图使用的颜色数。如8-比特/象素表示为100h或者 256.

 

0032h

biClrImportant

1 dword

指定重要的颜色数。当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要

调色板数据

根据BMP版本的不同而不同

Palette真彩色图是不需要调色板的,BITMAPINFOHEADER后直接是位图数据

N * 4 byte

调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2biBitCount个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节

rgbBlue

1字节用于蓝色分量

rgbGreen

1字节用于绿色分量

rgbRed

1字节用于红色分量

rgbReserved

1字节用于填充符(0)

图象数据

根据BMP版本及调色板尺寸的不同而不同

Bitmap Data:该域的大小取决于压缩方法及图像的尺寸和图像的位深度,它包含所有的位图数据字节,这些数据可能是彩色调色板的索引号,也可能是实际的RGB值(真彩色位图),这将根据图像信息头中的位深度值来决定。

xxx bytes

象素按照行、列的顺序排列每一行的字节数必须是4的整倍数。

比较:

2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。

16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。256色位图,一个字节刚好可以表示1个象素。

真彩色图,三个字节才能表示1个象素

 

 

显示位图的方法有许多,下面示范一种比较简便的:

// read bitmap file “a.bmp”

HBITMAP         hBitmap = (HBITMAP)LoadImage( NULL, "a.bmp", IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE );

HDC hMemDC  = CreateCompatibleDC( NULL );

BITMAP   bm;

 

// get bitmap size

// bm.bmWidth, bm.bmHeight - size of image

GetObject( hBitmap, sizeof(bm), &bm );

 

// select bitmap object

SelectObject( hMemDC, hBitmap );

 

// BitBlt the image to screen

// hdc – screen dc

BitBlt( hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY );

 

// release all

DeleteDC( hMemDC );

DeleteObject( hBitmap );

 

位图读写DEMO代码

 

bitmap的一般过程:

1、声明BITMAPFILEHEADER,并清空该结构:

       BITMAPFILEHEADER bfh

       memset( &bfh, 0, sizeof( bfh ) );

2、初始化该结构:

       bfn.bfType = 'MB';               // Bitmap

              //说明该文件的大小,cbBuffer为位图数据的大小

       bfn.bfSize = sizeof(bfn) + cbBuffer + sizeof(BITMAPINFOHEADER);

       //说明位图文件数据在整个位图文件中的偏移,即数据是从哪儿开始的

       bfn.bfOffBits = sizeof(BITMAPINFORHEADER) + sizeof(BITMAPFILEHEADER);

 

如果你的应用程序想以位映射的方式保存图像的话,你可以采用Windows操作系统的位图格式来保存。步骤是,先初始化BITMAPINFO结构(由BITMAPINFOHEADER结构和RGBQUAD结构数组组成),然后填写适当的数据用以说明待保存图像的各种参数。最后将BITMAPFILEHEADER结构及BITMAPINFO结构和位数组写入文件当中。

 

下面的范例代码演示了怎样初始化并填写BITMAPINFOHEADER结构

PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp) 
{
    BITMAP bmp; 
    PBITMAPINFO pbmi;
    WORD    cClrBits;  
 
    /* Retrieve the bitmap's color format, width, and height. */  
    if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) 
        errhandler("GetObject", hwnd);   
 
    /* Convert the color format to a count of bits. */  
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
 
    if (cClrBits == 1) 
        cClrBits = 1;
    else if (cClrBits <= 4)
        cClrBits = 4; 
    else if (cClrBits <= 8) 
        cClrBits = 8;
    else if (cClrBits <= 16) 
        cClrBits = 16;
    else if (cClrBits <= 24) 
        cClrBits = 24; 
    else 
        cClrBits = 32;      
 
    /* 
     * Allocate memory for the BITMAPINFO structure. (This structure 
     * contains a BITMAPINFOHEADER structure and an array of RGBQUAD data 
     * structures.)      
     */      
    if (cClrBits != 24) 
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER) + 
                    sizeof(RGBQUAD) * (2^cClrBits));      
    /*  There is no RGBQUAD array for the 24-bit-per-pixel format. */  
    else 
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER));    
 
    /* Initialize the fields in the BITMAPINFO structure. */  
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbmi->bmiHeader.biWidth = bmp.bmWidth; 
    pbmi->bmiHeader.biHeight = bmp.bmHeight; 
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (cClrBits < 24) 
        pbmi->bmiHeader.biClrUsed = 2^cClrBits;   
 
    /* If the bitmap is not compressed, set the BI_RGB flag. */  
    pbmi->bmiHeader.biCompression = BI_RGB;      
 
    /* 
     * Compute the number of bytes in the array of color 
     * indices and store the result in biSizeImage.
     */  
    pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 
                                  * pbmi->bmiHeader.biHeight 
                                  * cClrBits;      
    /* 
     * Set biClrImportant to 0, indicating that all of the 
     * device colors are important.
     */  
    pbmi->bmiHeader.biClrImportant = 0;
    return pbmi;  
} 

 

3、将bfn写入文件

4、申明BITMAPINFOHEADER,并清空该结构:

       BITMAPINFOHEADER bih;

       memset( &bih, 0, sizeof( bih ) );

5、初始化该结构:

       bih.biSize = sizeof( bih );

       bih.biWidth = biWidth;                        //位图的宽度

       bih.biHeight = biHeight;                //位图的高度

       bih.biPlanes = biPlanes;                //位图的位面数

       bih.biBitCount = biBitCount;                //位图的色深

6、将bih写入文件

7、最后写入数据就行了。

 

下面的范例将演示怎样打开一个文件,并拷贝数组、获取调色板索引、初始化保留结构、关闭文件等操作:

void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, 
                  HBITMAP hBMP, HDC hDC) 
{
    HANDLE hf;                  /* file handle */ 
    BITMAPFILEHEADER hdr;       /* bitmap file-header */ 
    PBITMAPINFOHEADER pbih;     /* bitmap info-header */ 
    LPBYTE lpBits;              /* memory pointer */ 
    DWORD dwTotal;              /* total count of bytes */ 
    DWORD cb;                   /* incremental count of bytes */ 
    BYTE *hp;                   /* byte pointer */ 
    DWORD dwTmp; 
 
 
    pbih = (PBITMAPINFOHEADER) pbi; 
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);    if (!lpBits) 
         errhandler("GlobalAlloc", hwnd); 
 
    /* 
     * Retrieve the color table (RGBQUAD array) and the bits 
     * (array of palette indices) from the DIB. 
     */ 
 
    if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, 
                   lpBits, pbi, DIB_RGB_COLORS)) 
        errhandler("GetDIBits", hwnd); 
 
    /* Create the .BMP file. */ 
 
    hf = CreateFile(pszFile, 
                   GENERIC_READ | GENERIC_WRITE, 
                   (DWORD) 0, 
                   (LPSECURITY_ATTRIBUTES) NULL, 
                   CREATE_ALWAYS, 
                   FILE_ATTRIBUTE_NORMAL, 
                   (HANDLE) NULL); 
 
    if (hf == INVALID_HANDLE_VALUE) 
        errhandler("CreateFile", hwnd); 
 
    hdr.bfType = 0x4d42;        /* 0x42 = "B" 0x4d = "M" */ 
 
    /* Compute the size of the entire file. */ 
 
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
                 pbih->biSize + pbih->biClrUsed 
                 * sizeof(RGBQUAD) + pbih->biSizeImage); 
 
    hdr.bfReserved1 = 0; 
    hdr.bfReserved2 = 0; 
 
    /* Compute the offset to the array of color indices. */ 
 
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
                    pbih->biSize + pbih->biClrUsed 
                    * sizeof (RGBQUAD); 
 
    /* Copy the BITMAPFILEHEADER into the .BMP file. */ 
 
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
       (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) 
       errhandler("WriteFile", hwnd); 
 
    /* Copy the BITMAPINFOHEADER and RGBQUAD array into the file. */ 
 
    if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
                  + pbih->biClrUsed * sizeof (RGBQUAD), 
                  (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) 
       errhandler("WriteFile", hwnd); 
 
    /* Copy the array of color indices into the .BMP file. */ 
 
    dwTotal = cb = pbih->biSizeImage; 
    hp = lpBits; 
    while (cb > MAXWRITE)  { 
            if (!WriteFile(hf, (LPSTR) hp, (int) MAXWRITE, 
                          (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) 
                errhandler("WriteFile", hwnd); 
            cb-= MAXWRITE; 
            hp += MAXWRITE; 
    } 
    if (!WriteFile(hf, (LPSTR) hp, (int) cb, 
         (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) 
           errhandler("WriteFile", hwnd); 
 
    /* Close the .BMP file. */ 
 
    if (!CloseHandle(hf)) 
           errhandler("CloseHandle", hwnd); 
 
    /* Free memory. */    GlobalFree((HGLOBAL)lpBits);
} 

 

 

读位图:(伪代码,未翻译)

If open Bitmap file

Read two bytes (type) and if different than 0x4D42 stop

Ignore eight bytes

Read four bytes (start of image data)

Ignore four bytes

Read four bytes (width of bitmap)

Read four bytes (height of bitmap)

Ignore two bytes

Read two bytes (bit count of bitmap) and if different than 24 stop

Read four bytes (compression of bitmap) and if different than BI_RGB stop

Move to start of image data

Allocate memory for image data (3(one byte for red, other for

green other for blue) * ImageWidth * ImageHeight)

Read (3 * ImageWidth * ImageHeight) bytes from file to buffer

Swap the red and blue components of buffer

If ImageHeight is negative

Flip the buffer lines

End if

Close file

 

 

参考资料:

posted on 2006-02-20 13:15 梦在天涯 阅读(1282) 评论(0)  编辑 收藏 引用 所属分类: MFC/QT


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

EMail:itech001#126.com

导航

统计

  • 随笔 - 461
  • 文章 - 4
  • 评论 - 746
  • 引用 - 0

常用链接

随笔分类

随笔档案

收藏夹

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

积分与排名

  • 积分 - 1795685
  • 排名 - 5

最新评论

阅读排行榜