• Bitmap位图文件读取、保存、屏幕截图


            虽然现在网上已经有很多位图读取、保存的文章,很多写的都很详细,提供的源代码功能也很强大,但是我仍然要自己重写一个位图加载程序。主要是因为这些大牛们的文章写的太深奥了,代码功能太强大了,以至于像我这样的菜鸟读不懂。所以,我要力求简洁。省略掉一些细节,比方说调色板。为了能够方便容易操作,我的程序只支持24位以上的位图文件加载。

            

           首先,了解下位图文件的结构。24位以上的位图文件包含3个部分:位图文件头(BITMAPFILEHEADER)、位图信息头(BITMAPINFOHEADER)、位图数据。

    下面是MSDN中两个信息头的具体定义:

    typedef struct tagBITMAPFILEHEADER {
        WORD    bfType; //图像类型:必须是‘BM’(BM的16进制编码为:0x4d42)
        DWORD   bfSize; //位图文件大小。
        WORD    bfReserved1; //保留值。必须为0
        WORD    bfReserved2; //保留值。必须为0
        DWORD   bfOffBits; //从文件开头到像素数据的偏移量。
    } BITMAPFILEHEADER, *PBITMAPFILEHEADER;
    
    typedef struct tagBITMAPINFOHEADER{
        DWORD  biSize; //当前结构体的大小。
        LONG   biWidth; //位图的宽度。单位是像素
        LONG   biHeight; //位图高度。单位是像素
        WORD   biPlanes; //位图平面个数。必须是1
        WORD   biBitCount //位图的位数,也就是位图深度。可以是1、4、8、16、24、32。16位以下的位图文件含有调色板信息。
            DWORD  biCompression; //压缩方式。位图没有压缩,赋予BI_RGB参数(也就是0)。
        DWORD  biSizeImage; //位图数据占用的字节数。可以设置为默认值0。
        LONG   biXPelsPerMeter; //指定目标设备水平分辨率,单位是每米的像素个数。可设置为0
        LONG   biYPelsPerMeter; //指定目标设备垂直分辨率,同上。
        DWORD  biClrUsed; //指定本图像实际用到的像素。可设置为0
        DWORD  biClrImportant; //指定本图像重要的颜色个数。可设置为0,表示所有颜色都重要。
    } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
    


    接下来就是位图数据了:

    位图按行存贮,位图的宽度不等于位图的行字节数。每个像素占用 biBitCount/8 个字节,且每行占用的字节数必须是4字节的整数倍!例如:101*101*24的位图,行占用字节数为:101*3=303,而303%4!=0,所以行占用字节数需修改为304,此时位图数据的实际大小为304*101,上面的biSizeImage就可设置为304*101。

    下面是保存位图文件的结构体填充例:

        BITMAPFILEHEADER bmheader;
        memset(&bmheader,0,sizeof(bmheader));
        bmheader.bfType=0x4d42;     //图像格式。必须为'BM'格式。
        bmheader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); //从文件开头到数据的偏移量
        bmheader.bfSize = m_nWidthBytes*m_nHeight + bmheader.bfOffBits;//文件大小
    
        BITMAPINFOHEADER bmInfo;
        memset(&bmInfo,0,sizeof(bmInfo));
        bmInfo.biSize = sizeof(bmInfo);
        bmInfo.biWidth = m_nWidth; 
        bmInfo.biHeight = m_nHeight; 
        bmInfo.biPlanes = 1; 
        bmInfo.biBitCount =  m_nDeep;
        bmInfo.biCompression = BI_RGB;

    注:m_nWidthBytes 可这样计算:int m_nWidthBytes = ((m_nWidth*m_nDeep/8 + 3)/4) * 4;


    下面是一个用来创建DIB位图,获得HBITMAP句柄一个API。

    HBITMAP CreateDIBSection(
      HDC hdc,                 // handle to DC
      CONST BITMAPINFO *pbmi,  // bitmap data
      UINT iUsage,             // data type indicator
      VOID **ppvBits,          // bit values
      HANDLE hSection,         // handle to file mapping object
      DWORD dwOffset           // offset to bitmap bit values
    );
    (这里就不详细介绍了,可以去百科里看看,如:http://www.hudong.com/wiki/CreateDIBSection


    大致说下打开文件文件的流程:

    1.读入文件头BITMAPFILEHEADER数据。

    2.读入位图信息头BITMAPINFOHEADER数据。

    3.读取位图数据。

    4.使用CreateDIBSection创建Dib位图,将读取到的数据拷贝给Dib位图。

    然后就可以使用Dib位图句柄进行位图的操作了。


    保存文件流程:

    1.填写文件头BITMAPFILEHEADER,并写入文件。

    2.填写位图信息头BITMAPINFOHEADER,并写入文件。

    3.获得位图数据(GetBitmapBits,或者是在CreateDIBSection时,将数据地址记录下来),将数据写入文件。


    截图功能(参考样例见文章末尾):

    1.创建掩码DC(CreateCompatibelDC),掩码位图(CreateCompatibleBitmap),将位图选到DC中。

    2.将 要截图的窗口的DC内容拷贝(BitBlt)给掩码DC(也就是拷贝到了掩码位图上了)。

    3.接着就是上面说到的保存文件流程了。




    附件:我将上面的操作封装起来。

    //DIBBitmap.h
    #pragma once
    
    class CDIBBitmap
    {
    public://方法
    
        CDIBBitmap(void);
        ~CDIBBitmap(void);
    
        bool create(HDC hdc,int width,int height,int deep);//创建位图
        bool create(HDC hDC,HBITMAP hBitmap);//通过拷贝来创建位图
    
        bool load(HDC hDC,char *szFileName);//从文件中价值位图
        bool save(char *szFileName);//保存位图到文件
    
        void clear(void);//清空数据
    
        void sampleRender(HDC hDC,int x,int y);//简单显示
    
    public://属性
    
        //获得位图
        inline HBITMAP getBitmap(void){ return m_hDibBmp; }
        //获得数据
        inline BYTE * getBmpData(void){ return m_pBmpData; }
        //获得宽度
        inline int  getWidth(void){ return m_nWidth; }
        //获得高度
        inline int  getHeight(void){ return m_nHeight; }
        //获得位深度
        inline int  getDeep(void){ return m_nDeep; }
        //获得一行字节数
        inline int  getWidthBytes(void){ return m_nWidthBytes; }
       
    protected://成员
        HBITMAP     m_hDibBmp;
        BYTE        *m_pBmpData;//指向位图数据
        int         m_nWidth;   //宽度。(单位:像素pixel)
        int         m_nHeight;  //高度。pixel
        int         m_nDeep;    //深度。bit。深度必须大于24位
        int         m_nWidthBytes;//一行所需的字节数。必须是4字节的整数倍。
    };


    #include <Windows.h>
    #include <stdio.h>
    #include <cassert>
    
    #include "DIBBitmap.h"
    
    
    class FileHoder
    {
        FILE *m_pFile;
    
    public:
    
        FileHoder(FILE *pFile)
            : m_pFile(pFile)
        {}
    
        ~FileHoder()
        {
            if (m_pFile) fclose(m_pFile);
        }
    };
    
    //////////////////////////////////////////////////////////////////////////
    CDIBBitmap::CDIBBitmap(void)
    {
        m_hDibBmp = NULL;
        m_pBmpData = NULL;
        m_nWidth = 0;   //宽度。(单位:像素pixel)
        m_nHeight = 0;  //高度。pixel
        m_nDeep = 0;    //深度。bit。深度必须大于24位
        m_nWidthBytes = 0;
    }
    
    CDIBBitmap::~CDIBBitmap(void)
    {
        clear();
    }
    
    bool CDIBBitmap::create(HDC hdc, int width, int height, int deep)
    {
        clear();
    
        m_nWidth = width;
        m_nHeight = height;
        m_nDeep = deep;
        m_nWidthBytes = ((m_nWidth*m_nDeep / 8 + 3) / 4) * 4;
        m_pBmpData = nullptr;
    
        BITMAPINFO bmInfo;
        memset(&bmInfo, 0, sizeof(bmInfo));
        bmInfo.bmiHeader.biSize = sizeof(bmInfo);
        bmInfo.bmiHeader.biWidth = m_nWidth;
        bmInfo.bmiHeader.biHeight = m_nHeight;
        bmInfo.bmiHeader.biPlanes = 1;
        bmInfo.bmiHeader.biBitCount = m_nDeep;
        bmInfo.bmiHeader.biCompression = BI_RGB;
    
        //如果创建成功,m_pBmpData将指向位图的像素区域。
        m_hDibBmp = CreateDIBSection(hdc, &bmInfo, DIB_RGB_COLORS, (void**) &m_pBmpData, NULL, 0);
        if (m_hDibBmp == NULL)
        {
            return false;
        }
    
        return true;
    }
    
    bool CDIBBitmap::create(HDC hDC, HBITMAP hBitmap)
    {
        if (NULL == hDC || NULL == hBitmap)
        {
            return false;
        }
    
        //获得位图信息
        BITMAP bm;
        GetObject(hBitmap, sizeof(bm), &bm);
        //创建位图
        if (!create(hDC, bm.bmWidth, bm.bmHeight, bm.bmBitsPixel))
        {
            return false;
        }
    
        //获得hBitmap数据,并拷贝给Dib位图
        GetBitmapBits(hBitmap, bm.bmWidthBytes*bm.bmHeight, getBmpData());
        return true;
    }
    
    void CDIBBitmap::clear(void)
    {
        if (m_hDibBmp != NULL)
        {
            DeleteObject(m_hDibBmp);
        }
        m_hDibBmp = NULL;
        m_pBmpData = NULL;
    }
    
    
    void CDIBBitmap::sampleRender(HDC hDC, int x, int y)
    {
        if (!m_hDibBmp) return;
    
        HDC memDC = CreateCompatibleDC(0);
        SelectObject(memDC, m_hDibBmp);
    
        BitBlt(hDC, x, y, m_nWidth, m_nHeight, memDC, 0, 0, SRCCOPY);
    
        DeleteDC(memDC);
    }
    
    bool CDIBBitmap::load(HDC hDC, char *szFileName)
    {
        assert(hDC && szFileName && "CDIBBitmap::load");
    
        FILE *fp = NULL;
        fopen_s(&fp, szFileName, "rb");
        if (NULL == fp)
        {
            return false;
        }
    
        FileHoder holder(fp);
    
        //读入文件头
        BITMAPFILEHEADER bmheader;
        if (fread(&bmheader, sizeof(bmheader), 1, fp) != 1)
        {
            return false;
        }
    
        //无效的位图文件
        if (bmheader.bfType != 0x4d42)
        {
            return false;
        }
    
        //读入位图信息头
        BITMAPINFOHEADER bmInfo;
        if (fread(&bmInfo, sizeof(bmInfo), 1, fp) != 1)
        {
            return false;
        }
    
        //注:目前程序不支持调色板数据位图加载。
        if (bmInfo.biBitCount < 16)
        {
            return false;
        }
    
        //创建DIB位图
        if (!create(hDC, bmInfo.biWidth, bmInfo.biHeight, bmInfo.biBitCount))
        {
            return false;
        }
    
        //定位到像素数据
        fseek(fp, bmheader.bfOffBits, SEEK_SET);
    
        //读入像素数据
        if (fread(m_pBmpData, m_nWidthBytes, m_nHeight, fp) != m_nHeight)
        {
            return false;
        }
    
    
        return true;
    }
    
    bool CDIBBitmap::save(char *szFileName)
    {
        assert(szFileName && "CDIBBitmap::save");
    
        if (NULL == m_hDibBmp || NULL == m_pBmpData)
        {
            return false;
        }
    
        FILE *fp = NULL;
        fopen_s(&fp, szFileName, "wb");
        if (NULL == fp)
        {
            return false;
        }
    
        FileHoder hoder(fp);
    
        BITMAPFILEHEADER bmheader;
        memset(&bmheader, 0, sizeof(bmheader));
        bmheader.bfType = 0x4d42;     //图像格式。必须为'BM'格式。
        bmheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //从文件开头到数据的偏移量
        bmheader.bfSize = m_nWidthBytes*m_nHeight + bmheader.bfOffBits;//文件大小
    
        BITMAPINFOHEADER bmInfo;
        memset(&bmInfo, 0, sizeof(bmInfo));
        bmInfo.biSize = sizeof(bmInfo);
        bmInfo.biWidth = m_nWidth;
        bmInfo.biHeight = m_nHeight;
        bmInfo.biPlanes = 1;
        bmInfo.biBitCount = m_nDeep;
        bmInfo.biCompression = BI_RGB;
    
        fwrite(&bmheader, sizeof(bmheader), 1, fp);
        fwrite(&bmInfo, sizeof(bmInfo), 1, fp);
        for (int i = 0; i < m_nHeight; ++i)
        {
            int index = m_nWidthBytes*i;
            fwrite(m_pBmpData + index, m_nWidthBytes, 1, fp);
        }
    
        return true;
    }
    
    //////////////////////////////////////////////////////////////////////////



    截图功能参考代码:

    //获得设备描述表
    m_hDeviceContext=GetDC(m_hWnd);
    //创建掩码dc
    m_hBackDC=CreateCompatibleDC(m_hDeviceContext);
    //创建掩码位图
    m_bBackBitmap=CreateCompatibleBitmap(m_hDeviceContext,m_nWidth,m_nHeight);
    SelectObject(m_hBackDC,m_bBackBitmap);
    ...
    将m_hDeviceContext的内容使用BitBlt拷贝给m_hBackDC
    ...
    CDIBBitmap bmp;
    if(!bmp.create(m_hBackDC,m_bBackBitmap))
    {
    	g_log.write("截图失败!");
    }
    else
    {
    	CreateDirectory(TEXT("shot"),NULL);
    	char buffer[64];
    	SYSTEMTIME tm;
    	GetLocalTime(&tm);
    	sprintf_s(buffer,64,"shot/%d_%d_%d_%d_%d_%d.bmp",
    		tm.wYear,tm.wMonth,tm.wDay,tm.wHour,tm.wMinute,tm.wSecond);
    	if(!bmp.save(buffer))
    	{
    		g_log.writex("保存位图【%s】失败!",buffer);
    	}
    }




  • 相关阅读:
    关系数据库设计一般方法 范式及完整性
    left join, right join , inner join, join, union的意义
    mysql 事务类型表的用法
    Java中静态变量与非静态变量的区别
    JSP生命周期
    Java&Tomcat环境变量配置
    JSP两种声明变量的区别
    一个web页面的访问的过程
    Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 解决
    IDEA:修改JAVA文件自动引入import.*包
  • 原文地址:https://www.cnblogs.com/ygxsk/p/7694023.html
Copyright © 2020-2023  润新知