与设备无关的位图(DIB)

 

11.4 与设备无关的位图(DIB) 的与设备无关性主要体现在以下两个方面:

 

DIB(Device-indepentent bitmap)

  • DIB

    的颜色模式与设备无关。例如,一个256色的DIB即可以在真彩色显示模式下使用,也可以在16色模式下使用。
  • 256

    色以下(包括256)DIB拥有自己的颜色表,像素的颜色独立于系统调色板。

 由于DIB不依赖于具体设备,因此可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存在磁盘中的,有时也会保存在*.DIB文件中。运行在不同输出设备下的应用程序可以通过DIB来交换图象。还可以用一种RLE算法来压缩图像数据,但一般来说DIB是不压缩的。11.4.1 DIB的结构Borland C++下的框架类库OWL不同,MFC未提供现成的类来封装DIB。尽管Microsoft列出了一些理由,但没有DIB类确实给MFC用户带来很多不便。用户要想使用DIB,首先应该了解DIB的结构。在内存中,一个完整的DIB由两部分组成:一个BITMAPINFO结构和一个存储像素阵列的数组。BITMAPINFO描述了位图的大小,颜色模式和调色板等各种属性,其定义为

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1]; //

颜色表

} BITMAPINFO;

结构用来描述颜色,其定义为

typedef struct tagRGBQUAD {

BYTE rgbBlue; //

蓝色的强度

BYTE rgbGreen; //

绿色的强度

BYTE rgbRed; //

红色的强度

BYTE rgbReserved; //

} RGBQUAD;

保留字节,为 0 注意, RGBQUAD 结构中的颜色顺序是 BGR ,而不是平常的 RGB 结构包含了 DIB 的各种信息,其定义为

typedef struct tagBITMAPINFOHEADER{

DWORD biSize; //

该结构的大小

LONG biWidth; //

LONG biHeight; //

WORD biPlanes; //

WORD biBitCount //

DWORD biCompression; //

DWORD biSizeImage; //

LONG biXPelsPerMeter; //

位图的宽度 ( 以像素为单位 ) 位图的高度 ( 以像素为单位 ) 必须为 1 每个像素的位数 (1 4 8 16 24 32) 压缩方式,一般为 0 BI_RGB ( 未压缩 ) 以字节为单位的图象大小 ( 仅用于压缩位图 ) 以目标设备每米的像素数来说明位图的水平分辨率

LONG biYPelsPerMeter; //

DWORD biClrUsed; /*

DWORD biClrImportant; //

颜色表的颜色数,若为0则位图使用由biBitCount指定的最大颜色数*/重要颜色的数目,若该值为0则所有颜色都重要
以目标设备每米的像素数来说明位图的垂直分辨率

} BITMAPINFOHEADER;

DDB 不同, DIB 的字节数组是从图象的最下面一行开始的逐行向上存储的,也即等于把图象倒过来然后在逐行扫描。另外,字节数组中每个扫描行的字节数必需是 4 的倍数,如果不足要用 0 补齐。 可以存储在 *.BMP *.DIB 文件中。 DIB 文件是以 BITMAPFILEHEADER 结构开头的,该结构的定义为

typedef struct tagBITMAPFILEHEADER {

WORD bfType; //

文件类型,必须为“ BM

DWORD bfSize; //

文件的大小

WORD bfReserved1; //

WORD bfReserved2; //

DWORD bfOffBits; //

0 0 存储的像素阵列相对于文件头的偏移量

} BITMAPFILEHEADER;

紧随该结构的是一个 BITMAPINFOHEADER 结构,然后是 RGBQUAD 结构组成的颜色表 ( 如果有的话 ) ,文件最后存储的是 DIB 的像素阵列。 的颜色信息储存在自己的颜色表中,程序一般要根据颜色表为 DIB 创建逻辑调色板。在输出一幅 DIB 之前,程序应该将其逻辑调色板选入到相关的设备上下文中并实现到系统调色板中,然后再调用相关的 GDI 函数 ( ::SetDIBitsToDevice ::StretchDIBits) 输出 DIB 。在输出过程中, GDI 函数会把 DIB 转换成 DDB ,这项工作主要包括以下两步: DIB 的颜色格式转换成与输出设备相同的颜色格式。例如,在真彩色的显示模式下要显示一个 256 色的 DIB ,则应该将其转换成 24 位的颜色格式。 DIB 像素的逻辑颜色索引转换成系统调色板索引。   11.4.2 编写DIB 由于 MFC 未提供 DIB 类,用户在使用 DIB 时将面临繁重的 Windows API 编程任务。幸运的是, Visual C++ 提供了一个较高层次的 API ,简化了 DIB 的使用。这些 API 函数实际上是由 MFC DibLook 例程提供的,它们位于 DibLook 目录下的 dibapi.cpp myfile.cpp dibapi.h 文件中,主要包括:

