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

图形擦除技术及编程应用

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

; W( X; J  s' F) B6 {) a: C/ p  解决方案/ d: }3 @2 Q4 p

0 i/ h* ~0 O2 w  X
7 D& S4 z: V0 h3 K, Y# n  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:
+ f0 w) U3 Q" B
. k: F3 d. k) O  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red
: e! ^7 t4 H5 k2 k7 H
! ^, w% b. x0 y$ ]# @. Y9 E& {  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
6 U: U0 j* n. P) j, X, t, j9 r, R, ^9 l7 a4 ]& z1 c
  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue: f, i; m- f) g- ~- h

' _$ y0 |$ I; x" a+ Y# p  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
3 c7 A& U1 A* x' ]& Y* _9 j
9 B: i( ?2 G9 P/ G* `5 z* |  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:  p8 l; m, g* V, u

- r. U3 i( T/ z1 N1 A. V% v* I  BOOL AlphaBlend(! v  d" V  Z8 h
9 }( v/ N: t  M5 @" }- x9 Z8 u
   HDC hdcDest,
1 Z7 {7 x* G. X9 c, r/ f4 t' O7 B9 H$ [. h% r5 `+ S% s! T& N5 A
  // 目标设备环境句柄
+ A0 B. V$ i- O8 L+ _9 t  L8 u, K2 W, A5 @/ e9 q$ d" m
   int nXOriginDest,
7 Y- r, }. Q+ z0 o9 {9 @0 N1 p5 u0 Q7 C$ T6 \( {; }4 D5 X
  // 目标坐标x# ~$ M4 i2 R9 m" m, ~) S4 I
( k* ]  C* G, C# R! c
   int nYOriginDest, / x6 D! E4 B" B& A
; e( U: g5 q, [& j. z
  // 目标坐标y- d. s: ?# q  y" M, C3 e

0 D! p+ R9 B7 W! W  s   int nWidthDest,
% I* E/ n/ z* G; j
# s/ o; `/ z0 x" y* J  // 目标宽度8 L/ x7 G' l& ^5 ~6 y2 t( A
" I. q3 \+ S5 u/ D
   int nHeightDest, : F0 [  q; O( I- t6 B- q

4 r3 y% N; x1 ^* p6 y  //目标高度
  g/ O: ?( |& x( a% ~
- D" |2 A/ F' T$ Q7 U3 _% `/ A   HDC hdcSrc, : ?- |2 T# a) T
' H: ]9 q  r2 Y- o) N; [# E" j
  //源设备环境句柄
2 o; d( L5 L1 _) m' x6 n
8 q" [. [  `6 S2 ]% n5 z   int nXOriginSrc, , M6 f0 {7 U3 w& T& i' X) e9 E" T

/ \, V$ q% @1 j5 t- Y  // 源坐标x
. ^$ y$ ]4 H) ~( }# N" J, J! b6 M8 c' t: D
   int nYOriginSrc,
0 |) G* M& b+ e( r3 }
' X, t. v- e3 t' X2 O- ]  // 源坐标y4 i5 A; w0 m1 R1 X
0 U) i9 v/ X* k& d# j* F1 j2 q: ?
   int nWidthSrc, ( J$ |2 j0 S2 N$ I" s
0 Y: _  ]! b; G! r6 H9 d
  //源宽度* @" X; X( C$ N9 Z/ H
) G" F/ @* Y5 i
   int nHeightSrc, ( i# ]! I: X9 _* U
# B  ]* S: h8 W& h; g! y
  //源高度
- c; b& [0 z3 o" E* N% N& K) T+ r+ N( L7 S, U( E' W
   BLENDFUNCTION blendFunction 6 _; L. n* V* y( [8 N# M. n

1 w- W; z: ~0 m' t$ k( C6 t  // 合成方式具体数据结构
4 L& G; F' {$ Q! M& y( g& r3 d6 k# k$ j# G
  );
0 o) m  b$ V  d
% n2 U4 j, m% D6 L) [" E  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:# K' f, t7 R9 I6 z" j) g7 i& ]

7 Y! s  N, n: z- d  typedef struct _BLENDFUNCTION {- W- B# O* D1 [! R  r
) B. {5 l8 ^4 s
   BYTE BlendOp;
! E, k; k8 a% B8 M$ R3 ~( o  y: q* o/ O. k6 o) y
   BYTE BlendFlags;//必须为零# T* L4 p% A1 H% O( D

5 ^4 S0 `% p$ w& T2 ]) {/ I0 W   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
) H7 |' F: m) O, `3 ]) R4 ?! J$ d  I% G# x- Y, [$ c, y, \
   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
" u. f- L; f- m: v; w8 ^
8 z4 {. Q7 j* ^8 ]  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;
/ ?; O) b- F4 O# ^8 Y0 E
: z1 V7 _$ Q/ X  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。2 Q$ f! j3 B  ~2 |" e* i

