在VC中自建操作BMP位图文件的类

2023-12-09 02:09

本文主要是介绍在VC中自建操作BMP位图文件的类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

有编程经验的程序员都知道:要使应用程序的界面美观不可避免的要使用大量位图。现在流行的可视化编程工具对位图的使用提供了很好的支持,被称为三大可视化开发工具的VBVCDelphi通过封装位图对象对位图使用提供了很好的支持:VB提供了两个功能很强的对象:PictureBoxImage,通过使用它们,装载、显示位图变得非常容易。Delphi中也提供了一个位图对象:TImage,它的功能与用法与VB中的Image类似。在VC中通过使用设备相关类CDCGDI对象类CBitmap来完成位图的操作。

然而在VC中使用CBitmap类必须将BMP位图装入资源中,然后通过类 CBitmap的成员函数使用它,在通过CDC类的成员函数操作它。这样做有两点缺陷:将位图装入资源导致可执行文件增大,不利于软件发行;只能使用资源中有限的位图,无法选取其它位图。而且BMP位图文件是以DIB(设备无关位图)方式保存,BMP位图装入资源后被转换为DDB(设备相关位图),类CBitmap就是对一系列DDB操作的API函数进行了封装,使用起来有一定的局限性,不如DIB可以独立于平台特性。

要弥补使用资源位图的两点不足,就必须直接使用BMP位图文件。VC的示例中提供了一种方法读取并显示BMP位图文件,但使用起来相当的麻烦。首先使用API函数GlobalAlloc分配内存并创建HDIB位图句柄,所有操作只能直接读写内存,然后通过StrechDIBitsSetDIBsToDevice函数来显示于屏幕上,操作起来费时费力。

因此笔者通过研究类CBitmap的封装与DIB结构,使用Win32中提供的新函数,建立了一个专用于操作BMP文件的类,而且完全仿照类CBitmap的实现:从类CGdiObject派生,新类的所有接口与类CBitmap 的部分接口完全相同。这样对于习惯使用CBitmap类接口用法的程序员来说两者的接口在使用上没有什么分别。

首先我们先简单介绍一下DIB的结构。DIB位图既可以存在于内存,也可以以文件形式保存在磁盘上(BMP文件)。所有DIB都包含两部分信息:位图信息(BITMAPINFO),包括位图信息头和颜色表;位图数据。对于内存中DIB的只要有上述两部分就行,而对于DIB文件则还要加上位图文件头。两种结构如图所示:

DIB DIB文件

 

 

 

 

 

 

 

 

 

其次,Win32中提供了一个新函数CreateDIBSection,通过它可以创建一个存储DIB位的内存区域,既可以执行相应的GDI操作,又可以直接通过指向DIB位区域的指针方位DIB位区域。这是一个非常有用的函数,通过它我们可以用DIB替代DDB

在了解了相应的知识后,我们可以自己由类CGdiObject派生一个操作BMP文件的类:CBitmapFile

在自己编写类时有两点值得注意:

    • BitmapFile.h文件中定义类CBitmapFile,首先必须声明类CBitmapFile是从类CGdiObject中公有派生。然后在类中首先使用宏DECLARE_DYNAMICCBitmapFile)表明新类的最高父类是类CObject,是符合MFC的类库规范。紧接着宏DECLARE_DYNAMIC的是声明静态函数FromHandle,这两个声明必须放在类定义的最前面。
    • BitmapFile.cpp文件中类的成员函数的实现前加上IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);表明类CBitmapFile直接派生于类CGdiObject

在类CBitmapFile的声明中有三个函数与类Cbitmap中的定义稍有不同:

    • 在类CbitmapFileLoadBitmap函数的参数是LPCTSTR型,保存的是BMP文件的文件名。
    • 在类CbitmapFileCreateBitmap函数的参数中少了参数nPlanes,在函数内部默认为1
    • 在类CbitmapFileCreateBitmapIndirect函数的参数中多了参数lpBits,它指向指定位图DIB位的内存区域。

