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

图形擦除技术及编程应用

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

9 h) g! c! O( U0 M0 P& ], u0 c  `8 n- E; @; z5 i/ R, }! I2 c) E( L: @
  解决方案* L3 E3 I' e0 D5 U3 R! ^) d3 S

3 M# ~3 D: O, i! q
$ L$ L1 G6 l, e0 e1 a. m0 `! R  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:4 ~1 _- }' @7 x5 |
4 _7 [9 r0 m( x, N! [
  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red 0 W, M4 h/ K& Z% J' D9 G5 ^

. y0 b  h! y; `  \" j+ K' b  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
3 P4 ^$ n6 h7 }7 C1 ?
# b  L0 s- R4 l9 r& w  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue
- g  R* j) T' J% g- M$ |
/ R$ a) \. r- ~) ^! p  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。1 x& d- \, d- I7 X& ~  M3 \
  V; X5 @  J# a  N
  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:5 {- a1 K6 O. H: D4 z: }

& m2 W7 \( ]; N. N* j& R! v( d  BOOL AlphaBlend(
8 K. L2 E- P3 f7 Z7 o5 v  ~3 ?( f, ?& i
   HDC hdcDest,
/ i: F% m0 c5 e8 w1 _) {
$ D! h  j' U8 f# T: o5 Z  // 目标设备环境句柄
& w8 I5 _2 n# F/ F; b- p  Z4 M) s
, Q+ B! T# F7 |" J( I3 }   int nXOriginDest,
  \# U7 V2 `$ J1 H2 j8 K
& S0 ^; t, C5 O! G8 T  // 目标坐标x
% s5 a: a! N. a
/ b5 L# ^& ?8 H" ~" a. Q   int nYOriginDest, 9 g) Q2 d7 D( [" s: ?5 w& S* j
# f8 ^0 c$ `: q' x& Q
  // 目标坐标y
$ R$ p7 s: q1 W) ~7 O. c% k: ~
% M1 k. x0 b6 U% u0 l4 L0 N3 W9 j5 N  k   int nWidthDest,
8 M/ e- i7 c( r! S/ h7 i  N. |' m3 r6 j/ H
  // 目标宽度) A) t& l/ @6 ?/ ~3 s. d0 C) ^
. z0 E+ W$ M) U
   int nHeightDest,
7 k" z0 S/ K5 j8 o$ y5 }
8 M: o2 h5 z4 L+ y4 y; h# j. h  //目标高度
  {8 \5 {( G" C
9 u# ]5 W- L: E! \3 Y7 f! e; J   HDC hdcSrc,
, H5 m! [/ E5 h5 F' P0 @- g. q
+ G4 O0 z) l  V# x1 `: V! E' l  //源设备环境句柄
0 ~. p: U2 {& G
1 C& H, Z8 `' i, m1 m# e7 G: L   int nXOriginSrc, * t3 e- r. u3 l7 j" l) |1 |  a; J
, E! r% x  L- ?! n4 o  @8 d- v
  // 源坐标x
) w5 m: Y9 ^9 Y
6 n" v8 N8 p: Y6 i* p* V" }! L" D   int nYOriginSrc,
% A1 X/ n0 J3 B' u
$ F0 ^5 }" a- b* x! G  // 源坐标y5 V8 k& s, U, }; X

4 e+ n( J% v0 J# ~6 e   int nWidthSrc,
: M; ?8 R, t! Q) @: J. I: P! T* s! M2 n
  //源宽度
6 [% H" v8 A* p6 h: e" }0 i: o4 f, ~4 I- t# F  g
   int nHeightSrc,
% w8 N7 n. p* e/ o& k
" P+ M+ `7 Y6 Z* h  ^% U: {  w7 D( u* j  //源高度
1 o6 T0 S( ]$ m. w6 v
3 |# R# _0 G( }2 W! a) _   BLENDFUNCTION blendFunction
% e# ^" K' n3 c" w+ K! T  c% R, f0 u) [0 y: l3 ^8 I) d
  // 合成方式具体数据结构; Q3 X9 Q  U& }0 k0 i/ h

7 ?4 |4 p, V2 A3 k  );
2 w6 r1 O5 n6 c# Q8 t9 W( e
5 |9 o# b8 V4 h; d  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:. C6 x: m2 F, q' Q% `* n

! F7 [- k# Z5 E1 D  typedef struct _BLENDFUNCTION {
9 y2 r/ Z+ X  v0 P- X$ o
6 q& Q7 a$ H; B) P* g   BYTE BlendOp;
" u. ?6 L' e3 z
+ H4 @( s1 y6 ]+ w* ~/ |, h   BYTE BlendFlags;//必须为零
+ d; H% _8 ?7 N# b4 o3 Y6 w
" X% h" E1 j# F: t) _8 O   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示( b: c2 v. N. ~* {. d- J& z: m% k
! u- v& G; T5 j& u; i7 M+ W
   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
- w* B1 a( A- r- G2 b$ j( w
* U. s( H) E. a- h  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;
( ?  q: Y. e5 i+ n
$ ^7 r: ?$ P+ m# c  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
* Z! `8 F. ^/ s0 x; o7 v* U. f* ?7 ~  Y
* I. P" Y$ ~8 ~9 j6 V4 G
  编程实现* V$ E, j+ m) j* S! X
1 H% @1 f. U& U9 O" g0 u/ r  [
% g; M( j8 u0 ~" i
  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
" c$ E- ?4 |$ Y" g$ j
$ J0 ~' p% L0 A; C+ {$ z! W  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:
. R2 O% x) n5 X3 C) A2 l) d& Q$ v
- J" t6 e7 N# Q! N7 u. O0 k  class CWipeImageDlg : public CDialog
& L2 Y! b; O( A. w5 Y! ^& [
; M0 n# v7 v- w8 v  }  {2 @5 A+ H1 l  M5 S* y2 H
3 P% U7 d. r8 N7 X0 T
  // Construction
# z3 e" K' w2 u1 |0 s( `1 p! W5 y, d+ F
  public:
* z: X  Z/ ?2 G7 O2 s6 l' \  {2 j2 b
   BLENDFUNCTION m_bf;2 {* I4 f' E1 J1 `2 y$ d
% K6 W1 }  C8 N  R7 X, D1 Y
   CBitmap cross,lantern;- {! v" J) h  @) ^. a- Z, `+ }. J: G

- O  {- a  ~, B   BITMAP bmp;9 R1 }) z. i" g' Y
5 q; r0 \+ S% i7 ?4 Q/ H6 W
   int bmpWidth,bmpHeight;) `) J# k0 {3 u# Q
: _1 d7 R" o. ~" W) d  D7 |
   CDC dcForCross,dcForLantern;
" |2 y( f0 i" ^! m% H
/ S0 Q  {- |4 P9 C7 Q2 W, j   CDC? dc;' z) B( Y0 [& m( @: _) P
( m7 Q. @/ n. ^7 O* u  E# p; s
   BOOL bShowLantern;7 H) {5 I- z* v& j, J

, w1 ]! l3 M8 z: j7 l5 ?- a  ………(系统自动生成部分)
$ A% Z3 z2 h: ~/ `! Y
6 z0 Z4 D6 Y; x, T  }; + n, r, e1 R6 `* _( U  X5 h& d3 J

0 L: @' {6 f3 @  g% F( Q3 o: }  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:" K. A5 n( {$ g5 M

, [3 O8 L5 f" [, Y; x" o  BOOL CWipeImageDlg::OnInitDialog()8 M/ `6 [4 Z) J- B% ^9 ]6 g* q1 i$ S
9 N6 n& W' G" i. X$ m
  {
$ ?* q; h! E2 M- m$ I+ d1 G  c/ D
  ………(系统自动生成部分)8 h- k8 g3 _5 ]3 d* _5 H6 G( p
1 D6 ~" q6 h0 q3 C. H' t
   // TODO: Add extra initialization here/ W' G- ~0 b# R5 {6 l: O
" L0 W$ A1 [& j; r* R0 c/ q
   //初始化全局成员变量
" V+ D' ^8 g5 h; V
  H3 c" P7 S" _0 E4 R: y- b1 ^   this->bShowLantern=TRUE;% r* X3 |! u: g1 W' W1 N* o
( `, C6 q' E; \
   m_bf.BlendOp = AC_SRC_OVER;
; O: m6 k8 t  R- A8 k8 a- Q1 p" f' `$ X
   m_bf.BlendFlags = 0;- F) t, l# U( H: ~$ R. }" D! d. e
7 H3 m5 f- i+ S0 i
   m_bf.SourceConstantAlpha =10;
9 V, q/ D* h7 p* B
  c' [8 [1 E3 ^/ z# B6 C   m_bf.AlphaFormat = 0;0 n& }0 m3 [" @' N  Q
. r& N0 f9 F6 }+ l: a- f/ P
   //为节约篇幅,以下代码中略去对操作不成功的处理代码; y: ^( y/ K6 z- p
* o5 J- |) c7 D! N: v0 L, d: z3 U
   if(!cross.LoadBitmap(IDB_CROSS))
& b2 k% I$ h' `3 l" O. X6 c6 H" e: f+ R/ v
   {
% T% ~  u- w$ x1 {: [% ^" ?. g# ^2 j- p9 l7 j
   AfxMessageBox("装载位图出错!");0 c) Q* U5 q6 Q4 Y* ?7 w( x3 d

/ R9 w8 Y( n1 n& B   return FALSE;' m/ F: K* P+ q  V
4 d- L. d4 ~) u( J
   }
& F/ N- ~# v# j2 Z  j4 [- D/ Y1 H2 l! q4 l! [
   cross.GetBitmap(&&bmp);
: q; A' M$ V' G( O* a! ]4 f$ B# R  m: p( @
   lantern.LoadBitmap(IDB_LANTERN);0 x7 z+ F% Z0 b
# V% \- I- t  u1 r* x3 ^
   cross.GetBitmap(&&bmp);& v3 p7 p" _3 ~) o2 e
- c+ \& k4 }" W- G5 L
   //获得位图的大小信息
: x  v5 X: g$ q) D! ^$ [( f7 l4 a! a' O5 r& D2 X& ?# c# {
   bmpWidth=bmp.bmWidth;
  i8 G" T- U7 @8 N- ^7 M) h* O$ ~/ q5 x0 K( y$ @6 Q
   bmpHeight=bmp.bmHeight;- y( W3 G9 b. |) b
" P) J+ g) S* D4 s3 R
  dc=this->GetDC();& F# \' l( r# K" O7 N& R/ M8 i

* B& v. W' L  H+ e   dcForCross.CreateCompatibleDC(dc);
2 e% A8 r$ q. o) L$ e
. O9 E, ~% j& }; }   dcForLantern.CreateCompatibleDC(dc);
1 L/ v( z, [# E0 ]0 @# o6 r
# W. J& S- _. x, ]4 [   //将位图装入设备环境句柄
; z! o) ^# X1 }  u/ f+ q2 }$ n  w) }3 [% ^- @
   dcForCross.SelectObject(&&cross);
+ ]- v8 N* ?- }
/ H  S6 S! V( {   dcForLantern.SelectObject(&&lantern);" O; W, d5 f. O1 x3 L( h; D

" _/ J  M8 E: f  W" T0 A$ v   //打开计时器7 r7 v+ J5 D+ p! N$ B: G

. k/ y5 T6 y& I. w+ k4 h   SetTimer(1000,50,NULL);
/ o' S) H- U( c) M2 U0 x2 t9 I( R. ^9 i, a8 j$ L; h
   return TRUE; 6 K% e8 q/ O& V$ U1 L7 o  q& m
, e3 K/ t  Y5 z0 \7 i3 o
  }2 y! v5 T" U) d+ B. e

$ n8 V. d/ o$ s. J8 U2 C9 I$ b6 I, e2 w3 I  void CWipeImageDlg::OnTimer(UINT nIDEvent)
. C$ W! G% v3 t/ u% {/ h/ K, j6 Q3 V8 ]0 v+ Q
  {) u4 A+ x3 G7 q6 a+ Z9 q0 s% l- I

4 w# @6 D2 x- Y6 E, R   //图片透明度每次递增5点
9 d. \. |, `7 u6 p
* X% |( v5 L! V1 {   m_bf.SourceConstantAlpha+=5;
; ^+ X: F" W& n8 i$ Z0 G8 X4 L# p8 o8 b) {6 m1 R) e. C+ ^  L, |
   //当第一幅图片完全可见之后,显示另一张图片: |$ _0 c6 r+ Z1 P1 C/ G
/ M2 v8 C" u. X2 e# x. X: M) V
   if(m_bf.SourceConstantAlpha>=200)
8 S# n2 P! p4 H
1 `# |% O, ~. x( L' i9 X   {- V: V9 k. }3 R$ ^5 D0 w3 b

. w( |6 h; Z% ], ?' M. o: D   m_bf.SourceConstantAlpha=10;: n+ u: H8 F" g; p1 d# s

3 B2 v3 V1 c3 P, ?) q5 d- h0 b   //将bShowLantern做为显示标志,确认应该显示哪一张图片 3 u8 k7 m" E. N9 \
: u" m8 C: r6 M$ H, g! H3 f3 x
   bShowLantern=!bShowLantern;
$ G* e6 J" J  z' U- K7 x8 Y6 b" p& W1 e) }" U" ?6 U
   }
- }+ H4 Y4 j: o  i- n) n3 _& m  d
: ?$ i( L: i  g+ ^+ a4 \3 o+ _   if(bShowLantern)- r$ Y, x' M+ V1 z7 e6 M* r4 e
; G1 ?, |, p1 Y: u4 k2 r
   {
7 K# N1 J! I. [( f1 F: W$ b! [: K0 y3 ~* H# i4 g3 W
   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);6 o; M* h3 ~* x

. ~, q- G) P) X, x  L8 F. {   }
* H  H- V5 @$ t* e4 R+ y, W7 D7 _( q0 e
   else
% M( S# B  l$ g/ `' d
1 u9 K! `: V, b* i9 s; `4 F$ K- x   {
$ v9 s6 N5 S2 [. n5 }
0 d' @! J6 [+ ]. p# [. d   //按透明度递增的方式显示“背景”图片$ m0 J- A8 `+ h( ], z
1 {+ K# R7 \/ F* D5 C, y4 H
   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);9 h( g& v1 w' e7 f1 W$ M
- J% n4 V: h* a, ~7 H
   }
1 p& s, F  U" \: a* ~3 q3 k8 g. l( m0 w
   CDialog::OnTimer(nIDEvent);# ]2 t+ P, c+ i* Z3 }0 t# H

! O! Q2 \- Z  R5 K; c: b# ?+ q  }
: S: M% o8 o. g% I
) X& j2 T8 c1 \/ \, l- G1 `4 V& r4 j6 Y; j# ~
  编译说明
; Q# q: x) Y$ L* r5 C
& N5 c" W; c9 A1 `! j/ W7 m
4 s2 V' J' U! D% |5 p7 O+ T  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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