找回密码
 注册
搜索
查看: 4361|回复: 0

图形擦除技术及编程应用

[复制链接]
发表于 2003-10-12 23:35:05 | 显示全部楼层 |阅读模式
周鸣扬
$ i& \9 y# Y6 q6 @3 {4 k$ A( q/ w% g. k7 m; e! ^: \1 a* j
  图形擦除是图形特技处理中最为常见的一种,在各种游戏中图形擦除技术有着广泛的应用。图形擦除在本质上是图形的消隐,即在两幅图片之间进行图片的平滑过渡显示。过渡的方式决定了图形擦除的不同视觉效果,其中最为常见的一种就是图片淡入淡出的更新:两幅图片由明到暗、由暗到明的循环交替显示。这种特技效果在编程中的实现,往往是通过DirectX技术实现的:DirectX Transform为我们提供了一个“Microsoft DirectAnimation Control”的类(在注册表中可以找到该类的注册信息HKEY_CLASS_ROOT\CLSID\{B6FFC24C-7E13-11D0- 9B47-00C04FC2F51D})供调用,以此实现高质量的图片擦除。不过,对于DirectX编程,大部分的编程爱好者对其程序框架难以适应,可以说,花在理解DirectX编程上的工夫要远远大于对图形擦除技术本身的理解。有没有一种更简单的方法,使用常规的编程方式来实现图形擦除呢?: y$ g/ l, l9 d( j6 t

( l/ N9 Y; _, R6 E" a5 b  v7 g; q% Q
  解决方案
7 c& }0 ^8 G# a- G, j$ s
8 O; U) w  V. }6 M2 D: l- j
; f% Z0 T! ~; i8 g  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:
9 c+ Q$ j$ E& d! a8 D4 h; W
$ {# Z. C2 b8 e$ [, ^  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red
0 ^4 g- C6 G5 Y
' T4 L! s4 I% p6 v% L' G$ W& H! y% D  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green # B# D3 U- Q( Q! L% r$ v5 G! l" C

6 C2 f) e6 P; j+ |8 Q  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue( A9 N( Q( f: m9 i

4 v( y: U* T8 j8 D& ~  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
8 h  @' `: E9 N2 n4 B: M9 C! j. ?1 J! L8 W
  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
( H5 }$ w( Q6 F& `
- K. Z$ V4 E( `: u6 O  BOOL AlphaBlend(
9 p6 W0 `% v" G
# G% Z" ~. R' |" N( \1 M2 z# H   HDC hdcDest,
5 \, |3 z& D$ G8 r1 r3 h
& t( W! }7 A5 p$ [- W( ^  // 目标设备环境句柄+ D" k, c- o0 d& a* x( p
# ^/ N7 v. y, U$ M+ R0 c
   int nXOriginDest, - n8 R( d4 A4 m) Z1 Y- w7 C2 _. R; [7 t
2 `- j# J4 n+ g$ J
  // 目标坐标x
7 U! x2 m7 r& T8 i: O. e" A' G5 d; W; P; M, V
   int nYOriginDest, # Q: B$ [' @6 l2 A

4 H" a/ j9 ^# q$ c- a4 r+ u- M  // 目标坐标y
5 p* ]2 Y/ I% j( W# ~4 w" a8 g7 \4 A  n- J
   int nWidthDest, " K2 u9 ?! G( J- e2 w3 t

! }, b. _! c& n* T% s" A* s: G8 D1 g  // 目标宽度; r, t4 i( s% c2 V+ D2 V1 |& C
0 {! l; o' N; D3 C' d* ^
   int nHeightDest,
8 D/ y* |) t2 j3 I! J4 h2 w$ ^3 o' @- r' J
  //目标高度
# f  Y1 g# a7 @; g: \: r. n6 y. }( l% u+ E& C0 q
   HDC hdcSrc, 9 ^6 T) Z+ c  Q" @
+ M2 f. D4 D' F9 |1 i
  //源设备环境句柄  i* |9 |5 {' t

2 h4 q% j! E) L' `3 v# }, @+ M   int nXOriginSrc,   M% G# N! c6 U8 Y
& i9 G7 ~7 p. c8 x3 _
  // 源坐标x
) J- ?. F; ~2 C  r2 }! d# x7 _+ B. q/ e& S. J: d& w
   int nYOriginSrc,
/ W# f5 K5 A9 ?" M3 Y6 w8 f! i2 s1 d2 j
  // 源坐标y- O2 e9 t/ j6 B) W3 [
" g5 T: a+ C. ^5 m
   int nWidthSrc,
9 @  v! j7 ~0 x0 ^4 k! }7 Q( l7 {& }3 l2 x& V# P! N
  //源宽度1 m$ G7 W* ?, @1 [

$ p7 @3 _4 b/ g4 E   int nHeightSrc, % |; g  x% b. g+ d! L1 I

- z  R. g, ]( E4 O$ }2 `8 \9 @- y  //源高度6 t$ ]: n& w- ?7 P4 H

& Q9 t* P  L3 c/ e   BLENDFUNCTION blendFunction # v- l, Y+ l% x6 S  F5 b" ]

7 n) Z& A+ h) d  // 合成方式具体数据结构: g( D) z$ n! @2 R* C  X' I

% G# n6 e- v1 H" P" b( ]+ \1 o  );
9 W6 F3 e2 A5 A+ B, @2 D% F2 e8 A6 u- G
  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:  D  b& L  u4 x

9 O1 d4 I2 T7 j  m3 _( g  typedef struct _BLENDFUNCTION {& B! B! }  F" k2 i
3 u' y+ o2 V( O  V4 o7 @) l
   BYTE BlendOp;2 H0 J. F- u: ]. \5 Q0 V
) w& ?' g" \! R2 Z( @! T/ H
   BYTE BlendFlags;//必须为零. ^+ q4 {5 z3 Y$ R" @
4 r6 j& ~6 x% y
   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示0 B" O1 i5 j- d) c$ Y) g# {+ l

9 |, n5 T% a" Y1 c# _9 W) X   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA! y2 \* ~. `/ L. Y