在成员函数中最重要的是函数CreateBitmapIndirect和函数LoadBitmap

    • 在函数CreateBitmapIndirect中使用函数CreateDIBSection创建了一个以兼容DC为基础的HBITMAP句柄,并用继承自类CGdiObject 的函数Attach把它与类CGdiObject的句柄m_hObject关联起来。然后将指定位图的DIB位图数据拷贝到由函数CreateDIBSection创建的DIB位的内存区域。
    • 在函数LoadBitmap中首先从指定文件名的文件中读取以结构BITMAPFILEHEADER为大小的数据块,然后由文件头标志判断文件是否为BMP位图文件,然后由BITMAPFILEHEADERbfSize保存的文件大小与文件的真实大小比较文件是否有损坏,再由BITMAPFILEHEADERbfOffBitsBITMAPFILEHEADER结构大小相减计算出位图信息头和颜色表一共的大小,动态申请一块空间保存位图信息头和颜色表信息,再由BITMAPFILEHEADERbfSizebfOffBits相减计算出DIB位图数据的大小,动态申请一块空间保存DIB位图数据,最后调用成员函数CreateBitmapIndirect来创建DIB位图。

在应用程序的OnPaint()事件中绘制DIB位图的方法与使用类CBitmap时绘制位图的方法完全相同,但有一点要注意的是由于CDC类没有提供返回新类CBitmapFile指针类型的将DIB位图选入内存的SelectObject函数,所以在使用SelectObject时要将返回类型强制转换为CbitmapFile *类型。

至此,关于新类CBitmapFile编写中的一些要点和使用时一些要注意的问题就介绍这么多了。

附源文件

//

// 文件描述:定义类CBitmapFile,此类是用于读取BMP文件,涉及读取、

// 建立及一系列常用的操作。

// 文件名: BitmapFile.h

// 时间: 1999-2-11

// 作者: 贾暾

//

#ifndef _CBITMAPFILE_H_

#define _CBITMAPFILE_H_

class CBitmapFile : public CGdiObject

{

DECLARE_DYNAMIC(CBitmapFile)

public:

static CBitmapFile* PASCAL FromHandle(HBITMAP hBitmap);

// Constructors

CBitmapFile();

BOOL LoadBitmap(LPCTSTR lpszFileName);

BOOL CreateBitmap(int nWidth, int nHeight, UINT nBitCount, const void* lpBits);

BOOL CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpBits);

// Attributes

operator HBITMAP() const;

int GetBitmap(BITMAP* pBitMap);

protected:

// Attributes

int GetColorNumber(WORD wBitCount);

public:

// Operations

DWORD SetBitmapBits(DWORD dwCount, const void* lpBits);

DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits);

// Implementation

public:

virtual ~CBitmapFile();

};

#endif

//

// 文件描述:类CBitmapFile内成员函数的实现

// 文件名: BitmapFile.cpp

// 时间: 1999-2-11

// 作者: 贾暾

//

#include "BitmapFile.h"

#include <memory.h>

IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);

CBitmapFile* PASCAL CBitmapFile::FromHandle(HBITMAP hBitmap)

{

return (CBitmapFile*) CGdiObject::FromHandle(hBitmap);

}

CBitmapFile::CBitmapFile()

{

}

BOOL CBitmapFile::LoadBitmap(LPCTSTR lpszFileName)

{

CFile file;

if(!file.Open(lpszFileName,CFile::modeRead|CFile::shareDenyWrite))

{

MessageBox(NULL,"BMP file open error!","warning",MB_OK);

return FALSE;

}

BITMAPFILEHEADER bfhHeader;

file.Read(&bfhHeader,sizeof(BITMAPFILEHEADER));

if(bfhHeader.bfType!=((WORD) ('M'<<8)|'B'))

{

MessageBox(NULL,"The file is not a BMP file!","warning",MB_OK);

return FALSE;

}

if(bfhHeader.bfSize!=file.GetLength())

{

MessageBox(NULL,"The BMP file header error!","warning",MB_OK);

return FALSE;

}

UINT uBmpInfoLen=(UINT) bfhHeader.bfOffBits-sizeof(BITMAPFILEHEADER);

LPBITMAPINFO lpBitmap=(LPBITMAPINFO) new BYTE[uBmpInfoLen];

file.Read((LPVOID) lpBitmap,uBmpInfoLen);

if((* (LPDWORD)(lpBitmap))!=sizeof(BITMAPINFOHEADER))

{

MessageBox(NULL,"The BMP is not Windows 3.0 format!","warning",MB_OK);

return FALSE;

}

DWORD dwBitlen=bfhHeader.bfSize - bfhHeader.bfOffBits;

LPVOID lpBits=new BYTE[dwBitlen];

file.ReadHuge(lpBits,dwBitlen);

file.Close();

BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpBits);

delete lpBitmap;

delete lpBits;

if(!bSuccess)

return FALSE;

