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

图形擦除技术及编程应用

[复制链接]
发表于 2003-10-12 23:35:05 | 显示全部楼层 |阅读模式
周鸣扬
5 x' \/ {* h/ J. \& @
7 y4 a  x2 c: C+ ?, |9 C: H  图形擦除是图形特技处理中最为常见的一种,在各种游戏中图形擦除技术有着广泛的应用。图形擦除在本质上是图形的消隐,即在两幅图片之间进行图片的平滑过渡显示。过渡的方式决定了图形擦除的不同视觉效果,其中最为常见的一种就是图片淡入淡出的更新:两幅图片由明到暗、由暗到明的循环交替显示。这种特技效果在编程中的实现,往往是通过DirectX技术实现的:DirectX Transform为我们提供了一个“Microsoft DirectAnimation Control”的类(在注册表中可以找到该类的注册信息HKEY_CLASS_ROOT\CLSID\{B6FFC24C-7E13-11D0- 9B47-00C04FC2F51D})供调用,以此实现高质量的图片擦除。不过,对于DirectX编程,大部分的编程爱好者对其程序框架难以适应,可以说,花在理解DirectX编程上的工夫要远远大于对图形擦除技术本身的理解。有没有一种更简单的方法,使用常规的编程方式来实现图形擦除呢?" y+ r9 }! [; h8 d, a
* B2 t7 V, e% U. V- W
* |( m) t5 n0 F% P5 F, v
  解决方案