0 F6 R' |! |% n4 a. \: S$ @/ w
7 a/ z- w' q8 j" o, S  编程实现
' _; ^  P5 v' p
2 E& r7 V$ W3 E
' e0 \7 v2 ]6 k- C* O+ t  R, _  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。3 K+ P1 }8 {9 Y% R
! ^. X: L: ^  s) H8 h% j4 ?
  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:& ^! C2 X, l. c) U" t( h9 E
5 V) e9 L5 j$ D9 O. Z* p
  class CWipeImageDlg : public CDialog
/ H! c' V+ w8 Q- V' o1 u8 r" n5 J) g& a7 u% k+ q# J
  {. B- I7 Z; K4 I

6 P0 W+ ], }/ ~$ e  y( J) @6 w  // Construction
2 d1 X: O4 B8 h8 ?$ a  f5 b+ u, T% B0 }7 u! g2 E, i+ Y* D- x
  public:
% T; b- _" B8 p3 a/ P/ ^7 f' M" w% u$ X1 ?4 P: g+ K
   BLENDFUNCTION m_bf;1 O0 J* ~! x& J8 S

, ~/ L' ^& |( J* Q1 l   CBitmap cross,lantern;, B; f) h& i4 i% ^' R' F+ G* @1 S

; O1 J  j- m' G& \1 f6 [   BITMAP bmp;
' z6 F0 z) b, `' M) r. a9 _' {
& k6 P( A7 C$ t$ P   int bmpWidth,bmpHeight;$ B8 S% g8 ]3 N5 h  o  \( Q
  Z  }: E4 p9 r/ m, X
   CDC dcForCross,dcForLantern;
2 ], }) \. X- t+ W, P* n1 p9 n# }
   CDC? dc;/ f4 Z; C7 g$ C: O$ I) s5 q' C1 j
; R# g1 |5 P: c2 ]" o
   BOOL bShowLantern;