return TRUE;

}

BOOL CBitmapFile::CreateBitmap(int nWidth, int nHeight, UINT nBitCount,

const void* lpSrcBits)

{

ASSERT(nBitCount==1||nBitCount==4||nBitCount==8

||nBitCount==16||nBitCount==24||nBitCount==32);

LPBITMAPINFO lpBitmap;

lpBitmap=(BITMAPINFO*) new BYTE[sizeof(BITMAPINFOHEADER) +

GetColorNumber(nBitCount) * sizeof(RGBQUAD)];

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

lpBitmap->bmiHeader.biWidth=nWidth;

lpBitmap->bmiHeader.biHeight=nHeight;

lpBitmap->bmiHeader.biBitCount=nBitCount;

lpBitmap->bmiHeader.biPlanes=1;

lpBitmap->bmiHeader.biCompression=BI_RGB;

lpBitmap->bmiHeader.biSizeImage=0;

lpBitmap->bmiHeader.biClrUsed=0;

BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpSrcBits);

delete lpBitmap;

if(!bSuccess)

return FALSE;

return TRUE;

}

BOOL CBitmapFile::CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpSrcBits)

{

DeleteObject();

LPVOID lpBits;

CDC *dc=new CDC;

dc->CreateCompatibleDC(NULL);

HBITMAP hBitmap=::CreateDIBSection(dc->m_hDC,lpBitmapInfo,DIB_RGB_COLORS,

&lpBits,NULL,0);

ASSERT(hBitmap!=NULL);

delete dc;

Attach(hBitmap);

BITMAP bmp;

GetBitmap(&bmp);

DWORD dwCount=(DWORD) bmp.bmWidthBytes * bmp.bmHeight;

if(SetBitmapBits(dwCount,lpSrcBits)!=dwCount)

{

MessageBox(NULL,"DIB build error!","warning",MB_OK);

return FALSE;

}

return TRUE;

}

CBitmapFile::operator HBITMAP() const

{

return (HBITMAP)(this == NULL ? NULL : m_hObject);

}

int CBitmapFile::GetBitmap(BITMAP* pBitMap)

{

ASSERT(m_hObject != NULL);

return ::GetObject(m_hObject, sizeof(BITMAP), pBitMap);

}

int CBitmapFile::GetColorNumber(WORD wBitCount)

{

ASSERT(wBitCount==1||wBitCount==4||wBitCount==8

||wBitCount==16||wBitCount==24||wBitCount==32);

switch(wBitCount)

{

case 1:

return 2;

case 4:

return 16;

case 8:

return 256;

default:

return 0;

}

}

DWORD CBitmapFile::SetBitmapBits(DWORD dwCount, const void* lpBits)

{

if(lpBits!=NULL)

{

BITMAP bmp;

GetBitmap(&bmp);

memcpy(bmp.bmBits,lpBits,dwCount);

return dwCount;

}

else

return 0;

}

DWORD CBitmapFile::GetBitmapBits(DWORD dwCount, LPVOID lpBits)

{

if(lpBits!=NULL)

{

BITMAP bmp;

GetBitmap(&bmp);

memcpy(lpBits,bmp.bmBits,dwCount);

return dwCount;

}

else

return 0;

}

CBitmapFile::~CBitmapFile()

{

CGdiObject::DeleteObject();

}

<script language=JavaScript1.2 src="../article_bottom.js"></script>

这篇关于在VC中自建操作BMP位图文件的类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/472108

相关文章

VC网络协议

// PCControlDlg.cpp : 实现文件//#include "stdafx.h"#include "PCControl.h"#include "PCControlDlg.h"#include "afxdialogex.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框#ifde

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作,接下来来看一下表的相关操作。 一、创建表 create

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

浙大数据结构:04-树7 二叉搜索树的操作集

这道题答案都在PPT上,所以先学会再写的话并不难。 1、BinTree Insert( BinTree BST, ElementType X ) 递归实现,小就进左子树,大就进右子树。 为空就新建结点插入。 BinTree Insert( BinTree BST, ElementType X ){if(!BST){BST=(BinTree)malloc(sizeof(struct TNo

hibernate修改数据库已有的对象【简化操作】

陈科肇 直接上代码: /*** 更新新的数据并并未修改旧的数据* @param oldEntity 数据库存在的实体* @param newEntity 更改后的实体* @throws IllegalAccessException * @throws IllegalArgumentException */public void updateNew(T oldEntity,T newEntity