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

图形擦除技术及编程应用

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

: K4 t  ~5 ~" b3 x9 c# x  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:! t* J4 V3 G9 `) \

( S% D2 }$ M6 l: B- X+ F: ]1 M  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red
* B% i' G, H: T: I6 T( l* i4 ?7 V$ {
  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green ' P0 P$ b- T$ y* ]( j. [. ~0 q
7 y' J. ^. _- O. k
  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue
5 r3 \( B( O0 Y$ ]$ ^: \' c; p7 Q: V8 \. F
  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
4 C! ]. j2 t  U5 y. ?1 C$ C4 O2 `
  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:2 ^1 ]) m+ M0 Z. d! ?: U
: S: j( m9 x' M. E6 q6 B4 z
  BOOL AlphaBlend($ a& Q9 j1 h1 t5 W" `
4 q; y9 o6 q3 k3 A" [
   HDC hdcDest,
: j9 ^- y3 D1 u8 N' c! ^* C
0 t! E4 e" {2 q; i: X  // 目标设备环境句柄/ [' I/ l7 a, a

0 G( N% V+ c" ]5 x1 c   int nXOriginDest,
3 l. h" X+ U+ Z9 y3 A7 m7 D) o4 b+ f0 j$ `4 Y: n0 X9 V: D7 J
  // 目标坐标x2 M% }8 q9 ]6 }) k& h
: Q* Q2 C. K* j) m/ g/ E
   int nYOriginDest,
6 l4 Q( h9 G2 G/ R; Q+ X) u
3 o7 f+ J% X9 O+ O) `2 Q7 `9 J  // 目标坐标y& k( T3 I, H; b2 p+ a1 Q# V: u4 h

0 `, i; J2 c+ a- E9 l& |2 a: P7 v, F7 X   int nWidthDest, : ]( b/ |! @1 W) ^# d9 K
2 d* j7 D/ j# ?5 g, P2 C0 y5 {
  // 目标宽度0 c) l( _9 u( D
. \  _( E8 T* v
   int nHeightDest, . ]7 V4 u5 _" U( y1 m- f

9 K, F5 I, }, d3 n6 ?* O- e( }  //目标高度
8 D, s) q: V7 K; n1 a' X9 m% Z  ~" ]2 J2 E1 M4 N
   HDC hdcSrc, 7 y% _1 _0 Z, E
2 u9 h. {, @: i2 m! ?
  //源设备环境句柄' i6 r! p" d1 y
: y4 g" O$ C) L: k, z$ j
   int nXOriginSrc,
( a, P; V8 b! _6 \7 h
) P% ^, o8 m- X# m. a  // 源坐标x) s7 ~( l0 S, Y* l

" l9 R) @; O) O  ^   int nYOriginSrc, / P8 J4 v9 \! L5 f

" F. i5 T( N: G  // 源坐标y
* k, p4 M) q5 I% s
: g7 z; V; Y& W9 Y/ _   int nWidthSrc,
; a: _1 Z5 T: U! X; p3 w( g  @% Y
8 X$ u& r$ P* s7 G' c2 F  q( l  //源宽度
1 @7 j( {4 M7 s" a" n5 O! ?
8 {6 \: \# w+ `   int nHeightSrc, " k% i$ o! M1 ?; p" e$ X
5 u1 g& J4 j' A6 D
  //源高度/ l5 \7 p( Q0 j4 f. Y' P7 k& ?

; E9 n; r9 ]4 o" m" @  H   BLENDFUNCTION blendFunction
# u4 A' S8 ?" N4 y) Z( F& T
7 e- e" v% i- {7 P9 k1 s. T( ~6 |- \  // 合成方式具体数据结构
- \3 k6 o- v/ R' r4 J* J+ d
+ A1 Z) f$ B2 i: x  );
8 ]# J9 e, J- N8 c: x, {1 @5 X$ R% W- ~  E+ k( |* L! l4 i% [
  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:
8 H; y' n" q8 i5 r& s/ U. f; u0 t( W! w+ n2 \5 D
  typedef struct _BLENDFUNCTION {
9 P" B( t9 f/ e* R: U8 y% e
/ R" M% `( Y' e5 @" d" `; F3 f   BYTE BlendOp;
% T9 |/ ^( Q) D) w& P8 O" Q  B. P/ |0 z8 i; w1 r
   BYTE BlendFlags;//必须为零: W) [, w8 U: J$ u( c* M