ReadDIBFile //

DIB 文件读入内存

SaveDIB //

DIB 保存到文件中

CreateDIBPalette //

DIB 中创建一个逻辑调色板

PaintDIB //

DIBWidth //

显示 DIB 返回 DIB 的宽度

DIBHeight //

返回 DIB 的高度

 

DIB

 

 

 

RGBQUAD

 

BITMAPINFOHEADER

 

DIB

 

DIB

 

 

 

 如果读者对这些函数的内部细节感兴趣,那么可以研究一下dibapi.cppmyfile.cpp文件,但要做好吃苦的准备。即使利用上述API,编写使用DIB的程序仍然不是很轻松。为了满足读者的要求,笔者编写了一个名为CDib的较简单的DIB类,该类是基于上述API的,它的主要成员函数包括:

BOOL Load(LPCTSTR lpszFileName);

该函数从文件中载入DIB,参数lpszFileName说明了文件名。若成功载入则函数返回TRUE,否则返回FALSE

BOOL LoadFromResource(UINT nID);

该函数从资源中载入位图,参数nID是资源位图的ID。若成功载入则函数返回TRUE,否则返回FALSE

CPalette* GetPalette()

返回DIB的逻辑调色板。

BOOL Draw(CDC *pDC, int x, int y, int cx=0, int cy=0);

该函数在指定的矩形区域内显示DIB,它具有缩放位图的功能。参数pDC指向用于绘图的设备上下文,参数xy说明了目的矩形的左上角坐标,cxcy说明了目的矩形的尺寸,cxcy若有一个为0则该函数按DIB的实际大小绘制位图,cxcy的缺省值是0。若成功则函数返回TRUE,否则返回FALSE

int Width(); //

以像素为单位返回DIB的宽度

int Height(); //

以像素为单位返回DIB的高度

 

 

 类的源代码在清单11.311.4列出,CDib类的定义位于CDib.h中,CDib类的成员函数代码位于CDib.cpp中。对于CDib类的代码这里就不作具体解释了,读者只要会用就行。 11.3 CDib.h     11.4 Cdib.cpp     从文件中载入DIB 清除旧位图 有可能没有调色板 从资源中载入DIB搜寻指定的资源载入位图资源 hRes中的内容复制hDIB 有可能没有调色板   显示DIB,该函数具有缩放功能参数xy说明了目的矩形的左上角坐标,cxcy说明了目的矩形的尺寸cy若有一个为0则该函数按DIB的实际大小绘制,cxcy的缺省值是0 11.4.3 使用CDib类的例子现在让我们来看一个使用CDib类的例子。如图11.4所示,程序名为ShowDib,是一个多文档应用程序,它的功能与VCDibLook例程有些类似,可同时打开和显示多个位图。11.4 ShowDib来显示位图 请读者用AppWizard建立一个名为ShowDibMFC工程。程序应该用滚动视图来显示较大的位图,所以在MFC AppWizard的第6步应把CShowDibView的基类改为CScrollView由于ShowDib程序要用到CDib类,所以应该把dibapi.cppmyfile.cppdibapi.hCDib.cppCDib.h文件拷贝到ShowDib目录下,并选择Project->Add to Project->Files命令把这些文件加到ShowDib工程中。ShowDib.h文件中CShowDibApp类的定义之前加入下面一行:当收到调色板消息时,主框架窗口会发送用户定义的WM_DOREALIZE消息通知视图。接下来,需要用ClassWizardCMainFrame加入WM_QUERYNEWPALETTEWM_PALETTECHANGED消息的处理函数,为CShowDibDoc类加入OnOpenDocument函数。最后,请读者按清单11.511.611.7修改程序。 11.5 CMainFrame类的部分代码 通知所有的子窗口  没有活动的MDI子框架窗口只通知活动视图返回TRUE表明实现了逻辑调色板  11.6 CShowDibDoc类的部分代码      载入DIB   11.7 CShowDibView类的部分代码    设置视图的滚动范围  刷新视图  参数决定了该视图是否实现前景调色板  输出DIB   在程序中使用CDib对象的代码很简单。当用户在ShowDib程序中选择File->Open命令并从打开文件对话框中选择了一个BMP文件后,CShowDibDoc::OnOpenDocument函数被调用,该函数调用CDib::Load载入位图。在CShowDibView::OnDraw中,调用CDib::Draw输出位图。在CShowDibView::OnInitialUpdate中,根据DIB的尺寸来确定视图的滚动范围。需要重点研究的是ShowDib如何处理调色板问题的。ShowDib是一个多文档应用程序,可以同时显示多幅位图。由于每个位图一般都有不同的调色板,这样就产生了共享系统调色板的问题。程序必须采取措施来保证只有一个视图的逻辑调色板作为前景调色板使用。当主框架窗口收到WM_QUERYNEWPALETTE消息时,主框架窗口向具有输入焦点的视图发送wParam参数为0WM_DOREALIZE消息,该视图的消息处理函数CShowDibView::OnDoRealize为视图实现前景调色板并在必要时重绘视图,这样活动视图中的位图就具有最佳颜色显示。如果活动视图在实现其前景调色板时改变了系统调色板,或是别的应用程序的前景调色板改变了系统调色板,那么Windows会向所有顶层窗口和重叠窗口发送WM_PALETTECHANGED消息,DibLook的主框架窗口也会收到该消息。主框架窗口对该消息的处理是向所有的视图发送wParam参数为1WM_DOREALIZE消息,通知它们实现各自的背景调色板并在必要时重绘,这样所有的位图都能显示令人满意的颜色。当某一视图被激活时,需要调用OnDoRealize来实现其前景调色板,这一任务由CShowDibView:: OnActivateView函数来完成。