* T7 O6 E+ m. j& M4 b6 G/ l  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;$ |9 A( r) t; \" ?% X

5 {( T7 w" H2 [) z8 R- K, V; D& s  P  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。, |* z1 Y! x! e% q
% I  |  r& Q- r& ^% j) G# d/ `
9 K, [0 e$ W) i1 N
  编程实现
2 }( R" p5 @9 W: r
! m8 ]6 e. }+ E  h) D; J* s$ X3 t: M; E# a# k4 P' H4 v
  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
9 D- v$ G( t0 l; |: B% m  Q+ a3 n7 B  j+ h6 @  i
  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:6 d& |8 D+ a5 W% a
) R/ ~. \2 Q$ K/ p8 ?
  class CWipeImageDlg : public CDialog" c2 q3 c5 j+ ^, l) a$ J

! y) I  C4 T# S) D. U! `# U  {
5 v' k& B4 @5 }1 z, v5 A
7 X8 [( X( g, a7 p0 T+ G  // Construction% E( D4 r6 R7 P' \

& ^) j9 w. n3 V  public:; a, e1 [; q* P7 Q+ V1 R4 w& G9 A( u+ y
$ R% P' }* \: ^2 w
   BLENDFUNCTION m_bf;
7 q- ]5 t) u9 i/ ^: N5 G6 i( ?% p! n
   CBitmap cross,lantern;# C8 [  \! `4 p$ A

0 ?; y2 A; `, z( Z5 P   BITMAP bmp;7 A2 L4 f; R+ ^' e

7 G- ?. ^" H7 d8 s: O' s   int bmpWidth,bmpHeight;
+ R4 y6 o4 O$ s, f, ?3 T9 s% I& a) y* s7 s# X
   CDC dcForCross,dcForLantern;
7 A) m3 ^8 f. j7 c
( w- l! b6 {7 g5 H& M   CDC? dc;# z* N$ B9 O- i' _: s6 h+ K# \! ^

& k. l4 x% ^7 Y( n; k: k* M   BOOL bShowLantern;$ G7 P9 K9 a, A  M' r' H; F' i
) a- P, i# e5 A* P
  ………(系统自动生成部分)
% B0 J8 D3 M+ G9 q
* c/ i$ P+ d' w  }; 7 H+ e+ a! D0 B: W; \5 S
, W: p, Q; ?5 N4 j
  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
- j, V0 i1 U6 `, F$ m) t4 H4 T  G0 q1 h( S9 w2 ~# M% H
  BOOL CWipeImageDlg::OnInitDialog()1 ^! \: I% j* }
! e& n- Q# k$ D; d9 w( f$ J  }
  {) L# q' R# Y* z9 u

$ a# a* ?% ?; [. d0 L' f  ………(系统自动生成部分)( j, _! x2 u5 x( J
% [. L" ^5 L5 o& Q3 x. R, k6 t* s* f
   // TODO: Add extra initialization here
$ a( H( ~& l6 Y" d7 M. z5 `& w* G7 V; \; t$ D# F7 Y
   //初始化全局成员变量6 k, C# @2 I' `7 X8 H+ K
; o9 Q6 |4 f6 `/ g
   this->bShowLantern=TRUE;
0 c6 u. n2 s$ a: f/ |' Q3 I; o+ t1 E5 p; g: r
   m_bf.BlendOp = AC_SRC_OVER;) L: D( [& ~$ @- u7 [

- K2 h& H6 R. J( v* @( j3 `7 r   m_bf.BlendFlags = 0;
5 \% U3 q& }: ~, r" J5 A. Z- e1 I9 j/ f4 l  N
   m_bf.SourceConstantAlpha =10;5 x6 c9 e- g- H% R% `

( y% ]6 G* ^% Q( j- t; e4 y+ _   m_bf.AlphaFormat = 0;( E8 h% w$ F, [5 C, \' L( g2 y
1 u5 \" d; ]1 A$ {6 o2 S  ], t: d
   //为节约篇幅,以下代码中略去对操作不成功的处理代码
# N; U5 F4 Q4 r% O7 q$ f) P3 s2 R7 O
   if(!cross.LoadBitmap(IDB_CROSS))
+ r8 R3 G$ k! G; F5 ]
7 q% |5 C% Q- w0 M8 o   {+ W7 X' Q$ k; r' S' V3 i& m

" @1 k' [! R! G5 ~0 {8 V% k  g   AfxMessageBox("装载位图出错!");
, r5 Q3 b0 i- `) w, R& [
% n- P+ o2 o4 v1 R1 x  A  p   return FALSE;
# R5 @# [% O$ t, P& i, m( ?/ }4 a# {. M- }
   }
7 c4 P" D- @! |! |6 x3 u6 M
9 y0 I9 X* g0 W  B   cross.GetBitmap(&&bmp);
. H: l% `3 z- l  I/ A9 a  ^4 q# v
, n3 s1 j1 ~! p: ~; T   lantern.LoadBitmap(IDB_LANTERN);3 v. G: x( d9 z1 e1 h7 [  F
9 O1 I9 M0 A/ s# E
   cross.GetBitmap(&&bmp);
# }  T0 Z; [$ ^6 V+ K# x; f5 v1 x+ h7 h. x- B* @
   //获得位图的大小信息
5 J  {9 ~- l; I- A0 X3 n
$ y; ]: T8 Y3 q1 ?" i   bmpWidth=bmp.bmWidth;
2 D1 _6 w+ r2 O1 d0 ?: t4 j5 ~+ t* x' ~# [
   bmpHeight=bmp.bmHeight;2 t8 ~! ^& w4 D4 A8 [7 y, I

) f& E2 W) w" C2 b  dc=this->GetDC();
  x. v1 f8 c# s* d0 R6 z% p1 D# c- a
4 x, \( c+ ?% d   dcForCross.CreateCompatibleDC(dc);
6 j! O7 F6 [  \' G+ H" ^* R
( \) v' Q' X& M5 Y   dcForLantern.CreateCompatibleDC(dc);4 V3 c+ H7 `, H
2 {9 ]8 I9 Y: D
   //将位图装入设备环境句柄
, r7 E* v9 h; x! z6 W* B) p
  P0 o+ D! t. {+ Q   dcForCross.SelectObject(&&cross);
& I. ?1 g' t7 ]& Z$ u) p
' T/ H+ Z. J9 P( A( g* Z, o. o   dcForLantern.SelectObject(&&lantern);4 B" A7 I3 i6 N
4 J3 l& W& |/ p) W
   //打开计时器
1 `. z, G: d* o" j; ]5 w% b, `# e( O# h3 t  R1 I
   SetTimer(1000,50,NULL);# [6 Z: t2 F$ A8 n! ]2 N

+ I. B$ M7 m8 f   return TRUE; ; k* U( ?: l. z, Q8 R9 W8 O2 L

* R  y' q/ g  a$ @' G0 r. d  }
; \/ b& R: N. M. H; h# d2 D5 y3 T3 r$ Y
  void CWipeImageDlg::OnTimer(UINT nIDEvent)
& w  |" z8 Z7 o7 [2 L
# I$ F. E/ U- H' Y7 a% ~1 E4 `6 |  {: D3 ^' }1 R" {

0 k! v( d, W2 |" A! W   //图片透明度每次递增5点
& a$ O! B2 T: |$ j1 j& [; Q$ Z, _' s$ o+ F& ]/ \% c) p% l3 m* g
   m_bf.SourceConstantAlpha+=5;$ |' S) N# ]. c3 Z$ e

4 w$ I- A# [! `  L  \' y; B. ]   //当第一幅图片完全可见之后,显示另一张图片2 S+ V1 _+ N2 c# `! n- O0 m
- m1 }% R/ l7 p# z
   if(m_bf.SourceConstantAlpha>=200)5 ]' T3 ]  O0 E  Q! r
$ `( T- P* k( }) p( Y
   {
4 r# u" Y9 G" r- b9 x+ z6 N. R6 w& v
   m_bf.SourceConstantAlpha=10;- h# ]) n/ U8 q  \1 t

8 p, R! j1 \9 J8 D9 v2 L9 p   //将bShowLantern做为显示标志,确认应该显示哪一张图片 . f! n& A, {1 y8 A6 |
: k: {* L, g) u! K  }( {
   bShowLantern=!bShowLantern;7 p& s2 u7 c" a/ ]3 H( J

' i. u7 T5 s! X$ Z   }; t( J! ]+ ]1 l  `1 u! H8 S2 U
) F" r$ w, s2 U+ G! D* e
   if(bShowLantern)2 a+ U& x  ^. a$ m5 }
