西安交通大学流体机械研究所
* ?" q1 h+ J9 H: h2 Y7 t张义云
; t$ i+ M" T ~& P& T6 K5 n9 w. P4 f K) b f
---- 用普通方法显示BMP位图,占内存大,速度慢,在图形缩小时,失真严重,
- ~2 v/ V& P6 [) g在低颜色位数的设备上显示高颜色位数的图形图形时失真大。本文采用视频函数
) V& ?7 s; n2 I3 p4 ~显示BMP位图,可以消除以上的缺点。
2 Y; n$ {6 C0 p% c3 k. Y1 @; m
---- 一、BMP文件结构
---- 1. BMP文件组成
* a3 W; f! z: [9 |) K$ d. V, X
---- BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
" T' A% u) a: k2 _* j1 K7 {7 }1 G
---- 2. BMP文件头
6 J8 r/ e' s* |# r5 q3 K8 K" e
---- BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息
% p3 ^$ k8 {7 I& N。
" W( U) V3 [' X n# r9 t
---- 其结构定义如下:
m) Y6 V! y/ Y3 u8 z2 O2 h
typedef struct tagBITMAPFILEHEADER
{
0 L% s( R+ g: t! q9 ?3 G# l) c2 VWORDbfType; // 位图文件的类型,必须为BM
DWORD bfSize; // 位图文件的大小,以字节为单位
WORDbfReserved1; // 位图文件保留字,必须为0
WORDbfReserved2; // 位图文件保留字,必须为0
5 \9 _% \# _" n% Z. H+ JDWORD bfOffBits; // 位图数据的起始位置,以相对于位图
5 |3 _1 Y5 `2 q2 d+ V9 g+ k% U$ \// 文件头的偏移量表示,以字节为单位
0 S: V* o& i1 J, @} BITMAPFILEHEADER;
. z8 h3 r. q* \
; k2 Q+ o% B9 Z9 |& ]( y3 V, E% N
---- 3. 位图信息头
----
/ z2 L& l* r* C. b
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节数
LONGbiWidth; // 位图的宽度,以像素为单位
LONGbiHeight; // 位图的高度,以像素为单位
( D- a* l, k+ U$ kWORD biPlanes; // 目标设备的级别,必须为1
) X: k3 [2 Z6 W! VWORD biBitCount// 每个像素所需的位数,必须是1(双色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
0 o9 ]$ w. D& c5 _8 i// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
& v1 b% @4 B2 a! e3 E4 V$ M# YDWORD biSizeImage; // 位图的大小,以字节为单位
LONGbiXPelsPerMeter; // 位图水平分辨率,每米像素数
) r6 Q/ A/ `! Q( c d2 N' S; YLONGbiYPelsPerMeter; // 位图垂直分辨率,每米像素数
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
) ~( L, k% P% M% ]& zDWORD biClrImportant;// 位图显示过程中重要的颜色数
, W# I4 b- } n) j5 J0 Y} BITMAPINFOHEADER;
---- 4. 颜色表
! h' | m3 \: H' @ A3 e. u' C---- 颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个
RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD {
% _3 K4 g4 ^' Q4 v9 B9 H# XBYTErgbBlue;// 蓝色的亮度(值范围为0-255)
BYTErgbGreen; // 绿色的亮度(值范围为0-255)
- j* r+ ~( N% x- c' H' z8 ?; R% Y6 iBYTErgbRed; // 红色的亮度(值范围为0-255)
BYTErgbReserved;// 保留,必须为0
} RGBQUAD;
7 i1 n* X U+ g颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
: \3 K2 l+ {2 \6 }& v2 F当biBitCount=1,4,8时,分别有2,16,256个表项;
5 z& x) T8 F7 G( @当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO {
. C X5 A/ {+ q3 w+ g0 ?BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;
( I U) S! }4 n' U) {( f
7 x5 `( N( c8 d! |( S# @2 u' A, u
---- 5. 位图数据
---- 位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右
,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
; w. I/ Z) Y& I$ `# }* M当biBitCount=8时,1个像素占1个字节;
0 q* q& T: K" |* g( D& }) Y, o当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,
一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
$ d# P; a# F3 u' D: I3 B// 一个扫描行所占的字节数
( K) I0 X$ Y) J' I! y+ I$ O' UDataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
0 B* y% m' x! x' X4 [6 m# o位图数据的大小(不压缩情况下):
DataSize= DataSizePerLine* biHeight;
# x5 ?1 `+ C( ? U. `3 H$ W7 A
- J1 H2 N, a8 b! K8 s+ H
---- 二、BMP位图一般显示方法
---- 1. 申请内存空间用于存放位图文件
---- GlobalAlloc(GHND,FileLength);
/ H. Y2 q& |2 c9 W) B% r* O
---- 2. 位图文件读入所申请内存空间中
---- LoadFileToMemory( mpBitsSrc,mFileName);
0 c+ f) A' M5 Z2 q$ D
---- 3. 在OnPaint等函数中用创建显示用位图
; Y5 i7 B( J: r: k) b2 G- ?2 k
---- 用CreateDIBitmap()创建显示用位图,用CreateCompatibleDC()创建兼容
. K! L% r% c' e0 ^6 M5 O4 jDC,
. G" l+ ]( e/ }" ^" n9 P. ]& g! w1 }, S6 I2 c4 V0 P, G9 @/ I
---- 用SelectBitmap()选择显示位图。
- x; ^" R, a) S9 K1 L3 e
---- 4. 用BitBlt或StretchBlt等函数显示位图
$ _' z' ^: q0 Q9 h r' S, y6 i% L8 F: q8 I! ]' ~: _0 k; g! }/ a
---- 5. 用DeleteObject()删除所创建的位图
5 Q0 n0 F! L) w6 J% e! t. A7 H. |* B$ ^; Q8 \% F p
---- 以上方法的缺点是: 1)显示速度慢; 2) 内存占用大; 3) 位图在缩小显示
# H1 A$ P# w% u& C+ x* z时图形失真大,(可通过安装字体平滑软件来解决); 4) 在低颜色位数的设备上(
) j1 u( f# w4 u0 l6 r3 I1 R7 R5 d如256显示模式)显示高颜色位数的图形(如真彩色)图形失真严重。
/ t4 ^1 S6 s4 w
---- 三、BMP位图缩放显示
/ D2 u& x, p. ]; r9 U
---- 用DrawDib视频函数来显示位图,内存占用少,速度快,而且还可以对图形
进行淡化(Dithering)处理。淡化处理是一种图形算法,可以用来在一个支持比
% r* q% Z$ ]% {" i+ T3 t图像所用颜色要少的设备上显示彩色图像。BMP位图显示方法如下:
---- 1. 打开视频函数DrawDibOpen(),一般放在在构造函数中
. Y+ g0 V1 g7 W- @! M
---- 2. 申请内存空间用于存放位图文件
) E; }+ z! B( l8 W, ]1 H# V" n) ^" k7 o9 V0 P
---- GlobalAlloc(GHND,FileLength);
7 i: W7 X- N6 w2 S0 v* W6 I
---- 3. 位图文件读入所申请内存空间中
, v/ x" f! H' j4 `; J; }
---- LoadFileToMemory( mpBitsSrc,mFileName);
4 f* n d+ W q9 M) Y3 Y# W
---- 4. 在OnPaint等函数中用DrawDibRealize(),DrawDibDraw()显示位图
---- 5. 关闭视频函数DrawDibClose(),一般放在在析构函数中
0 Y; N$ \" @- z. v9 L8 \& t- I* {' b9 U Z2 }. ~: D3 |4 A. y# I
---- 以上方法的优点是: 1)显示速度快; 2) 内存占用少; 3) 缩放显示时图形
失真小,4) 在低颜色位数的设备上显示高颜色位数的图形图形时失真小; 5) 通
5 w6 U: p9 T1 ]' S过直接处理位图数据,可以制作简单动画。
6 Y0 ?3 a8 X2 q8 t8 ~4 A
---- 四、CViewBimap类编程要点
---- 1. 在CViewBimap类中添加视频函数等成员
% J- l8 A/ y3 l! d
HDRAWDIB m_hDrawDib; // 视频函数
HANDLEmhBitsSrc; // 位图文件句柄(内存)
LPSTR mpBitsSrc; // 位图文件地址(内存)
BITMAPINFOHEADER *mpBitmapInfo; // 位图信息头
9 p; X9 s7 b! r6 E$ H8 ?/ _$ o6 H
% t6 z, Q0 d2 |, r7 }, ^* c; w
---- 2. 在CViewBimap类构造函数中添加打开视频函数
" v+ y/ T9 y4 l' \9 i6 N---- m_hDrawDib= DrawDibOpen();
---- 3. 在CViewBimap类析构函数中添加关闭视频函数
: r( _1 x2 B! V! @- R
if( m_hDrawDib != NULL)
{
DrawDibClose( m_hDrawDib);
" F6 @, d, h# b$ L* K- I4 ^m_hDrawDib = NULL;
}
! T; |: G' Q3 G3 s' v# j0 Z3 _0 o. F" F }* F0 w: f0 T" w4 w9 N- Y, y
---- 4. 在CViewBimap类图形显示函数OnPaint中添加GraphicDraw()
7 Z4 y$ h% N; a/ i bvoidCViewBitmap::OnPaint()
{
CPaintDC dc(this); // device context for painting
GraphicDraw( );
}
; o* s$ m1 {$ A1 U. rg. H6 k) Y l+ s* d( |
voidCViewBitmap::GraphicDraw( void )
{
CClientDC dc(this); // device context for painting
9 M8 C1 A/ h3 G! b7 M* l1 `BITMAPFILEHEADER *pBitmapFileHeader;
ULONG bfoffBits= 0;
CPoint Wid;
# L* _. R5 l* @
// 图形文件名有效 (=0 BMP)
" Q: I8 x% c- Pif( mBitmapFileType < ID_BITMAP_BMP ) return;
+ K- q' v/ [8 [; A
// 图形文件名有效 (=0 BMP)
# X+ V) w6 u. C5 g7 B5 W// 准备显示真彩位图
pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;
7 H: b' ]5 {. w" M+ nbfoffBits= pBitmapFileHeader->bfOffBits;
8 V" G# I3 p7 Q0 \/ B) _- P) C4 C+ ]3 S6 `& k! l
// 使用普通函数显示位图
% M* M1 ~- \# [: ^. R, n, F7 {& J6 V0 e- G( M4 Y
if( m_hDrawDib == NULL || mDispMethod == 0)
$ W) t2 j- @& D2 f# K9 `% j{
# n( K. v+ l3 n' t7 U. `HBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,
mpBitmapInfo, CBM_INIT, mpBitsSrc+bfoffBits,
: m2 B; | w3 Q(LPBITMAPINFO) mpBitmapInfo,DIB_RGB_COLORS);
// 建立位图
$ B8 b8 C$ Q7 e0 eHDC hMemDC=::CreateCompatibleDC(dc.m_hDC);// 建立内存
" h5 Z- J) _" @; g( z7 n/ DHBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 选择对象
; r, v, ]- c+ j2 }9 h8 M! y) y) p8 \// 成员CRect mDispR用于指示图形显示区域的大小.
// 成员CPoint mPos用于指示图形显示起始位置坐标.
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
N! `8 G/ G# j7 bmPos.x= mpBitmapInfo->biWidth - mDispR.Width() ;
8 k* F) B5 h0 i! {0 `if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
. E+ G' D. |+ x) }+ u" O5 {if( mPos.x < 0 ) mPos.x= 0;
if( mPos.y < 0 ) mPos.y= 0;
1 |- w) }+ ^9 @3 M3 d* ?* s! g3 l- w& ?* e x" n+ r
if( mFullViewTog == 0)
{
1 k# j) x. | l) v4 T// 显示真彩位图
7 [7 y" j+ F' F& z+ g5 z::BitBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
7 I' I0 J0 B( F& t r/ u. bhMemDC,mPos.x,mPos.y, SRCCOPY);
} else {
2 I: C! u# @" ?+ p::StretchBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),
7 Y G, I0 g" m* nhMemDC,0,0, mpBitmapInfo- >biWidth, mpBitmapInfo-
>biHeight, SRCCOPY);
}
// 结束显示真彩位图
:
eleteObject(SelectObject(hMemDC,hBitmapOld));
// 删 除 位 图
} else {
// 使用视频函数显示位图
if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))
mPos.x= mpBitmapInfo- >biWidth - mDispR.Width() ;
if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))
mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();
, ^1 a& }: c4 F8 Fif( mPos.x < 0 ) mPos.x= 0;
' |# C0 U5 F V6 Xif( mPos.y < 0 ) mPos.y= 0;
/ I3 b) Y( N9 U- D; Q# A
// 显示真彩位图
DrawDibRealize( m_hDrawDib, dc.GetSafeHdc(), TRUE);
if( mFullViewTog == 0)
{
Wid.x= mDispR.Width();
Wid.y= mDispR.Height();
// 1:1 显示时, 不能大于图形大小
if( Wid.x > mpBitmapInfo- >biWidth )
) r, v q9 @% Z: k, N9 KWid.x = mpBitmapInfo- >biWidth;
if( Wid.y > mpBitmapInfo- >biHeight)
# Z5 X! ?) a" Y) X9 {1 ?Wid.y = mpBitmapInfo- >biHeight;
% b! s, [- L4 g! |$ V7 S3 ?: H
DrawDibDraw( m_hDrawDib, dc.GetSafeHdc()
, 0, 0, Wid.x, Wid.y,
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
, z4 |9 _) w) T* C- m! C} else {
6 l8 P. ?+ A4 Y0 \+ F/ \2 fDrawDibDraw( m_hDrawDib, dc.GetSafeHdc(),
7 l( w4 z' n5 a- S( X! R* y& q2 `' R0, 0, mDispR.Width(), mDispR.Height(),
mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),
& v* g, ^6 w- L$ f7 [0, 0, mpBitmapInfo- >biWidth, mpBitmapInfo- >biHeight,
DDF_BACKGROUNDPAL);
2 r1 p& X) ] R& x' z}
: U" F9 a* B5 k}
5 }& w6 N+ c& _% s: d/ q4 f- }. treturn;
5 j0 h6 I/ M/ K( u8 T9 R- M3 o}
, L. V( W5 h0 L" B: `! l
e8 e; K# O& v4 \# P
---- 五、使用CViewBimap类显示BMP位图
; h0 K; X! M% y! V9 f, ?% b7 D---- 1. 在Visual C++5.0中新建一个名称为mymap工程文件,类型为MFC
AppWizard[exe]。在编译运行通过后,在WorkSpace(如被关闭,用Alt_0打开)点
击ResourceView,点击Menu左侧的+符号展开Menu条目,双击IDR_MAINFRAME条目
,进入菜单资源编辑,在'“查看(V)”下拉式菜单(英文版为View下拉式菜单)的
# o6 E& l0 J1 b4 d尾部添加“ViewBitmap”条目,其ID为ID_VIEW_BITMAP。
! s5 _4 B$ i7 V
---- 2. 在Visual C++5.0中点击下拉式菜单Project- >Add To project-
( q2 E& q" \2 Z5 T. i>Files...,将Bitmap0.h和Bitmap0.cpp添加到工程文件中。
+ [% l, w3 G2 U8 z6 X
---- 3. 在Visual C++5.0中按Ctrl_W进入MFC ClassWizard,选择类名称为
CMainFrame,ObjectIDs: ID_VIEW_BITMAP,Messages选择Command,然后点击Add
Fucction按钮,然后输入函数名为OnViewBimap。在添加OnViewBimap后,在
+ J& [& F$ r, `$ B4 eMember functions: 中点击OnViewBimap条目,点击Edit Code按钮编辑程序代码
5 ]- u8 {, }* _, A。代码如下:
; P! `5 z) ]# L7 y( A) S( ^
void CMainFrame::OnViewBitmap()
{
// TOD Add your command handler code here
2 F3 }+ K [4 Y2 YCViewBitmap *pViewBitmap= NULL;
) M# W0 E) B# T, O: F
pViewBitmap= new CViewBitmap( "BITMAP.BMP", this);
pViewBitmap- >ShowWindow( TRUE);
}
( }1 l# N% D B' k3 h
" c$ j: g A# G
---- 并在该程序的头部添加#include "bitmap0.h",然后编译运行。
: R( b; G7 ]4 I5 G0 x8 F# ~ i9 P4 `---- 4. 找一个大一点的真彩色的BMP位图,将它拷贝到BITMAP.BMP中。
' ~# g' n. i3 q: P. t. G5 U
---- 5. 运行时,点击下拉式菜单“查看(V)- >ViewBitmap”(英文版为View- >
ViewBitmap)即可显示BITMAP.BMP位图。
& e% p* B* }( C/ S$ u
---- 六、CViewBimap类功能说明
---- 1. 在客户区中带有水平和垂直滚动条。在位图大小大于显示客户区时,可
以使用滚动条;在位图大小小于显示客户区或全屏显示时,滚动条无效。
& y: U: B! q6 s) w8 G2 c) S8 _, H& ]. P5 W3 J" S, j
---- 2. 在客户区中底部带有状态条。状态条中的第一格为位图信息,第二格为
' V' h+ F. q, R9 f, U位图显示方法,可以是使用普通函数或使用视频函数。在第二格区域内点击鼠标
: S3 k2 {4 }. Q" M, ]& t. r,可在两者之间接换。第三格为位图显示比例,可以是1;1显示或全屏显示。在
: a6 @. k1 S) {* l2 Q; u3 }6 K第三格区域内点击鼠标,可在两者之间接换。在全屏显示时,如果位图比客户区
小,则对位图放大; 如果位图比客户区大,则对位图缩小。
5 F8 t, p* |6 }
---- 3. 支持文件拖放功能。可以从资源管理器中拖动一个位图文件到客户区,
就可以显示该位图。
---- 程序调试通过后,可以找一个较大的真彩色位图或调整客户区比位图小,
: p8 w& a+ L* {! I. @在全屏显示方式下,比较使用普通函数与使用视频函数的差别。可以看出,位图
, y. |, u; R: U放大时两者差别不大,但在位图缩小时,两者差别明显; 使用视频函数时位图失
$ l. C/ c8 h; [' y8 `% e真小,显示速度快。
+ @0 i8 `. W+ Y: q9 D
---- 还可以从控制面板中将屏幕显示方式从真彩色显示模式切换到256色显示模
式,再比较使用普通函数与使用视频函数显示同一个真彩色位图的差别。现在可
以体会到使用视频函数的优越性了吧。
---- 在全屏显示时,位图的xy方向比例不相同,如要保持相同比例,可在显示
4 f$ P$ A+ H) S# _+ x8 a% I9 k) h程序中加以适当调整即可,读者可自行完成。
| 欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) | Powered by Discuz! X2.5 |