zoukankan      html  css  js  c++  java
  • Windows 位图

    1简介    1

    1.1 DFB    1

    1.2 DDB    1

    1.3 DIB    2

    2相关API    3

    2.1 创建    3

    2.1.1 CreateCompatibleBitmap    3

    2.1.2 CreateBitmap    3

    2.1.3 CreateBitmapIndirect    4

    2.1.4 CreateDIBitmap    4

    2.1.5 CreateDIBSection    5

    2.1.6 小结    6

    2.2 查询    6

    2.3 载入DIB    6

    2.4 访问位图数据    7

    2.5 位图传输    7

    3示例代码    8

    3.1 屏幕拷贝    8

    3.1.1 CaptureScreen    10

    3.1.2 BmpToClipboard    11

    3.1.3 BmpToFile    11

    3.2 读取位图文件并显示    12

    1简介

    下图显示了三种位图:DFBDDBDIB

    图1.1

    1.1 DFB

    DFBDevice-format bitmap)是设备格式位图。假如设备是显示器,那么DFB就在显卡内存中。DFB格式并没有统一的标准,所以直接访问DFB并不是一个明智的做法。事实上,在Windows系统里一般是无法直接访问DFB的,程序员常常使用设备的HDC与之联系,如下面的三行代码将在屏幕上绘制一个矩形,其实质就是通过屏幕的HDC访问DFB,往显存里绘制了一个矩形。

    HDC hDC = GetDC(NULL);            //获得屏幕的 HDC

    Rectangle(hDC,200,100,50,90);            //绘制一个矩形

    ReleaseDC(NULL,hDC);                    //释放 HDC

    1.2 DDB

    DDBDevice-dependent bitmap)是设备相关位图,其实就是HBITMAP。笔者认为它存在的理由之一就是:通过HDC访问DFB的效率有时太低了。它叫设备相关位图是因为它的内部格式与DFB是有关系的,保证了与DFB相互转换、传输时的高效率。

    访问DDB的方法有三种:

    1、通过内存HDC

    2、分配内存,通过GetDIBits获取位图数据,通过SetDIBits将修改后的位图数据写入HBITMAP

    3、创建DDB时获取位图数据的首地址,这样就可以直接访问位图了。不过这种方法创建的位图实质上已经不是DDB了,它不再与DFB有关联,相互转换时不能保证最高的效率。

    DDBDFB可通过BitBlt等函数相互转换、传输。

    1.3 DIB

    DDB的格式随DFB的格式而变,如果需要根据不同的DDB格式编写不同的代码去操纵一个像素,那将是程序员的恶梦。这个时候就需要DIB了。

    DIBDevice-independent bitmap)是设备无关位图,是程序员访问DDB时的位图标准格式。知道了这个格式就可以对位图中的单个像素进行高效的操作了。DIB位图读写完毕后,可通过API函数将其转换为DDB

    DIB的实质就是一种位图格式,而且它基本上就是.bmp文件格式。所以,把DIB理解为加载进内存中的.bmp文件也是可以的。

    DIB通过GetDIBitsSetDIBits访问DDB

    DIB通过SetDIBitsToDevice等函数可直接访问DFB。因为格式不同,所以需要大量的转换,不能保证高效性。

    2相关API

    2.1 创建

    创建位图的API5个:

    CreateBitmap

    CreateBitmapIndirect

    CreateCompatibleBitmap

    CreateDIBitmap

    CreateDIBSection

    下面逐个介绍

    2.1.1 CreateCompatibleBitmap

    使用示例:

    HDC        hDC    =    GetDC(NULL);

    HBITMAP    hBmp    =    CreateCompatibleBitmap(hDC,100,50);

    第一行代码获取屏幕的 hDC

    第二行代码将创建与屏幕兼容的DDB位图,其大小为100×50

    hBmphDC兼容的含义就是其格式尽量与hDCDFB保持一致,这样在与DFB相互转换、传输时效率就会比较高。

    2.1.2 CreateBitmap

    此函数的声明如下:

    HBITMAP CreateBitmap(int nWidth,                //位图宽,单位:像素

                            int nHeight,                    //位图高,单位:像素

                            UINT cPlanes,                //位图颜色面数

                            UINT cBitsPerPel,            //一个像素的比特数

                            CONST VOID *lpvBits);    //对像素进行初始化

    它的实质就是创建了一个三维数组。数组大小为:nWidtnHeighcPlanes,数组中的每个元素代表一个像素,每个像素占用的比特数为cBitsPerPel

    如果要对三维数组进行初始化,请指定参数lpvBits

    现在再来看看上一节的代码:

    HBITMAP    hBmp    =    CreateCompatibleBitmap(hDC,100,50);

    它可以被CreateBitmap函数代替,具体代码如下:

    HBITMAP    hBmp    =    CreateBitmap(100,50

                                ,GetDeviceCaps(hDC,PLANES)

                                ,GetDeviceCaps(hDC,BITSPIXEL)

                                ,NULL);

    2.1.3 CreateBitmapIndirect

    CreateBitmapIndirectCreateBitmap的简化版本,如下面的代码:

    BITMAP bm;

    CreateBitmapIndirect(&bm);

    等价于下面的代码:

    CreateBitmap(bm.bmWidth,bm.bmHeight

                    ,bm.bmPlanes,bm.bmBitsPixel,bm.bmBits);

    2.1.4 CreateDIBitmap

    CreateDIBitmap不是创建一个DIB位图,而是创建一个DDB位图,然后用DIB位图数据对这个DDB位图进行初始化。其声明如下:

    HBITMAP CreateDIBitmap(HDC hdc,

        CONST BITMAPINFOHEADER *lpbmih,    //指定位图的宽和高

        DWORD fdwInit,                            //是否初始化

        CONST VOID *lpbInit,                        //DIB像素数据首地址

        CONST BITMAPINFO *lpbmi,                //DIB信息及颜色表)

        UINT fuUsage);                                //DIB_RGB_COLORS

    它等效于如下代码,即首先创建DDB位图,然后再根据DIB位图数据对其进行初始化。

    HBITMAP hBmp = CreateCompatibleBitmap(hDC

                                    ,lpbmi->biWidth,labs(lpbmi->biHeight));

    if(CBM_INIT==fdwInit)

    {

    SetDIBits(hDC,hBmp,0,labs(lpbmi->biHeight),lpbInit,lpbmi,fuUsage);

    }

    2.1.5 CreateDIBSection

    以上创建的位图都是DDB位图,CreateDIBSection创建的将是一个DIB位图,其声明如下:

    HBITMAP CreateDIBSection(

    HDC hdc, // handle to device context

    CONST BITMAPINFO *pbmi,

    // pointer to structure containing bitmap size,

    // format, and color data

    UINT iUsage, // color data type indicator: RGB values or

    // palette indexes

    VOID **ppvBits, // pointer to variable to receive a pointer to

    // the bitmap's bit values

    HANDLE hSection, // optional handle to a file mapping object

    DWORD dwOffset // offset to the bitmap bit values within the

    // file mapping object

    );

    1个参数hdc一般没什么用,仅当iUsageDIB_PAL_COLORS时才会用到它;

    2个参数pbmi指定了DIB位图的宽度、高度、颜色位及颜色表;

    3个参数iUsage说明了颜色表中的颜色含义。iUsageDIB_RGB_COLORS时,颜色表中的颜色是RGB格式;iUsageDIB_PAL_COLORS时,颜色表中的颜色是索引值,这个时候就需要用第1个参数hdc将索引值转换为RGB值;

    4个参数ppvBits是一个输出参数,*ppvBits是一个void*,它指向了DIB像素数据的首地址。也就是说CreateDIBSection会为DIB位图分配内存,然后把像素数据首地址通过ppvBits传出来,程序员根据此地址即可对DIB位图进行操作。

    最后两个参数一般不用,直接设置为NULL0即可。

    2.1.6 小结

    CreateCompatibleBitmap是最简单且经常用到的;

    CreateBitmap是最正规的;

    CreateBitmapIndirectCreateBitmap的简化版;

    CreateDIBitmap在创建DDB位图后,用DIB位图进行初始化;

    CreateDIBSection最特殊,它创建的不是DDB,而是DIB。也只有它能获得位图数据的地址,能够对位图数据进行直接操作。

    2.2 查询

    给定一个HBITMAP hBmp,如何得知它的宽度、高度等信息?答案就是使用GetObject函数,其示例代码如下:

    BITMAP bm;

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

    此时:

    bm.bmWidth;         //位图宽度,单位:像素

    bm.bmHeight;         //位图高度,单位:像素。如果是DIB,它可能为负

    bm.bmWidthBytes;     //位图一行的字节数

    bm.bmPlanes;         //位图颜色面数

    bm.bmBitsPixel;     //每个像素的比特数

    bm.bmBits;            //如果是DIB,它就是位图数据的地址,否则为NULL

    只有通过CreateDIBSection获得的HBITMAP,它对应的bm.bmBits才不为NULL。此时,下面的代码将获得更加详细的信息:

    DIBSECTION ds;

    GetObject(hBmp,sizeof(ds),&ds);

    DIBSECTION 的定义请参考 MSDN

    2.3 载入DIB

    LoadBitmap        从资源里载入位图

    LoadImage        从资源或文件里载入位图

    2.4 访问位图数据

    GetBitmapBitsSetBitmapBits可用于访问位图中的像素数据。这两个函数不能获得颜色表,属于过时的函数。

    GetDIBitsSetDIBitsGetBitmapBitsSetBitmapBits的升级版本,它们增加了处理颜色表的功能。

    2.5 位图传输

    位图传输主要有如下几个函数

    BitBlt

    MaskBlt

    PlgBlt

    StretchBlt

    TransparentBlt

    上面的函数StretchBlt,可以用SetStretchBltMode设置模式,用GetStretchBltMode获取模式。

    上面几个函数都是在两个HDC之间传输位图,下面两个函数是将DIB位图直接传输到HDC上:

    SetDIBitsToDevice

    StretchDIBits

    3示例代码

    3.1 屏幕拷贝

    Windows系统里,按下 Print Screen 键即可将屏幕位图拷贝到剪贴板中。下面是实现此功能的 MFC 代码:

    HBITMAP CaptureScreen()

    {

    int    cx            =    GetSystemMetrics(SM_CXSCREEN);

    int    cy            =    GetSystemMetrics(SM_CYSCREEN);

    HDC        hDC        =    GetDC(NULL);

    HBITMAP    hBmp        =    CreateCompatibleBitmap(hDC,cx,cy);

    HDC        hMem        =    CreateCompatibleDC(hDC);

    HGDIOBJ    hBmpOld    =    SelectObject(hMem,hBmp);

    BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY);

    SelectObject(hMem,hBmpOld);

    DeleteDC(hMem);

    ReleaseDC(NULL,hDC);

    return hBmp;

    }

     

    void BmpToClipboard(HBITMAP hBmp)

    {

    if(OpenClipboard(NULL))

    {

    EmptyClipboard();

    SetClipboardData(CF_BITMAP,hBmp);

    CloseClipboard();

    }

    }

     

    void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile)

    {

    BITMAP bm;

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

    if(0 == wBitsPixel)

    {

    wBitsPixel = bm.bmBitsPixel;

    }

    DWORD dwSizeClr = 0; //颜色表字节数

    if(wBitsPixel <= 8)

    {

    dwSizeClr = (1 << wBitsPixel) * sizeof(RGBQUAD);

    }

    //一行的字节数

    DWORD dwLineBytes = ((bm.bmWidth * wBitsPixel + 31) & ~31) >> 3;

    //像素数据字节数

    DWORD dwSizeImg = dwLineBytes * bm.bmHeight;

    BITMAPFILEHEADER bmfh;

    bmfh.bfType = 0x4D42; //BM

    bmfh.bfOffBits = sizeof(bmfh) + sizeof(BITMAPINFOHEADER) + dwSizeClr;

    bmfh.bfSize = bmfh.bfOffBits + dwSizeImg;

    bmfh.bfReserved1 = 0;

    bmfh.bfReserved2 = 0;

    BYTE* pBmpFile = (BYTE*)malloc(bmfh.bfSize);

    memcpy(pBmpFile,&bmfh,sizeof(bmfh));

    LPBITMAPINFO lpbi = (LPBITMAPINFO)(pBmpFile + sizeof(bmfh));

    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

    lpbi->bmiHeader.biWidth = bm.bmWidth;

    lpbi->bmiHeader.biHeight = bm.bmHeight;

    lpbi->bmiHeader.biPlanes = 1;

    lpbi->bmiHeader.biBitCount = wBitsPixel;

    lpbi->bmiHeader.biCompression = BI_RGB;

    lpbi->bmiHeader.biSizeImage = dwSizeImg;

    lpbi->bmiHeader.biXPelsPerMeter = 0;

    lpbi->bmiHeader.biYPelsPerMeter = 0;

    lpbi->bmiHeader.biClrUsed = 0;

    lpbi->bmiHeader.biClrImportant = 0;

    HDC hDC = GetDC(NULL);

    GetDIBits(hDC,hBmp,0,bm.bmHeight

    ,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS);

    ReleaseDC(NULL,hDC);

    CFile f;

    f.Open(szFile,CFile::modeWrite | CFile::modeCreate);

    f.Write(pBmpFile,bmfh.bfSize);

    f.Close();

    free(pBmpFile);

    }

     

    void CTestDlg::OnButton3()

    {

    HBITMAP hBmp = CaptureScreen();

    if(hBmp)

    {

    BmpToClipboard(hBmp);

    BmpToFile(hBmp, 0,_T("0.bmp"));

    BmpToFile(hBmp, 1,_T("1.bmp"));

    BmpToFile(hBmp, 4,_T("4.bmp"));

    BmpToFile(hBmp, 8,_T("8.bmp"));

    BmpToFile(hBmp,16,_T("16.bmp"));

    BmpToFile(hBmp,24,_T("24.bmp"));

    BmpToFile(hBmp,32,_T("32.bmp"));

    DeleteObject(hBmp);

    }

    }

    当用户单击Button3时,会调用CTestDlg::OnButton3。它做了三项工作:

    1、调用CaptureScreen(),将屏幕的DFB转换为DDB

    2、调用BmpToClipboard(hBmp);DDB复制到剪贴板里;

    3、调用BmpToFile(hBmp, 0,_T("0.bmp"));DDB保存到文件里。

    下面逐个进行介绍。

    3.1.1 CaptureScreen

    屏幕拷贝的实质就是将显存里的DFB提取出来,转换为DDB

    根据图1.1可知,DFBDDB可通过函数BitBlt相互转换、传输。

    现在的问题是:BitBlt只能在两个HDC之间相互传输位图,并不能直接操纵HBITMAP。这里就需要内存HDC

    CaptureScreen的详细讲解如下:

    //下面两行代码用来获得屏幕的宽度和高度,单位是像素。

    int cx = GetSystemMetrics(SM_CXSCREEN);

    int cy = GetSystemMetrics(SM_CYSCREEN);

    //下面这行代码用来获得屏幕的HDC

    HDC hDC = GetDC(NULL);

    //创建与屏幕兼容的DDB

    HBITMAP hBmp = CreateCompatibleBitmap(hDC,cx,cy);

    //创建与屏幕兼容的内存HDC

    HDC hMem = CreateCompatibleDC(hDC);

    //DDB选入内存HDC

    HGDIOBJ hBmpOld = SelectObject(hMem,hBmp);

    //DFB传输给DDB

    BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY);

    //恢复内存HDC原来的位图

    SelectObject(hMem,hBmpOld);

    //销毁内存HDC

    DeleteDC(hMem);

    //释放屏幕HDC

    ReleaseDC(NULL,hDC);

    3.1.2 BmpToClipboard

    BmpToClipboard函数把DDB复制到系统剪贴板内,代码很简单,不用多解释。

    3.1.3 BmpToFile

    BmpToFile函数用来把DDB转换为DIB,然后把DIB存入文件里。它的参数如下所示:

    void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile)

    第一个参数是DDB

    第二个参数是转换为DIB时的颜色位数。0 表示采用DDB的颜色位数,其它的取值有:1248162432。颜色位数越大,颜色数就越多,生成的文件越大;

    第三个参数是保存时的文件名。

    BmpToFile函数的关键点有:

    1、调用GetDIBits,把DDB的位图数据以DIB的格式提取出来;

    2、调用f.Write(pBmpFile,bmfh.bfSize);DIB数据写入文件。下面就是.bmp文件的格式:

    图3.1

    首先是一个BITMAPFILEHEADERpBmpFile指向它;

    接着是一个BITMAPINFOHEADERlpbi指向它;BITMAPINFO的第一个成员变量就是BITMAPINFOHEADER型的,所以也可以把lpbi看做BITMAPINFO*

    对于颜色位数小于16DIB需要定义颜色表,即定义多个RGBQUAD。对于颜色位数大于等于16DIB,一般是不需要RGBQUAD的。

    lpbi->bmiColors指向颜色表。颜色表的项数为(1 << wBitsPixel),颜色表的字节数为(1 << wBitsPixel) * sizeof(RGBQUAD);

    中间还有一个"未定义",它可以是零字节也可以是多个字节,内容随便填。

    最后是像素数据,pBmpFile + bmfh.bfOffBits指向它。

    现在再看看GetDIBits的代码:

    GetDIBits(hDC,hBmp,0,bm.bmHeight

    ,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS);

    5个参数pBmpFile + bmfh.bfOffBits指向像素数据;

    6个参数lpbi是一个BITMAPINFO*lpbi->bmiColors指向颜色表。

    调用GetDIBits将改写颜色表(lpbi->bmiColors)和像素数据(pBmpFile + bmfh.bfOffBits)。

    3.2 读取位图文件并显示

    读取位图文件并显示的代码有两份。

    第一份代码如下所示:

    void CTestDlg::OnButton4()

    {

    HBITMAP hBmp = (HBITMAP)LoadImage(NULL,_T("0.bmp")

    ,IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);

    if(hBmp)

    {

    BITMAP bm;

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

    CClientDC dc(this);

    HDC hMem = CreateCompatibleDC(dc.m_hDC);

    HGDIOBJ hBmpOld = SelectObject(hMem,hBmp);

    ::BitBlt(dc.m_hDC,0,0,bm.bmWidth,bm.bmHeight,hMem,0,0,SRCCOPY);

    SelectObject(hMem,hBmpOld);

    DeleteDC(hMem);

    DeleteObject(hBmp);

    }

    }

    说明:

    1、载入位图使用了LoadImage函数。

    2、显示位图的步骤:

    1)创建一个与屏幕兼容的内存HDC

    2)将载入的位图选入内存HDC

    3)调用BitBlt将内存HDC的内容传输到屏幕HDC上;

    4)销毁内存HDC

    第二份代码如下所示:

    void CTestDlg::OnButton5()

    {

    CFile f;

    if(f.Open(_T("1.bmp"),CFile::modeRead))

    {

    DWORD dwSize = f.GetLength();

    if(dwSize > sizeof(BITMAPFILEHEADER)

    + sizeof(BITMAPINFOHEADER))

    {

    BYTE* pBmpFile = (BYTE*)malloc(dwSize);

    f.Read(pBmpFile,dwSize);

    BITMAPFILEHEADER*pbmfh =

    (BITMAPFILEHEADER*)pBmpFile;

    if(pbmfh->bfType == 0x4D42)

    {

    BITMAPINFOHEADER*pbmih =

    (BITMAPINFOHEADER*)(pBmpFile + sizeof(BITMAPFILEHEADER));

    CClientDC dc(this);

    SetDIBitsToDevice(dc.m_hDC

    ,0,0,pbmih->biWidth,pbmih->biHeight

    ,0,0,0,pbmih->biHeight

    ,pBmpFile + pbmfh->bfOffBits

    ,(BITMAPINFO*)pbmih

    ,DIB_RGB_COLORS);

    }

    free(pBmpFile);

    }

    f.Close();

    }

    }

    说明:

    1、载入位图就是打开位图文件,然后将所有内容载入到内存;

    2、显示位图没有使用BitBlt,而是使用SetDIBitsToDevice直接把DIB传输到屏幕。其中pBmpFile + pbmfh->bfOffBits指向像素数据,(BITMAPINFO*)pbmih指向BITMAPINFOpbmih->bmiColors指向颜色表。

  • 相关阅读:
    sqli-libs(38-41(堆叠注入)关)
    Vue 短信验证码逻辑
    promise(二)
    js类型转换
    微信小程序的组件传值
    微信小程序首页跳转详情页
    微信小程序的事件绑定
    微信小程序简介
    webpack4入门教程十分钟上手指南
    Vue 全局注册组件和局部注册组件
  • 原文地址:https://www.cnblogs.com/hanford/p/6164445.html
Copyright © 2011-2022 走看看