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

图形擦除技术及编程应用

[复制链接]
发表于 2003-10-12 23:35:05 | 显示全部楼层 |阅读模式
周鸣扬 ' u; I9 E, P6 I2 c

0 a+ P7 S. a% m' |9 Q( n  图形擦除是图形特技处理中最为常见的一种,在各种游戏中图形擦除技术有着广泛的应用。图形擦除在本质上是图形的消隐,即在两幅图片之间进行图片的平滑过渡显示。过渡的方式决定了图形擦除的不同视觉效果,其中最为常见的一种就是图片淡入淡出的更新:两幅图片由明到暗、由暗到明的循环交替显示。这种特技效果在编程中的实现,往往是通过DirectX技术实现的:DirectX Transform为我们提供了一个“Microsoft DirectAnimation Control”的类(在注册表中可以找到该类的注册信息HKEY_CLASS_ROOT\CLSID\{B6FFC24C-7E13-11D0- 9B47-00C04FC2F51D})供调用,以此实现高质量的图片擦除。不过,对于DirectX编程,大部分的编程爱好者对其程序框架难以适应,可以说,花在理解DirectX编程上的工夫要远远大于对图形擦除技术本身的理解。有没有一种更简单的方法,使用常规的编程方式来实现图形擦除呢?0 t. z+ s+ T) a1 |9 T. s* u- r

* V8 `( J$ [3 o' F0 C( D( l# l5 c3 B$ U' `, M
  解决方案/ h: W$ v4 o& c! |! D& Q

) r' Y% m; O8 o* ^7 `( c  V6 b# _* w, J3 ?
  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:- L% Y( @1 V1 I- p0 o
* f" D1 o7 B) C, j1 r( u
  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red # {5 _' J) ^# H) N& u7 P' g9 r
& K8 \4 c1 A4 f5 ~
  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green 7 R0 B6 m* r1 d& t& }: c$ W