' J. W9 S, N9 c5 s) ]5 Z
   {7 `0 E* j/ _0 M/ c- O

) `; {6 v) C0 n   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);
4 p/ O$ W1 X- G4 e# L# @( t( I
+ G5 P) }6 L5 R5 C; h   }* P  n! o/ N+ H3 z  `9 _2 y) H( I% z

( M3 e- [( T1 U' P  `   else
: N3 Z5 {) B! Q8 u, Z- R0 J6 W" m0 e! k) `! @) y  c8 ^
   {
4 b! F0 I2 \& N# l. f3 T& |( g8 D4 _
   //按透明度递增的方式显示“背景”图片
4 s$ h# n7 J3 A# X- |3 a& }
- ~1 j' r0 a  f6 ^$ b& H   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);  l  D* [- G( F7 d# v

; K( k5 {- p0 M4 J  P' u* Y   }# t" o9 N2 T- l  p. X

% L2 K- A. G$ k7 W1 C   CDialog::OnTimer(nIDEvent);8 s4 Q& E0 L; ~& \1 c" x! u0 r  V! P! ~

( P( i2 `- w. N* `8 b' t  }8 o& s' ]0 P$ a4 R; Y

' U" g- z4 k, Y' p; ]
; z8 l1 o8 o1 d$ @  编译说明
7 J* u3 k0 U: u2 K& o
- ~  h- O- P6 ^9 |: ]' N+ w
% p. P. @9 i2 s( s+ u7 M' ]& q  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-6-20 00:57 , Processed in 0.037280 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表