/ n+ z$ l# e" Y: X" V4 O   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
1 [6 x, e7 A! X3 Y' s5 T9 _- h, ^( l; }5 S# ^7 N2 B
   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
" b, }. Z' l8 Z  d; m
. X3 O7 e1 M8 H4 x' u- T& k  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;2 d: Z5 H! g# F, O, e6 ^

) D  B3 _  O- e! \4 y. r  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
7 S9 \4 X$ a9 r# u1 o
5 f; n+ c+ S! M
0 G5 [6 k* p2 z" g* ]+ X  编程实现
( V# d5 s: @' T- \1 P
6 l' D0 T4 t5 h) K+ S& |* X3 \
& a/ \* j( L/ `# y  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
( v8 n* C- h$ @/ O$ N. n$ J; \
2 T8 |. ^+ ~6 E7 N7 h  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:. B! M* ?& a3 a/ U$ e( O8 j

% l' \5 [9 J% M, Y. R  class CWipeImageDlg : public CDialog" Y1 t) Q& f: E) b2 E- ?; A
' p4 C( f1 s% ?6 s  n9 R8 e8 H
  {! f. I; }. Z1 K

9 `) s6 ~& x$ y/ Q) o" E  // Construction9 ^# i  j. ?  E5 j2 A3 {* K5 h' f5 O
; i3 G: Q9 r; B) [7 d+ z
  public:
) P! G: G6 }: w7 }. k& e. Y# H6 M
3 e+ L7 ~+ j1 _1 N4 x+ p   BLENDFUNCTION m_bf;
0 \2 X6 s* P! z- C3 m5 h) h- l. Y' c( [3 E7 O, O
   CBitmap cross,lantern;
  h/ \" ?0 l# J
$ t3 q" o' G) `% U   BITMAP bmp;7 Q& Y; A" m: i# [7 o

0 u  \6 }% M6 k   int bmpWidth,bmpHeight;
! Y* Q$ T+ U: U6 d1 [, V
& f% m- S$ ?/ ~9 F# Y   CDC dcForCross,dcForLantern;& R# ?% k" N0 t) ^" e0 Y7 g3 C: ~
! H$ G, N, \6 q7 G0 D) |# o" N3 ]
   CDC? dc;6 v1 C- `6 P2 n: `! L. C' Z8 U
0 v, D! e" p# C7 S
   BOOL bShowLantern;* [" `& o: N# f
$ s* v! `' ?- D. F1 Z5 S7 K
  ………(系统自动生成部分)
. r- f7 C2 V6 f7 ]. r
, ?6 ?8 k7 @  X: x8 l  }; 
. T, G0 [; k2 s: `+ e6 s. G- q' e% J& L% U4 L
  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
  \; l' b/ ]/ ^9 }
) Y% l% g- M# }" k+ Y1 x! u$ _  BOOL CWipeImageDlg::OnInitDialog()
$ `$ y  Q, w: ~: G! |; |2 ^3 v8 G) {$ }
  {
, t) ~% h2 t- z0 x: h2 L( f6 A/ ?4 L4 P- b- K
  ………(系统自动生成部分)6 Q# U! c8 x* w- R3 Z7 U. t* m
: Q. Q# @6 A: S, _$ m
   // TODO: Add extra initialization here
9 b0 B  V! T- M) s( z* ?" P) B! y9 K+ W) S" p. H. d
   //初始化全局成员变量
0 `) e/ X6 [9 T' i$ x! F* r( v( P! r+ V; B) t2 J
   this->bShowLantern=TRUE;
  t8 i9 j* U2 }4 O5 w2 e
0 X  T( K* Q) H- c1 ~; _& T8 f   m_bf.BlendOp = AC_SRC_OVER;
% z& D) J7 S( O  l5 ]  l6 a* h1 S; A* d  k
   m_bf.BlendFlags = 0;
4 I. r7 d  d/ [
7 B8 r+ K" ~7 l& Q3 i: X   m_bf.SourceConstantAlpha =10;" L/ q* N# n3 H( X( K

' J, R; `' R. e5 X# j# v   m_bf.AlphaFormat = 0;
/ x9 X8 r# ^- r) h6 C, N+ j! a+ w3 n/ d# D- s( K1 F, z( K
   //为节约篇幅,以下代码中略去对操作不成功的处理代码& w& A1 O# [! @& A  _8 x9 E+ o5 }
2 p( J1 S+ p8 U7 P; R
   if(!cross.LoadBitmap(IDB_CROSS))
. \- j' I, L2 _2 K
9 e$ k* d/ ]- ^$ U' V   {" F9 Y% J- D- H! Z) a; y
/ a& B+ T2 t- z' f
   AfxMessageBox("装载位图出错!");
; }5 n# X2 Y. ^. c" B& x$ m  g% ?6 B
   return FALSE;7 b6 T( n3 ?" ^# ~  Q- b
( B2 {! ]/ G/ {
   }# V4 U/ p. o5 i6 L& _; _9 W* i
. O% O! m+ Q6 K3 o# K% y9 x
   cross.GetBitmap(&&bmp);
3 ?) P- y! j5 S6 z# R3 P
" D8 A% k$ v/ d+ a. N1 u1 @   lantern.LoadBitmap(IDB_LANTERN);
6 X, L, e, p" X* H! d7 w9 U/ e) l2 N7 w5 B, {
   cross.GetBitmap(&&bmp);
# p* T9 u' m! L) ^6 F
9 n) G- i$ V) h6 F; Y! H   //获得位图的大小信息; ^$ Y  K, J0 Q! b& V; |

4 S" T# j5 ~+ R1 ^  ^7 I* }4 Z% V   bmpWidth=bmp.bmWidth;7 z& R6 ~: r# g0 h7 k