CDib

 

清单

#if !defined MYDIB

#define MYDIB

 

#include "dibapi.h"

 

class CDib

{

public:

CDib();

~CDib();

protected:

HDIB m_hDIB;

CPalette* m_palDIB;

public:

BOOL Load(LPCTSTR lpszFileName);

BOOL LoadFromResource(UINT nID);

CPalette* GetPalette() const

{ return m_palDIB; }

BOOL Draw(CDC *pDC, int x, int y, int cx=0, int cy=0);

int Width();

int Height();

void DeleteDIB();

};

 

#endif

 

 

清单

#include <stdafx.h>

#include "CDib.h"

 

#ifdef _DEBUG

#undef THIS_FILE

static char BASED_CODE THIS_FILE[] = __FILE__;

#endif

 

CDib::CDib()

{

m_palDIB=NULL;

m_hDIB=NULL;

}

 

CDib::~CDib()

{

DeleteDIB();

}

 

void CDib::DeleteDIB()

{

if (m_hDIB != NULL)

::GlobalFree((HGLOBAL) m_hDIB);

if (m_palDIB != NULL)

delete m_palDIB;

}

 

//

BOOL CDib::Load(LPCTSTR lpszFileName)

{

HDIB hDIB;

CFile file;

CFileException fe;

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

{

AfxMessageBox(fe.m_cause);

return FALSE;

}

TRY

{

hDIB = ::ReadDIBFile(file);

}

CATCH (CFileException, eLoad)

{

file.Abort();

return FALSE;

}

END_CATCH

 

DeleteDIB(); //

m_hDIB=hDIB;

 

m_palDIB = new CPalette;

if (::CreateDIBPalette(m_hDIB, m_palDIB) == NULL)

{

// DIB

delete m_palDIB;

m_palDIB = NULL;

}

return TRUE;

}

 

//

BOOL CDib::LoadFromResource(UINT nID)

{

HINSTANCE hResInst = AfxGetResourceHandle();

HRSRC hFindRes;

HDIB hDIB;

LPSTR pDIB;

LPSTR pRes;

HGLOBAL hRes;

//

hFindRes = ::FindResource(hResInst, MAKEINTRESOURCE(nID), RT_BITMAP);

if (hFindRes == NULL) return FALSE;

hRes = ::LoadResource(hResInst, hFindRes); //

if (hRes == NULL) return FALSE;

DWORD dwSize=::SizeofResource(hResInst,hFindRes);

 

hDIB = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize);

if (hDIB == NULL) return FALSE;

pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB);

pRes = (LPSTR) ::LockResource(hRes);

memcpy(pDIB, pRes, dwSize); //

::GlobalUnlock((HGLOBAL) hDIB);

 

DeleteDIB();

m_hDIB=hDIB;

m_palDIB = new CPalette;

if (::CreateDIBPalette(m_hDIB, m_palDIB) == NULL)