* ?  O9 i/ l6 R7 x; ~5 c. }% g, ~5 u% h
% c* t0 D! L9 Z; n" u5 x. ?/ V
  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:* j2 R, Y' ?: ?# r  k' [$ K  h* {( x
& R+ }2 L- M( L$ N, g2 i
  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red " z7 ^4 l# A, h3 J2 G5 ^: l
2 c$ g3 W- P- s" n3 q) @$ Q
  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
/ q' ~& |' ^! \5 V: @0 p) `5 x, ]/ ~
  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue% M1 g6 J) X7 j3 ]: O$ a8 p- K
/ T7 U/ I9 {0 t" c
  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
; j6 I) H* l9 w% H+ [2 v7 p
2 J4 {( ]- I& I4 p  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
& @+ d8 U" h; @* V2 _# N5 M4 b9 h) P* |- p
  BOOL AlphaBlend(- D6 O( Y$ w$ F$ M" b

9 z# F7 K5 `* _+ l- e* Q* }5 p   HDC hdcDest,
# t  g$ r# S& K) [6 c2 U/ [4 N% A6 @6 x$ Y
  // 目标设备环境句柄$ U) `, L1 }% F/ a9 E5 F
' A& C# @# k1 J
   int nXOriginDest, 2 c% h1 e' q# }+ W8 O( \- Y* E" N

$ m, I; e- |/ s7 J! p/ x9 [3 l! Z  // 目标坐标x% ^4 A% G$ f- ^2 E' b5 h' t
7 L; {5 \0 S1 S& @
   int nYOriginDest,
: A0 ]0 m$ u8 |( p* R" W
* `8 i! s! b/ W7 z. x  // 目标坐标y
) D8 |8 w. b2 N; G# b3 J9 g' n* R
- Q: j5 S8 ~, c' f$ K) \/ f   int nWidthDest,
, P, ~' H! l+ }/ C. r# ~. Y. w4 B$ E
  // 目标宽度  |+ ]: S- Z$ o/ k7 I/ n: T4 k( q
8 W& v, {3 [- p& c9 Q* d; d- U, u( {
   int nHeightDest, 0 a2 n9 F9 d0 J  U
" n9 A6 c# @, @4 ~
  //目标高度
' O- f2 i9 y: J1 Q$ E1 U' f& C6 F. s% a
   HDC hdcSrc,
5 ^7 K+ [( B' |9 K% n0 {- V2 T; G
% z  z$ z4 f! v/ @: c  //源设备环境句柄  X% ~$ X/ D* R$ ~- r

' O6 f/ Z- w: y  d   int nXOriginSrc,
) A/ S4 n9 _/ _( u- b' i2 ^0 r9 g6 {1 O6 e  Y4 n4 o; L; V- _
  // 源坐标x
6 e. |2 H3 M" r$ J9 y. w
: u; V5 \/ P+ w( c: u  [1 }& }7 y' [( b   int nYOriginSrc, - Z2 S4 g: ?+ j* b' S) q

0 p# A' I) j$ S: u4 j  // 源坐标y1 S! T/ S2 C( M8 x1 R& O

; P! r/ Y- t0 ~% e+ Y: ^* B   int nWidthSrc,
* \% N! E  Z* N+ I, F( y9 _% B/ _0 L# _. e; y: ?% a
  //源宽度
5 H6 T- w/ r6 P+ v/ |) w3 J, U
/ w+ e, `. ?  E# ]  t% U   int nHeightSrc, % k( @' n# K# U! P" V4 {9 z- [) R

2 x( {: z- M+ r1 d( u  //源高度
2 m/ g8 Z8 Z/ U$ P/ ?; ?. L3 Y  W# c9 h; K% \1 O2 O4 f5 N; K4 y! r2 D
   BLENDFUNCTION blendFunction * ?- v, n( m, @) @4 a8 Z, g

' _( W8 ~6 o1 d4 `: A& @  // 合成方式具体数据结构5 D; T% n1 w' G# l$ L
. |5 a. T; K1 K$ F3 C% S
  );
3 p, ~5 \) T6 R$ B, r- _- \& u6 D4 F% ?' u, p, |2 Y& B  P
  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:# ^. x# Q* R" P8 r# K; k

  f1 ]& S" [3 ^( u# @/ C  typedef struct _BLENDFUNCTION {
& O+ |4 q( z' h, n; w) w; P2 o
- s% i) N; [# M/ l5 J# B   BYTE BlendOp;
8 M9 a4 d5 t, A( @) d0 |2 b* u& N$ Z3 |) E9 G! C/ a# p2 A. b
   BYTE BlendFlags;//必须为零
' L4 e3 q6 j* E2 e9 p' u/ P' i8 w# P8 P) s
   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示% R9 _( R) ?1 Y8 j1 {" e
2 J  l5 P/ F3 a9 V/ l' u2 G
   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA5 q& S$ Q3 \* f0 G0 s
5 ]1 x9 ?. ]  ?7 C! _. G
  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;( I6 J3 ~/ w. d' l5 P' O
$ ~; K" c" o. o6 @2 w+ s. ], S) ?
  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。* X! F. t# p# N
! t, i- i! S" ?- F& q' X- C4 P
3 m0 ?4 M& J/ F
  编程实现
, v) }2 S- `" P2 r3 B) y% [4 h$ ~" j6 z9 n* x

& ^/ K9 U, `3 r  e  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。- A4 _* r8 ~% {  J. j

% N2 A* O& V3 {3 T0 C$ @  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:8 ?( Z" e# x5 H$ y) Y; D
0 F. P% w1 w/ C5 m# z2 v
  class CWipeImageDlg : public CDialog
2 d) \/ s( v0 V" u; h, K" h# Q# U9 v  l) u* n) f
  {
# ]3 c1 U, |5 g3 x5 E" n6 v5 V
* e7 U6 ?5 s% _& E+ K, r  // Construction+ _& k4 F0 S2 ?( W

6 v, I, U2 P+ v, t  public:
) U2 {& ~; Q; P1 p5 a: o' a; F( K: F
   BLENDFUNCTION m_bf;! B7 _& w! y  O: x; s

3 t/ q( a$ D5 R7 m/ |   CBitmap cross,lantern;% G- F0 \* Z) h- Q, u$ A
4 \9 z8 Z7 v8 c2 j# _5 y
   BITMAP bmp;  u8 W- W9 j! W7 x

5 f! G) F0 a: z1 _% ]$ U5 L; ]   int bmpWidth,bmpHeight;  G0 X+ R; r9 D( n1 F$ m! H
5 t* c, W' _# \( n
   CDC dcForCross,dcForLantern;
$ A' _% g* b$ ]1 T2 V" G) R
- G1 m# m) l8 j( G2 [   CDC? dc;; @0 s5 b1 ~' x0 r  C6 z0 E2 ^
  ~! S3 F3 j' ], r  q1 z$ g1 b  J! s
   BOOL bShowLantern;/ O; U" A* X4 L

& m8 d" t! Z5 `& S9 P- E) j  ………(系统自动生成部分)
4 U0 b5 }: w/ x5 t9 [5 l! |4 t, `+ i" J- @: B$ _# L2 v
  }; 6 N7 C+ D  C6 F; O2 u  t
( {3 a) u" n' e# }) \8 B. r
  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
5 X; ?; l$ A* u2 {
" E6 v' ~$ F8 W5 @! ~- m  BOOL CWipeImageDlg::OnInitDialog()
) q4 N' h% y5 E* P; q: S
" w! H' ~) N$ i* b* ^5 o" v' t& o  {" A% S- O$ G3 x; g0 N! B) i6 r
% P, @" W* L, r& i9 l! ]
  ………(系统自动生成部分)6 l- F" S# I! _' N# n) [
. a; V+ r6 c- ~
   // TODO: Add extra initialization here  p$ ~# }! _2 b

1 U4 o- f& {6 B* j* b/ p, `   //初始化全局成员变量- H0 M! W6 }& \: v( `+ Q
3 m& g* S' d! E
   this->bShowLantern=TRUE;6 x2 v8 A5 t3 b; E! r- {
6 ?$ |9 y* i8 y- z$ r
   m_bf.BlendOp = AC_SRC_OVER;
6 C9 t- k9 S- O0 i' e. j) P1 P6 w- ~0 ?$ D8 S( L
   m_bf.BlendFlags = 0;
7 O  Q3 K$ {: V" r3 X7 ~7 k0 D3 R) I/ C, |0 ^, X( N8 I- C) z
   m_bf.SourceConstantAlpha =10;
6 x1 j3 Z  h" N' |$ ^7 [% G- K2 q: `1 A1 e, U5 n
   m_bf.AlphaFormat = 0;
- a: L2 y& H( G  ^( G! G' _3 {( o0 w0 F6 O* e
   //为节约篇幅,以下代码中略去对操作不成功的处理代码0 h; g* l2 i0 q, T$ w9 A

) S& Z/ I) G% u& Q5 J   if(!cross.LoadBitmap(IDB_CROSS))
% L7 X+ r. V( G* q9 H' \- T- v# Y$ X
   {2 o) b0 `3 c+ z, ]# g# }! `3 J

% [, [+ a' \) Y/ W0 l* A   AfxMessageBox("装载位图出错!");
' B5 j9 B, I8 Y% J  R/ S9 ]  \. _3 j
   return FALSE;
2 x' G$ M* S% Q0 q# T& H/ p0 D* A# M0 k
   }
0 a2 a8 z8 q2 F
  ^- l% X- P1 y9 T   cross.GetBitmap(&&bmp);2 @9 k; R1 A9 c1 f8 Y- ~& ^
2 s5 G- t1 [% u3 T
   lantern.LoadBitmap(IDB_LANTERN);6 Y; G  ?# N% k) c

- P  R6 v) J+ l- v8 ~   cross.GetBitmap(&&bmp);# V6 i5 a7 p: o6 i- p' R; H

. N& M/ e6 H& P  k   //获得位图的大小信息0 x" F; a( k* _* ]1 Q2 ?
7 t' g% P' C* y" V) q* b0 _
   bmpWidth=bmp.bmWidth;& B' \' F7 \3 ?  U
! p( f; N. W6 }, ]" E# Y
   bmpHeight=bmp.bmHeight;2 {" `- ?: n1 `, G" y7 t

3 ~" p1 u" Z/ A  dc=this->GetDC();7 ^& _+ S$ O' C) w1 {  C3 q

1 J9 f- N5 J5 e* c- w   dcForCross.CreateCompatibleDC(dc);$ W' D! D9 ?. H8 n+ c- T
9 z* n/ i  e8 d6 k% C3 D7 f+ ?. A
   dcForLantern.CreateCompatibleDC(dc);
' A' y9 |& M/ }6 v0 ^
6 I- s; E9 n4 }% D) w8 i" b3 w   //将位图装入设备环境句柄
) ^" Z  ~% n# p/ I/ `% s0 }4 s* o
7 d, \8 v- C1 F" ~! z" |. f4 k. y   dcForCross.SelectObject(&&cross);
" ?/ K' v2 \% r0 ~( z2 e, T9 c2 B! X" I
   dcForLantern.SelectObject(&&lantern);7 L$ t$ o- h% W4 g# Q: i
, K9 E( v' s- i9 m! V+ t
   //打开计时器/ `3 t( U/ y' H/ q

# k8 F5 U+ s2 v# U/ z" k8 d4 h   SetTimer(1000,50,NULL);0 L% w  w$ |2 W, o' q, ^# x5 Y

# F. k* T  R6 z+ u   return TRUE; ! f/ ~9 r( y9 ~  m% f& F
; ]/ k/ p, f6 T- G8 W1 h" i
  }
/ @0 u3 l6 Q: r0 B' ^
# z$ i: X. i1 F0 z4 T  void CWipeImageDlg::OnTimer(UINT nIDEvent)# P9 H) A+ y. G
3 ]8 x6 W/ p; a+ x, C
  {
) M/ ~- {9 V+ ^0 f% F* }% H% g. a6 r) `7 A1 T! ]' b" ]
   //图片透明度每次递增5点
. c* ]( {; R9 ]# H% z1 U5 ^# |9 p( A+ @& w6 y: ~* A' |
   m_bf.SourceConstantAlpha+=5;3 W* c# \  v; n- p+ z) a
( Z, \" y/ W$ f9 o* G# J# Z1 B
   //当第一幅图片完全可见之后,显示另一张图片
8 x/ y7 a! h( C6 U, U. z! f/ a; T4 W" T! [7 o9 o
   if(m_bf.SourceConstantAlpha>=200)
% r& G) p% w3 [+ h0 ]: e; o0 H+ p
9 G3 R. d9 ^6 S/ C' ?   {! h0 U' o. f5 t  m' C* n3 u9 x

# c2 U) w6 Z2 J& w7 s   m_bf.SourceConstantAlpha=10;
$ ]# o3 z5 ?2 F8 |' P$ _7 r. `+ ^9 b
, t! g8 k) }6 t& C* [) E" [   //将bShowLantern做为显示标志,确认应该显示哪一张图片 
: W% `. v& g7 m8 M( M1 R4 E$ k8 r4 G3 \
   bShowLantern=!bShowLantern;- n% b9 y" @4 `' X

) b  l, r6 [' t. e' o) S+ c( K: b   }+ x& ^+ ^- n! L5 U/ t- P# o% Q2 A

