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

图形擦除技术及编程应用

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

; Z+ G/ A# u( M5 W+ r0 \4 R2 p* N# p/ i* s5 ?- O$ q
  解决方案7 x9 X& u, ^$ w% {" r$ c& o

9 _3 ~' {; a& S) k# k
$ m- T7 O7 [8 @+ ~2 W3 M- B9 V/ o  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:7 W' o. t& K' K! j3 D

# s) B# n$ o$ s) F5 h& j  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red 7 b9 N5 T2 \4 ^5 M

: R0 R4 G+ v% `2 s: C  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
; |# O, w3 H% w2 \3 F
1 n* h. ?, ?3 z# G0 Q  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue
9 R. @) }* ^2 j5 i
9 p3 K2 V" e  n  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。7 r$ m& ]+ a! u9 K* |+ m

* h, N$ k) J3 i  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
5 h# D6 A: J' _% B
/ }  O: G; p: k; r- @  BOOL AlphaBlend(: P* k. p8 S3 H& Y4 ~
3 W) c& S0 P' T3 x  v, y
   HDC hdcDest,
+ T* q/ V1 }" g
* }  ?3 a# O; r5 x! F  t' ?* o$ `  // 目标设备环境句柄
0 }: F% P+ w$ i1 f5 x; p
, `- b/ {2 m! W* B  o   int nXOriginDest, . G; y2 E# A3 a  J5 P* Y& V$ Y

* _8 z8 ?7 B5 }7 H, W$ Z  // 目标坐标x- v( @5 r6 V6 K* F% N( C1 F$ N
4 B* }. ~1 y% S
   int nYOriginDest, $ o) G* W# `% f

* |8 j3 D) W0 E8 ?& J1 L  // 目标坐标y
9 J$ f. R) P: N) l' @3 F
: J1 [3 \8 e  B   int nWidthDest,
: [% G( Z  f+ D8 z2 f+ C. V7 {
9 k3 `+ V+ H! [; h2 t' v  // 目标宽度$ H1 P/ o% G6 r- }5 g) b0 M
% {; }2 @! F3 N- I
   int nHeightDest, , C9 N% l. u) O" e' F
5 K) d  P5 i/ O$ ~2 a- X
  //目标高度: I6 m4 B3 j+ Q/ k% Q

6 K3 M2 C# g' i( Z' f% {$ d) t% l7 v0 C   HDC hdcSrc, 9 y) q6 Q  m9 v3 A
8 a  C* u; f' A- Z
  //源设备环境句柄
, S9 @' E: ^, k) H3 G8 h  r/ N% F6 `, G+ c
   int nXOriginSrc, ) N& E% a8 D0 |# ]0 W$ P7 p/ S
' G6 Z& K; M- T
  // 源坐标x
: s, t6 l1 B+ h
( u0 d" W# I2 @   int nYOriginSrc, $ P- L" l* i( B3 [

$ @0 w7 z% c: Q& o& `- z4 U, L& `  // 源坐标y
* U* S6 G6 x, T& H6 m% i( U
& z/ K" l" h3 u% @, K   int nWidthSrc,
( b6 r% v. H8 i; `6 I- G* [
; Z: M* ~8 y" G! C9 c7 K  //源宽度6 U+ S% H' i3 F- `. D1 \+ |! z
$ U! `0 t: K7 n& I4 c. b
   int nHeightSrc, ) V( _9 o' v' C  O' ]3 }8 W

6 b1 M- ^2 G; _  //源高度7 n0 A7 Z7 V& S+ x& p; M

; a& o1 h# D3 {2 v  h. @   BLENDFUNCTION blendFunction ( r# f& l& J" a: E0 Q! v
* e: C& R& _( Z% e2 v
  // 合成方式具体数据结构( P; Q5 k( ]6 R$ E: b9 p# X  F9 d
# B8 u# U- Y3 |
  );6 P% A/ U: c9 M* g- Q6 r: U

( a- C3 x' _/ I- J  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:
/ I& w7 f% w; T; Y8 d
: p2 |6 r, x) Y, C: v  typedef struct _BLENDFUNCTION {
! y9 @2 S& G$ `' k, v( n# u4 [  u: ^4 I
   BYTE BlendOp;
/ P+ P1 S0 ?- c+ y+ d
  r' ~; k% \' ~: y$ m, m5 `' |3 e   BYTE BlendFlags;//必须为零# }1 k6 H6 o: H

  i2 {( t. l5 S4 e% m! k   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
+ V* {7 i1 i) M8 b; b
7 [+ L& F/ l/ G, a( j   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
+ z+ V' f, J  X0 n- i& l7 ?( f5 e* N( H% B" W% ?8 v. s' p
  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;6 j9 F' g& C" `
, P( Y4 A$ f1 y+ _, J
  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
* g1 v0 q7 x5 a1 v8 E0 i! w, ~$ D
2 J, j) u6 L) P, H9 ]
# e5 w1 I# i+ B& |  编程实现
; k* G8 B4 V0 [5 G* o& R: B: s! U, Z+ o3 ~/ C( p! P

1 Z4 O0 ?# U8 X* t5 z  T5 @  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
' `9 \5 z( U- u& h0 y1 K6 D& _9 e8 R% `3 Y( C: ^  n
  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:+ d6 L9 T7 E, ^0 t  b# w

3 H5 K1 D3 V( f( ~& o& V$ Z/ g0 Y  class CWipeImageDlg : public CDialog  x" P' u1 G1 F* C, Q- S
! E4 b/ V, U/ ~+ ^. c9 i$ V! \/ B
  {
# }; S! s& W) i& y5 x) a! E" W% T9 V! U: L, j# [  B
  // Construction
4 U2 E1 l% k  h3 n& e. c1 M: J, L5 }% Z7 x
  public:
; A# q& Z% e5 I9 F! x
$ l7 L; [" b0 @+ {   BLENDFUNCTION m_bf;, k% A; O0 ]9 c1 v. J4 I. J4 i
+ p( D9 m" p9 f- F; o  |4 v2 G
   CBitmap cross,lantern;
4 b" ?6 f6 s4 V9 \8 r3 L/ d
" j; E, a. ?2 J# }- \- V   BITMAP bmp;, |( p  w7 q9 ^
' j7 x3 Z" P2 W! S: k
   int bmpWidth,bmpHeight;
* j  N7 s$ ^+ s( X  n7 p
/ N$ K2 {+ I" M   CDC dcForCross,dcForLantern;2 @! a- M7 c8 P, f0 r2 F- U
/ b- G: R# D  n  n2 j3 q, [
   CDC? dc;
! N  A, t2 b1 K4 c) N! o8 N" P
- V6 _2 s$ w$ v4 g( ~   BOOL bShowLantern;7 n4 ]! Y8 G# u/ o% D1 Z, k5 K
5 R6 ]/ o7 T/ l+ h- T% q- \8 u
  ………(系统自动生成部分)
; J3 ?5 j, @+ i  d% u) n! ]9 `& }4 b  x
  }; 
4 G" b2 q! o: D# @; V
/ [5 U/ h4 \6 V0 [  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
8 v. }; `) {3 c6 P4 |5 _% x, z9 F: q4 R4 f$ ?; ?  ~
  BOOL CWipeImageDlg::OnInitDialog()7 ~1 A1 H- i% \6 v  M3 N

! L# N8 z; \1 D/ I! E9 x1 O  {* ^2 a* V' j# C" `+ K% j% [9 l1 A
, x) ?8 i8 x* ^) r* c/ ]6 O# |
  ………(系统自动生成部分)! c3 p! C* T0 [8 V* }& `2 n& f

8 z2 B- N/ c# T" N8 t   // TODO: Add extra initialization here
$ }$ _& p5 T2 `: a/ e' C% V7 P6 i  y# h
   //初始化全局成员变量
5 V+ b$ J% h9 o  }/ x- {2 S/ U, ^. Y
   this->bShowLantern=TRUE;& H" Y* t7 G& C& ^. E% h8 t
5 j# V' q' [5 L+ l8 |4 l
   m_bf.BlendOp = AC_SRC_OVER;: i6 m2 X) M, |: e3 ?

) m, Q" Z* ]) I" [   m_bf.BlendFlags = 0;; v# H: }; r  [, `1 T
4 j" p# e: s6 T3 h9 C
   m_bf.SourceConstantAlpha =10;0 ~; ]& y, R7 H4 A; ]: @3 t) l
: J" p  e4 o# G; e1 P
   m_bf.AlphaFormat = 0;
! y+ \" ]1 W8 [/ C8 L, s  t
5 w+ q3 Y  J( E   //为节约篇幅,以下代码中略去对操作不成功的处理代码$ x3 S/ B+ E. v: y) K& v$ A

5 A0 _/ @/ T5 U/ Q( C# E   if(!cross.LoadBitmap(IDB_CROSS))
; ~5 @( E  Q& J
4 S0 Q; h( d0 [& o   {- L  I1 u& D2 S# u. |" f, X0 H
$ s: I2 V% t3 R4 m. O
   AfxMessageBox("装载位图出错!");3 t: m  H- c1 J: x8 N

0 {# V4 C6 \! `   return FALSE;
5 y6 H8 E' O$ [, k" ^9 T
0 q4 ?+ w0 Q' u% p$ ~- Z- X+ ^9 q   }) Z  z# _2 @/ C9 U$ m- s% z

! `" Z" p; Z) ?- j) F# v   cross.GetBitmap(&&bmp);
) p* p+ k* \, `0 q* M
0 R$ a1 Q3 y+ x; |, o   lantern.LoadBitmap(IDB_LANTERN);
7 R/ h. r- c% d/ ~" Q/ x7 v% P1 s8 S1 ~) g+ \
   cross.GetBitmap(&&bmp);. P; W1 W* v, l
# A2 x% m7 F) D& [
   //获得位图的大小信息
/ b) ~# N7 k$ `: _5 F* c; u& j. @* I/ H! p* j
   bmpWidth=bmp.bmWidth;+ \5 ~; m0 G* t: Y, V0 T# L
! W3 A0 S  I0 Q/ l3 D7 H3 H
   bmpHeight=bmp.bmHeight;+ W  E& L. d: _: J' U
5 ^" x: R& {- g! K  c% l; D& j
  dc=this->GetDC();! o$ u( w* M) o  t- |
8 Q/ k5 P) t# T/ K" c
   dcForCross.CreateCompatibleDC(dc);
3 k; ]4 y! D) J  S
4 z6 I8 \# O5 ~5 R  N# f  w: G   dcForLantern.CreateCompatibleDC(dc);2 H8 B' M0 e' w' |) N: ~6 E* k* C3 v
/ g$ c0 c* S' j  L  H
   //将位图装入设备环境句柄, }/ T! ]0 S; w

9 p& J- q# e, k; ]4 P; v   dcForCross.SelectObject(&&cross);5 R3 R4 m. c( s5 G# u

8 a, m4 ~0 Y" }* g) y   dcForLantern.SelectObject(&&lantern);
  b) L1 B8 u+ c
2 Z) _3 l" m( b) s/ g  U5 @   //打开计时器
" r$ j2 Y4 j2 ?" P4 Y
" s+ q2 Y: t( E7 [2 y& w* J2 n   SetTimer(1000,50,NULL);6 h4 x7 a7 g" V+ s1 y! l. b

5 s2 `3 l6 x+ |   return TRUE;
. r) ~; i& }6 P% t8 V" `, s+ o7 V
' @: P/ I4 _( d  ]  }
+ k- W: h1 c" ~+ R' S- ]( l' D: B7 ]) y6 c  J
  void CWipeImageDlg::OnTimer(UINT nIDEvent)- L6 {+ {# w2 K7 q

" t( G# W1 T; D; w; o. ~  {7 k7 F" U+ F, e4 s; |6 }1 Q

% T9 y, f( F+ x- `  S0 \   //图片透明度每次递增5点' A, ?5 z  _! B/ T  L

8 U1 a  T& a! c% x, `8 Z( V3 P, s. m   m_bf.SourceConstantAlpha+=5;
" p% |: s, Y  w/ a3 A( ]; S( r( H. C7 L
   //当第一幅图片完全可见之后,显示另一张图片9 R  V: x2 ]0 s" g- f5 z: L
  ~7 C" T, ]  q( F
   if(m_bf.SourceConstantAlpha>=200)
& N" y( v2 L$ m/ R
- L; m6 O. T* J+ F* T   {
3 C# x) K% J. H; f: i$ K+ c& V9 M, ^6 k: \+ e5 p, S
   m_bf.SourceConstantAlpha=10;6 y! C* X. R: `- w0 _

* A5 k" b5 {0 r   //将bShowLantern做为显示标志,确认应该显示哪一张图片 ! V! s$ h7 M1 ^, H% s% ~
9 T- |0 w0 a- ~" T8 r% _
   bShowLantern=!bShowLantern;8 J: c2 _( X6 X' c/ E0 C0 }8 i
* H7 q( i7 v5 r4 S- t! o- `( C
   }) a! D8 o3 O% f! y3 S/ o/ `& T- O

8 J6 ~% B+ ?  q: D1 E' V   if(bShowLantern): B7 S4 O& a& O* b% s% ~

) M1 M" V. C# e) c: a% }2 K   {+ g/ d+ D7 v2 G" ^
% R8 b3 t" F9 [, b
   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);, _3 q! C9 G7 j+ ]

& J: u7 _) B- K% h1 i' o   }
/ D) E' b! [3 N& {# V2 Z& _; a! d8 \) i. S( m  m" e) j
   else
; \1 w: p- M0 {0 s( a' Z4 p  ~$ E( K2 F
   {1 ?+ `; ?$ s+ y" h5 ]3 u
5 }# Q0 L6 I- W# ?
   //按透明度递增的方式显示“背景”图片: b5 Z) M+ Q- F4 u+ L, _
3 `; r! b' {* V. ^2 _
   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);. A6 ~& ?8 q0 q( L' U# U

8 d( w" a4 z6 o6 D$ d7 F   }
3 S- \/ o) F5 Q# [# g% J* ^" K5 x. x* K& q4 ]
   CDialog::OnTimer(nIDEvent);
" `% b8 K" K* ^- P. n
3 S1 \. Z3 S: {# m4 L  }3 r7 L; U! h3 ]- F

$ Y  E# t* Y0 A9 b- i2 z8 g) Y1 \( ^: n
  编译说明
7 P, ]1 K6 G5 M# E1 |9 A
2 y( k" K( S8 \0 ~1 B# N) ~9 W0 Y4 g9 x9 ~
  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-8-9 04:28 , Processed in 0.034151 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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