, c0 b5 V) k+ c5 E& i$ {" a8 \
8 k$ w1 s" |& j( z  ………(系统自动生成部分)8 N! j. b3 N& y2 A! y" I5 c6 o4 w
. s0 v  Q4 s$ S0 Q9 \% [5 H
  }; 
5 H9 Q- k+ K8 E8 T1 p; H
( o, m( n( M! j* _  [: i& {  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
3 A% E+ g1 h1 P6 U; T3 }# X3 a& B2 h' q, t$ {1 |7 w  {( ]1 q
  BOOL CWipeImageDlg::OnInitDialog()
1 |$ F, ]0 l& t' R) y8 N9 T& y/ t* a8 z1 |+ I* [8 i! ?
  {- l, `8 x. U7 o# |
0 ]( o% C+ @$ g/ o
  ………(系统自动生成部分)
, V" M  E# u$ q" M* `. a
, a3 R' L3 l: K& c! h   // TODO: Add extra initialization here! v4 h, B$ c. w* n! P- Q

3 o. ]) R; f: [5 T  O; ^2 M" p) n- G  w   //初始化全局成员变量: q  D3 L* _" ]. z  q

  }  E; x8 G8 k" f   this->bShowLantern=TRUE;- w: N+ W/ K, [* t2 h, z1 U* {4 m

7 S0 I- b7 h) y- I$ d3 Q. u   m_bf.BlendOp = AC_SRC_OVER;3 P0 b6 @: z; n" c

3 v; O& `. f: R4 v+ A8 H, M: ]   m_bf.BlendFlags = 0;; N* g5 w( X* k$ \7 h' q

) i8 U  s/ M' I8 q+ ~  e! \   m_bf.SourceConstantAlpha =10;7 e' k! e: n& J. ~% n5 {

; V% A7 q' O# E9 K3 \+ H   m_bf.AlphaFormat = 0;  L% \  A$ N: e! {) T

* X# p+ Z1 V/ x$ }  s   //为节约篇幅,以下代码中略去对操作不成功的处理代码
( N( _4 b$ M" k- c0 P; v6 q, F4 u8 h* X. M5 w9 r- l) `
   if(!cross.LoadBitmap(IDB_CROSS))
: k, Y  o2 L" e/ e) D6 Q- A! R0 e( a; g! J' }
   {
. L4 D( L$ Q4 |! v. _/ ?1 {+ M4 b5 T0 S: R% p) i5 H6 t
   AfxMessageBox("装载位图出错!");( s. s  ^1 v5 ^+ V

& a1 H6 e+ A7 o  N/ u   return FALSE;
3 D9 B- T  ], R. N: U. C$ m5 D- P; L' r$ o
   }% m; H( q# g; n  ~3 E# x/ Y9 L- r
" x% f0 a/ `) B; Q& `2 w8 l$ ^& t
   cross.GetBitmap(&&bmp);. l. v0 I; v2 a" k; Y
5 J; B( W! I. ?6 U+ l" }
   lantern.LoadBitmap(IDB_LANTERN);
. O( z9 `" f; T  S. g, {
- Q4 I4 K0 E% d   cross.GetBitmap(&&bmp);
* [  G9 I- {5 r- U; t; A( y( z* X' j: Y# X2 D
   //获得位图的大小信息  l) H( s, f1 G/ M- z

, n, o3 c3 D' R' O   bmpWidth=bmp.bmWidth;. i( Q- y% y) n* b3 }% r) q
& f" k$ }9 L7 G4 x( f) q( T5 o* A
   bmpHeight=bmp.bmHeight;
( H! E" s% U1 n. _/ a; E3 I1 q- r- h+ O9 N( g
  dc=this->GetDC();
' Q+ s, N9 x5 I' p( ^3 F: N6 e, O& c
7 N0 }! V( |( F3 ^   dcForCross.CreateCompatibleDC(dc);
2 E! B- x+ P8 j) `3 _
- Y$ ]5 g$ n  e# z0 G" _   dcForLantern.CreateCompatibleDC(dc);
+ U2 _2 x% o% m) j+ K! M( \% o' i  }7 y8 K* y2 C
   //将位图装入设备环境句柄) H; q* u7 Q. ^" \) H0 U9 |* {! M
! @# H6 y5 F2 I( y7 }
   dcForCross.SelectObject(&&cross);
1 @, I# ?; Q/ ~1 B8 A4 M2 ]. y& \3 u5 u  Q9 ^
   dcForLantern.SelectObject(&&lantern);
: w; G8 ?% a4 g1 M
9 n5 u/ W9 P2 h   //打开计时器
" l% v4 C- W2 O: ~% x( B
( x( ?, L1 M! F5 r* F9 g   SetTimer(1000,50,NULL);
- x+ R- r% ]1 e7 v0 `5 ?
9 h  n  x2 [( V: m% l% V; M  z   return TRUE; - i4 g0 z- }$ s& p6 [4 x2 @
' Z9 H; O$ k3 v) i
  }
) e# I: v: {& c7 {. ?$ z& q) q, j
  void CWipeImageDlg::OnTimer(UINT nIDEvent)
+ Y* e: t, N6 ~" l2 I) M0 r! Y: S
! b4 C, U" ^+ y# A) ]( ?$ N  {
( P! V- R9 l1 \& n- ?
2 \5 P- [8 @; ]   //图片透明度每次递增5点
6 U' @2 c* s1 y; Q0 R2 E, k# U- X: r* x$ y% V4 T% h
   m_bf.SourceConstantAlpha+=5;4 S6 l/ o  f) s$ z

) V5 v5 T, f7 V7 M   //当第一幅图片完全可见之后,显示另一张图片
& O2 _: C" B. t1 f& z
$ i% s/ O* Y4 y5 j2 |* b   if(m_bf.SourceConstantAlpha>=200)& h( }9 V% e% f; D

1 ~  w6 @" x" z- |% A   {0 ]$ W( I: v! b6 E( e
  ?! D2 D% h7 E3 s0 z2 ^
   m_bf.SourceConstantAlpha=10;
4 q+ {( Z8 T& Q' G& M8 M6 n; S8 a4 j' y
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 ' q6 W7 X# o  i0 R
  d7 F4 A! j4 t6 Z) k
   bShowLantern=!bShowLantern;8 {2 Q- |/ T( a( c9 x) \2 R
# s0 V! v* M+ ~& U
   }
+ P2 l! u# U8 Z- \5 C6 u: [: D
   if(bShowLantern)+ J4 }6 O! b7 g: v% G
/ |! M/ U- N& c. I6 T* X8 {0 u; B
   {
7 m, d% Y- ?: s- ?; F" w
/ q0 u- c4 [: j+ P" V   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);
- }, C9 F. ^: F9 y" z' l, p
9 J1 n: M, B( W& y9 Q* y+ |   }
% ^4 g( Z+ m/ V! S7 A3 ]5 M0 C8 g4 [; v2 D9 E
   else7 z4 `1 B6 ?0 g2 @3 d
" M  n1 M  \0 J$ ^- G4 V1 e% d4 f
   {
: w7 L. q0 Q# L5 g& ^) _, F# T$ t
   //按透明度递增的方式显示“背景”图片5 T  E  K. S4 g( |; [( ~: p

0 p7 E3 J3 {3 P* F   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
" s# z0 \$ |4 z% b" r; w0 S. g8 w; q
* E( ]6 r/ M2 A( s3 Q( Z+ o- q   }
2 I/ {  Z- t( x) n# W# ~; e# _+ m. i  v) ]2 Z1 k) J
   CDialog::OnTimer(nIDEvent);
4 h' w" R: j$ D6 @
. o- c1 r( [; ]) C2 D3 E8 B$ A  }  A* G; r, _0 q4 |% `  s

$ d8 \! d* q' \, g- Y; e/ }, q  z
  编译说明
) X* \" r4 \+ {6 U3 y# F) d, v
( a( n) N9 s2 b- |9 c! e) y3 Y; w* Z- n: v5 w8 R, {2 Y; ?! _
  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 10:23 , Processed in 0.019179 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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