{

// DIB

delete m_palDIB;

m_palDIB = NULL;

}

return TRUE;

}

 

int CDib::Width()

{

if(m_hDIB==NULL) return 0;

LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) m_hDIB);

int cxDIB = (int) ::DIBWidth(lpDIB); // Size of DIB - x

::GlobalUnlock((HGLOBAL) m_hDIB);

return cxDIB;

}

 

int CDib::Height()

{

if(m_hDIB==NULL) return 0;

LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) m_hDIB);

int cyDIB = (int) ::DIBHeight(lpDIB); // Size of DIB - y

::GlobalUnlock((HGLOBAL) m_hDIB);

return cyDIB;

}

 

//

//

//cx

BOOL CDib::Draw(CDC *pDC, int x, int y, int cx, int cy)

{

if(m_hDIB==NULL) return FALSE;

CRect rDIB,rDest;

rDest.left=x;

rDest.top=x;

if(cx==0||cy==0)

{

cx=Width();

cy=Height();

}

rDest.right=rDest.left+cx;

rDest.bottom=rDest.top+cy;

rDIB.left=rDIB.top=0;

rDIB.right=Width();

rDIB.bottom=Height();

return ::PaintDIB(pDC->GetSafeHdc(),&rDest,m_hDIB,&rDIB,m_palDIB);

}

 

 

 

T11_4.jpg (24220 bytes)

 

 

 

 

 

#define WM_DOREALIZE WM_USER+200

 

 

 

 

清单

// MainFrm.cpp : implementation of the CMainFrame class

void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)

{

CMDIFrameWnd::OnPaletteChanged(pFocusWnd);

// TODO: Add your message handler code here

 

SendMessageToDescendants(WM_DOREALIZE, 1); //

}

 

BOOL CMainFrame::OnQueryNewPalette()

{

// TODO: Add your message handler code here and/or call default

 

CMDIChildWnd* pMDIChildWnd = MDIGetActive();

if (pMDIChildWnd == NULL)

return FALSE; //

CView* pView = pMDIChildWnd->GetActiveView();

pView->SendMessage(WM_DOREALIZE,0); //

return TRUE; //

}

 

 

清单

// ShowDibDoc.h : interface of the CShowDibDoc class

 

#include "CDib.h"

 

class CShowDibDoc : public CDocument

{

. . .

// Attributes

public:

 

CDib m_Dib;

. . .

};

 

// ShowDibDoc.cpp : implementation of the CShowDibDoc class

 

BOOL CShowDibDoc::OnOpenDocument(LPCTSTR lpszPathName)

{

if (!CDocument::OnOpenDocument(lpszPathName))

return FALSE;

// TODO: Add your specialized creation code here

 

BeginWaitCursor();

BOOL bSuccess=m_Dib.Load(lpszPathName); //

EndWaitCursor();

return bSuccess;

}

 

 

 

清单

// ShowDibView.h : interface of the CShowDibView class

class CShowDibView : public CScrollView

{

. . .

afx_msg LRESULT OnDoRealize(WPARAM wParam, LPARAM lParam);

DECLARE_MESSAGE_MAP()

};

 

// ShowDibView.cpp : implementation of the CShowDibView class

 

BEGIN_MESSAGE_MAP(CShowDibView, CScrollView)

. . .

ON_MESSAGE(WM_DOREALIZE, OnDoRealize)

END_MESSAGE_MAP()

 

void CShowDibView::OnInitialUpdate()

{

CScrollView::OnInitialUpdate();

CSize sizeTotal;

// TODO: calculate the total size of this view

 

CShowDibDoc* pDoc = GetDocument();

sizeTotal.cx = pDoc->m_Dib.Width();

sizeTotal.cy = pDoc->m_Dib.Height();

SetScrollSizes(MM_TEXT, sizeTotal); //

}

 

void CShowDibView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)

{

// TODO: Add your specialized code here and/or call the base class

 

if(bActivate)

OnDoRealize(0,0); //

CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView);

}

 

 

LRESULT CShowDibView::OnDoRealize(WPARAM wParam, LPARAM)

{

CClientDC dc(this);

//wParam

dc.SelectPalette(GetDocument()->m_Dib.GetPalette(),wParam);

if(dc.RealizePalette())

GetDocument()->UpdateAllViews(NULL);

return 0L;

}

 

void CShowDibView::OnDraw(CDC* pDC)

{

CShowDibDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

 

pDoc->m_Dib.Draw(pDC,0,0); //

}

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值