; h: W: c; q3 @7 T   if(bShowLantern)
  T6 l! U$ T+ E, h( ?. \% b
. z( J# O7 y0 H/ v1 |: I" `$ L% f   {; E# b+ P3 r; ]

; x% A3 F2 O$ X* j5 ~0 Z) ?   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);4 [0 V4 e/ }9 u) x
6 u6 X7 |* R0 n' I+ R
   }# q; \8 |/ E/ @
1 ~1 y. Z" r) E$ `/ q. D+ S) l9 E
   else
* ^  s; O8 ?4 S/ t' M. {8 [: A) e) V
   {
) e% W2 m& x5 g# z& ?  w$ @& J! A! K" B# L; D
   //按透明度递增的方式显示“背景”图片
  `; F8 v! k' R6 O. g* m2 E  @
8 H3 W. [  Y: w) C! `3 l/ _% V   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
% C0 Z  A0 \0 s+ @: B8 N& G+ e6 e* {
   }
7 z4 D# [, {2 Z* S7 h* `# r$ J! `  L/ _
   CDialog::OnTimer(nIDEvent);
  V5 w/ x0 T; B, E7 Z3 G8 _1 P
$ b& `  g8 N& O9 u* |, l' H' N  }6 e# Q- {) @+ r- d, R1 B. F

. d; J9 z! ]5 U; m
; o( q9 ^" B0 z% `1 R% z  编译说明9 g" w$ N& X+ N" V9 h% b% ^
& H6 C! g2 u) ?2 g+ k
3 {' F0 ?" u  d/ u) P6 L
  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 08:58 , Processed in 0.041142 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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