$ k0 a5 D" ~. }, Y: E& z7 j: L, C   bmpHeight=bmp.bmHeight;% t6 R) Z5 r; ~2 B/ U

% b) @3 O* `( `/ ?- \. t* k& c  dc=this->GetDC();2 ?# i9 k- V/ g5 Z( p- q& g, m
8 n- G6 ^; D( F0 ~- k- C
   dcForCross.CreateCompatibleDC(dc);  A' ?" J2 H/ D  Z7 m3 E
! ^. J5 p' @+ x6 M
   dcForLantern.CreateCompatibleDC(dc);" A+ x& _% C' A. L* s2 s
/ ^! i7 N0 C: v$ l* `
   //将位图装入设备环境句柄
, C4 I( @. @' E2 a& Y  q, }, Q; |4 A/ U7 q
   dcForCross.SelectObject(&&cross);
/ {3 ]( R* A! a) k/ F& m# s4 h1 u# r# U' U' E  U
   dcForLantern.SelectObject(&&lantern);
; i3 V9 x3 H2 I0 P* t# e: ~' b4 u9 Y7 A& b' L# J6 j
   //打开计时器
/ o4 t+ U( g  e5 Z, b0 w8 b
- B/ }+ e/ B# {- u0 u$ y# A   SetTimer(1000,50,NULL);
+ ~2 F  S* G. d) G7 q/ E4 b; w% V0 J2 L& ?5 J" r
   return TRUE; - [& R/ d4 _$ ?6 a4 r. h8 \
4 t8 Q$ b  Z. h% f
  }1 f, I( [- S# Z+ Y" G+ }3 l: r- M, z( U

  z, G0 V$ c: r* g) A8 `6 Q  void CWipeImageDlg::OnTimer(UINT nIDEvent)
4 B( O5 S' Z; p5 s3 w* s8 T/ ~0 u) Q+ E# W
  {
- F  ]8 t* g  v/ |* V0 N  H8 O  ]
& D; P8 m" W* g" b3 E   //图片透明度每次递增5点  t/ Q& y# H- q2 Q5 s% D; s- \

0 W; S& Z  C. y' W0 n: ]' X   m_bf.SourceConstantAlpha+=5;4 @- b2 s; h3 k% M

+ B3 r& d" _( K0 w) B2 e% M   //当第一幅图片完全可见之后,显示另一张图片
. i. y$ H! y% A. W
! [+ Z" K# ^- U( L8 F/ U7 E! I& t   if(m_bf.SourceConstantAlpha>=200)
: ]3 k3 K$ M6 P: I7 o  R- s- y3 ~! V- o$ x
   {, Z8 N- A* K+ `& j9 p6 K9 I
: d( h3 h0 `/ `6 {0 ^
   m_bf.SourceConstantAlpha=10;
1 b4 Z8 Z6 R; o+ O& e$ v' J- t. @% h1 L; H8 K4 z; z3 L2 M
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 ( Y! Q4 l- }4 M3 u) T8 Q; Y7 D, N% C6 t

/ }2 K0 F2 C, ^( q( h   bShowLantern=!bShowLantern;2 H% {: d, M1 S, n# b

* \$ t' ~( F' H1 i+ n" ~   }
9 H9 \1 t' f  T* e
8 I5 o3 W$ |  I9 ~   if(bShowLantern)' `$ Y0 ^" z* y* R

8 z+ x! N9 y( L6 m+ n   {
6 `5 H7 D8 ]% k2 _8 F" U  N( o$ v! V! D0 p; ^
   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);9 ^) W  d7 ?. D6 M: w3 U- W
8 T# d8 s* [  g9 I
   }
: R% H. Y  U5 c5 e: ?- A0 `# Y' \* d* ~5 g% @: V' j# H) q( _0 }
   else
: N2 ]5 Z: G$ `9 \/ S3 @! ~( X2 o: E0 q' F/ ]/ E6 L7 g) [" e
   {) A( q! _9 `/ K5 U  r

9 K+ i' J" R: {' U* F* k0 P$ q; k   //按透明度递增的方式显示“背景”图片7 ]+ m! O  b2 D" d3 K( r
& U- u6 N! t) E
   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
4 q1 N4 M+ @( W5 F# @0 ]8 ?% J+ y
" u, W7 s& ^$ W   }8 U  m" [" ]2 q. H1 O
! ]1 ~3 h6 A/ |  W1 H; C3 A
   CDialog::OnTimer(nIDEvent);8 J& |8 S0 A" x6 K) Z/ `4 B4 T

% b: b* \# j+ c; P5 L  }8 u! w6 [+ z3 K2 W
4 I& [, X+ Q% C7 k, A* t

+ i2 k9 B, L$ O& n3 n0 S  编译说明2 \6 d( |# p9 A; A/ H. D* _! _  {
: N0 P. B8 ^/ m+ D0 N% y

* W+ G; c0 ~* J; D  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-15 05:04 , Processed in 0.017116 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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