* ^, l; K2 S2 r8 O+ r  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue: B: `, _0 I( Y9 [3 n
+ T: G0 I7 c! @
  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。6 o  N" b; Z! w# t

3 O+ q* h7 m# n  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
, X" p1 l. Z6 g9 x! y9 F6 D, ?8 n
4 @% j1 V$ _# V  U/ H  BOOL AlphaBlend(3 P: O2 w8 b' P' n; D

% O5 i# l8 H1 M9 y* R' m! `   HDC hdcDest, : O* y0 d) J* ]* @1 [) Z( C
' p$ B9 B' J7 ^' y  b4 n$ B8 f
  // 目标设备环境句柄& P9 b. A  ~+ E
# g/ z" a7 X9 ?/ ?3 g" s# e
   int nXOriginDest,
- `# _5 F2 ]% q2 b5 x: S! ?
( v6 ~7 y  B4 P3 B3 _' M. p; T  // 目标坐标x. S: ]) b% z, ^% |

8 ~, D. d/ T+ G   int nYOriginDest, + W+ |  e. C0 ^* E- n: Q2 |$ F4 R

$ Y! X. W. @) ]3 q  // 目标坐标y5 x" k" y6 d+ G' O' q
* a- M: T4 Q+ X- |5 O% z% ?
   int nWidthDest,
. L$ Q2 A. S  e
2 j+ L3 V/ }; c: Z5 g: U, G$ \  // 目标宽度
2 ^$ O  O5 l+ t1 m2 E* g6 Y. [6 ?+ O' d5 |( _( r& G, r& T2 T
   int nHeightDest, 4 r' w6 l5 q5 a) x6 i
6 p, z- {6 h: }1 P% K; \
  //目标高度0 O1 @- g3 b# H! x

  f; w2 @4 U8 D# t% M7 g1 O0 o  T   HDC hdcSrc,
! a7 o  c8 |- f- e- N8 G2 R5 {% a- w/ V
  //源设备环境句柄% C3 h, W5 K4 g3 J3 T+ V

0 f/ l3 q5 ]0 f   int nXOriginSrc, $ @+ K: M$ m- k/ e: P0 s6 P

+ P' k: p' C; n+ e  // 源坐标x
8 j- V) t9 n5 o  @& `6 K: t6 L- K/ `
   int nYOriginSrc, % G5 M. Q% n6 L1 X  K* W
# I" c4 [+ v- s% U
  // 源坐标y
$ L$ K% H5 B% n9 N6 U, S
8 u( ~" u. [- p  O. u   int nWidthSrc, 8 S( q* Q/ C" x0 u% Q

3 \$ g( T# Z7 F! D. S% \  //源宽度9 O2 t9 n0 h4 V
) i) e: s8 J2 Y7 [$ N  p. I
   int nHeightSrc,   z0 ]1 c# V( ~8 @; |& ^
! N7 S* ]1 M6 R( ^9 N4 t0 a
  //源高度& x% t7 ]" q$ k
( ^/ a9 z6 u* k4 D, a/ k4 h
   BLENDFUNCTION blendFunction
8 J$ A& p' B. J& a4 R' r. @: p, N/ S6 J; G  `/ j. S4 `# d
  // 合成方式具体数据结构
% }8 M3 Z, C* ]! r* {$ b/ _
) m+ {2 p2 F) F# R! j/ V# g  );7 R! k4 r! l1 C# I- I

8 w% {* T8 N* `/ i( @6 W. {  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:
& s% B- k* w5 D4 g/ J; a
3 C, z, {, c1 k! G! P  J  typedef struct _BLENDFUNCTION {
: j/ g! w8 o3 B5 `! ?8 D1 ~3 u# v/ b0 C& s8 ?0 N' W; J
   BYTE BlendOp;
6 I9 ?+ i( F; ?$ N% F6 g$ M9 n( f2 f- [6 ?( t4 J
   BYTE BlendFlags;//必须为零+ G6 j6 q' O: s; e# @- b9 G
0 D$ e- n5 y% x0 B7 F* a2 M2 R" r
   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
& N! h, P5 P$ G7 S6 p# @
2 `. o9 ?, \9 J2 n1 k9 ~   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
* Y3 u: p( o3 B( T# x% t8 m
* Z) a" u. X1 g6 H( f  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;
" {5 Z# _3 Y# O6 C2 ?  G# q, C2 S$ ^* l9 [4 R2 N" P
  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。" V( W  ^0 f3 Q& n; j  U! g

, I- c5 J/ z) `3 c5 D
! ^+ W: }2 M" Z+ ]) P  编程实现
- r, I% S/ W, N7 I7 ]5 l0 T7 [& s5 `5 U' R: Y0 O
7 S7 w! B" Q% N* j; {, R  h! x
  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
+ ]5 D8 R( r9 r" E6 W# L
" {5 j7 ?& o0 Q  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:' ]5 F/ ?, `9 k) s7 ^+ L0 B  k

9 n6 `5 e+ i5 g/ U7 j  class CWipeImageDlg : public CDialog
5 }5 j, ]# e9 u% o6 p
/ K8 J* G' E* R9 E: l3 _  {
. Q1 Q; ?& F! L) Y3 y
0 \; Q7 _( ~" y! X7 g  // Construction0 D4 t. `6 R! H5 d2 f8 H4 k/ A

# g& J6 K' W) u+ g2 Y  public:$ U' H+ B* T, H. A

3 |1 g3 L9 E7 o( \7 v   BLENDFUNCTION m_bf;' e' c- k% q5 J: ~9 `
4 P& r1 {% f& I! _8 U
   CBitmap cross,lantern;
; I" w1 _8 V$ o- p5 F8 m$ X
. H; \; @9 D4 y* U2 P5 D   BITMAP bmp;
3 `' O" s  l7 r; f9 z
% @: l2 A$ @& }1 ^4 H# k+ c   int bmpWidth,bmpHeight;" V8 g% q# ]) j1 |
- Z5 J, s' K# f' G! r
   CDC dcForCross,dcForLantern;
6 b' }  l  M- Q! P/ E1 S6 r! d  y  _2 m
   CDC? dc;9 F, |4 M" R' j9 Y, G* O2 P
  i! \$ M) u) ^. W
   BOOL bShowLantern;- X' h. ]2 A# D. x/ B  G
0 C# I$ H1 W" i
  ………(系统自动生成部分)$ g+ @. e: N- \# C" o: \  N3 i! v
6 U( [7 {" i/ V) M+ V/ m+ M
  }; 9 \' `  K6 @0 s" Y

" p% t- u: U. N1 M- S( D  b  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:' j5 N: f# }5 s4 n% {* j
3 s/ w- L2 a1 h; q1 s' F
  BOOL CWipeImageDlg::OnInitDialog()
8 S% ]! M# R- Z1 M
8 E4 j& V* M+ M2 [* m7 g  {+ _+ K! i% ?' b8 O" f

# e8 ]0 V* \% k( Y  ………(系统自动生成部分)! m0 U  n0 x  i& m, F7 k
( B9 k4 b( h8 e$ H. L
   // TODO: Add extra initialization here
2 t/ t2 U: j8 r* w6 Y) q" h2 @6 V4 S$ B/ U; x
   //初始化全局成员变量3 ]: h( U3 i0 `, p  u

# k! m+ U9 i, E1 }) ?   this->bShowLantern=TRUE;
+ G- z9 m) ?3 e4 @$ _
+ h/ I6 R7 [' {. e. N: r5 ^* Q   m_bf.BlendOp = AC_SRC_OVER;
" S* w  U9 Z9 y* z* f* \3 W7 c* m& d- c, b- Z
   m_bf.BlendFlags = 0;7 p3 I5 o5 [$ v% Y

' W9 a' T0 p# V0 B+ p+ H* `' ~/ K+ |   m_bf.SourceConstantAlpha =10;5 r, D% M% f. @1 D! ~2 Q
2 K" t: g! a( w( w& o. b
   m_bf.AlphaFormat = 0;6 o$ v* i4 e" F; r' B1 h! F
1 D6 d+ ~2 \0 I, t2 v, i
   //为节约篇幅,以下代码中略去对操作不成功的处理代码
& o/ z+ V' c4 E, e8 N
6 w& w' D# S+ q7 ]- B   if(!cross.LoadBitmap(IDB_CROSS))
+ [  b7 F+ a- B5 h
: O% t  M7 O0 u' Q4 f: l   {+ e7 V* u$ h  F7 g+ M& J+ s

; k9 w' [8 Y! {2 Z   AfxMessageBox("装载位图出错!");
0 E# G/ l& x4 N8 ]) b
  P# M1 P2 ?* G2 `   return FALSE;
) j, l  D/ ]% }; O. K" _4 a
* x  E/ y# z' |6 |0 }! w   }/ h: W& ?1 M  X+ ]" o
6 c% k- V% L: Y# y
   cross.GetBitmap(&&bmp);" x( C1 W) K: s( K3 Z

( @$ {' L" B% \3 F' L3 B) W8 h) R   lantern.LoadBitmap(IDB_LANTERN);
! v8 f7 @$ Q3 n* V
8 m! s+ X, B  h) }  T   cross.GetBitmap(&&bmp);: B$ Q1 }" n1 s
9 A8 Z; ~" c  e
   //获得位图的大小信息
( R9 B: R3 T1 i% i* B8 }2 }
7 s. t0 c+ F  B; k+ }   bmpWidth=bmp.bmWidth;7 j; }- [( J' S- H4 G4 e
* I5 R5 F5 s8 C6 p
   bmpHeight=bmp.bmHeight;
  p, j" R) K: h2 C: K. ?) t2 \3 E6 Z5 |' F& S! S% T/ l
  dc=this->GetDC();
. u# ?0 Q$ Z) _! e( H% V+ b
, v3 N/ t0 W- }. \- E   dcForCross.CreateCompatibleDC(dc);/ [+ _; Q3 u" V: e# @) R+ ]5 x$ D. U

& W) b6 g/ V. L. |, ^, r" Z: J' W   dcForLantern.CreateCompatibleDC(dc);5 z5 J  q5 {* j1 p# K  r

  l; d, F( |+ `5 w" @: ?* b  l1 Y   //将位图装入设备环境句柄
! h, i% O; s, g2 O+ H7 s" I! `6 [! F+ l* a
   dcForCross.SelectObject(&&cross);  }9 h, n; I3 O7 {, }( v

3 F) ]  H* c2 S" ^1 ?+ t: G/ s3 T   dcForLantern.SelectObject(&&lantern);- ^, f0 y" r+ g# a$ }; n1 L
& ?* }# O$ I* P
   //打开计时器
9 M3 E, J1 ?, I! \/ y) T+ n  S4 e7 D& v9 s$ E) a+ q
   SetTimer(1000,50,NULL);
# W' `5 l$ f. L+ y0 k3 P* q2 F" _. v/ F  C) T  a) A
   return TRUE; ) t- m1 S6 V+ \: ~0 }

' E2 _- N+ `2 z9 \# O' g+ V  }( g+ U" _6 l% ^- c! U" }  z) j  S

1 L/ f% }, A- Q8 Z  void CWipeImageDlg::OnTimer(UINT nIDEvent)) Y, p) a4 p, g$ _0 P$ \
0 N( t8 M: d7 G& e6 n9 _
  {# r) d# d4 v) Z( C+ Z/ m2 S
6 X" `3 V5 @. ~( N8 \' f/ e0 z* w
   //图片透明度每次递增5点, Z3 u/ I& O8 X3 \8 X* Y% X3 q6 S
9 J% y' z, C/ H0 ^3 X3 Y7 i  i# C: t4 Z
   m_bf.SourceConstantAlpha+=5;# z; V# t! h+ @/ X  M+ Q; [
( T/ J* U& v2 y* \- n" ]. Q" n
   //当第一幅图片完全可见之后,显示另一张图片0 v; x  G" q' M6 Q& r) }( d8 ^
5 x' y6 ]/ @2 Z; a6 F
   if(m_bf.SourceConstantAlpha>=200)
. ^$ X/ _: _* z2 f5 }
& M2 B/ m( M1 m, [- B   {
: V- g* b; Q  a8 n2 Y! |. r" G  F2 I- U
; p7 V5 R, v& ]; @   m_bf.SourceConstantAlpha=10;7 ~! Z# ~4 ]8 x6 v* U9 g
( @4 H: L8 h) w6 }- S
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 8 K: @6 Z& O! E/ ?4 s8 E  ]+ L% o
. b/ w  K4 Y1 a# e; F
   bShowLantern=!bShowLantern;( c' m# H: c& A+ D
( o/ U' F0 E1 G- G
   }* _* g$ H/ l1 V, z/ y# r% w+ M2 V% j
% \) |6 D, n: i" r$ I' m; g. d
   if(bShowLantern). ~  h& M% ?4 ?- G$ M) A( N

( Z1 z6 f4 D, \   {
2 P  H) `8 Q7 k) u& O, \
- [6 Z* M7 R- O# J6 u) o$ B# K! r7 ^   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);
; @& f) Y3 |4 H$ N7 A# i" a5 l4 t" }" S
   }
5 m6 k0 d1 T4 i( `. {: o# a  [4 _+ O. ?0 l8 \# L
   else
  W" }! w5 ]2 q+ \* r( f6 ]0 H" p# O
   {
9 a/ P/ l$ q4 D! O$ ~3 s
2 f# q6 T5 J: r1 F   //按透明度递增的方式显示“背景”图片$ Z! F% o* ~( H# m- F8 c$ K" {
, ~% F9 k% e+ s  L& Q
   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
1 F' W, _% \1 u* m3 t  R, P8 H- \4 c! z8 H6 g; R3 D& ^
   }
; _# P+ F# h( {- d: B% m  p/ z; r3 ~. V" b1 V* N# ~
   CDialog::OnTimer(nIDEvent);
0 `; L7 s" ?# w* r0 |8 o% I) a, F( w& G' j3 `' }/ b# u% ?
  }1 J7 @, N4 |8 l6 x: @+ p& j

0 n$ O4 x4 c* F, E
4 \$ t& L7 E3 b& w  编译说明( [* |8 F3 `- Z: K" K2 m* f

$ \- H9 _8 y: Z1 }; @. N! r% h! s; P0 T& t
  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-13 22:46 , Processed in 0.020115 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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