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

图形擦除技术及编程应用

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

' H# i  M: f, y
! @: v8 P  R) p7 M& M, F" ]6 ]  解决方案
; d0 P* V7 R4 d
3 S8 B) s( d- s6 q/ H# P, O( T& K6 H( S- H
  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:) J7 M8 h0 M( Q4 t
6 T0 l" @% j% I3 V5 k' D  {& I
  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red ) d7 y+ W" P4 X% {# L$ D

' L! E9 @% o! J) i, _& A  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green ; Y. k+ w* Z' _8 ^0 k4 H9 Q) q

' i, g& G  Y3 `5 Z9 y: `8 G  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue3 D! ~. q4 d$ C* H  Y

& x5 M/ d  [& M" v" K" [  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。
3 E& V# H7 b) P, Z9 p# w0 j& P' _6 ]4 g
  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
8 l" ~2 [. ^! d# |7 d# v+ }
1 T4 u1 |+ S: E+ p" z5 B  BOOL AlphaBlend(
; [/ P1 J6 T% [) ?0 G2 m' ^. r
! j0 u4 u) }$ ~2 o4 k: j   HDC hdcDest, * `; X8 s+ k4 \; Y

* e  K) z  K/ O3 x) w. g  // 目标设备环境句柄  n) J, d& l) p; e# A: v
5 ~: f1 q6 d6 n4 D( y
   int nXOriginDest,
3 @) c8 Z& p: k' p9 q" l
, ^* B" F" u: e, z  // 目标坐标x. C% [& i# o9 ]+ H

# D+ u; A) D+ h4 v2 F0 ^: I7 p, z   int nYOriginDest, % }% ]  Z* J' y! l4 N

% N" }8 n$ i7 E# M5 \; Z  // 目标坐标y6 w& r  t3 o! b  x& [; b

" {' w4 }9 d, t- _( f1 I8 Y   int nWidthDest,
; P3 D4 b! ~* G: J/ ^
/ E2 F# U0 i5 q4 O  // 目标宽度/ w) K9 L9 t' T4 w9 |- r
6 l/ r* n, N& w. A( ?% @) v
   int nHeightDest,
# n5 |& N- K: W2 ~4 |4 d
+ k# w' Y' F: w# ^9 f  w" A  //目标高度9 p/ F9 X: B6 [- e

6 U! K2 O# H; N5 C8 l7 W$ c   HDC hdcSrc, / K+ ^5 E; l3 v

, M, D/ H- f0 K  //源设备环境句柄. g- q4 K$ {3 ?2 E

3 K# |3 Q  N* |6 D# J   int nXOriginSrc,
1 c: H2 G4 K3 T) |. w
! C) J( T: Q, ^" O7 }  // 源坐标x
' w: E- g- z/ I: U/ D9 X( m  q5 C. Z  y3 E# b/ k
   int nYOriginSrc, & D( x/ P9 h( V# ?" i; t/ U0 |
. O! C8 c/ D0 p: R2 i
  // 源坐标y
* {/ a0 }' V" ?" `5 x
0 Q1 }% D# _. L) G& i" U8 S   int nWidthSrc,
* I* @8 a2 s( y$ L2 S$ F) o, L) G4 f6 j9 p" }" W
  //源宽度8 d7 b; t. q5 P; }& s" e. m
  h3 o+ F" L) |: p! x: p, ?
   int nHeightSrc, + [- O9 u* Y" ^* \

! P( N) ]- u6 O( x" w4 Y  //源高度
% _- |0 `, N$ q# W: u0 n5 q; Y3 t0 S2 H) A8 s8 e4 O
   BLENDFUNCTION blendFunction ' r$ m; @$ p' \  O& w
6 R( V9 l- [/ |, D5 l* w. Z1 W
  // 合成方式具体数据结构  _' T. \3 @2 |
* g3 t  A$ M) O
  );
9 {/ d. o3 ?% X( r& c$ C
9 k# o% ^+ \! z, Q  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:
6 A0 q7 R4 t8 e. J
1 X8 g! d7 p- n0 u$ x4 h+ m4 b  typedef struct _BLENDFUNCTION {& W/ M& }2 h- {. T6 a2 {
) m" u; T- F! H1 T% k
   BYTE BlendOp;3 H* u# o* V- ]& X8 m; |

) Y* c# m- ]3 V5 p- }& c   BYTE BlendFlags;//必须为零
/ M  q" I5 h. m& F
8 T; A  V+ X! m5 A% V/ Q   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
0 Y: T: ?5 [7 r2 J: D7 h6 U( O, w2 R, J1 u8 T6 ^
   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
& G3 d- S1 P6 P% V$ L9 z" L
9 r9 P+ S7 y! u7 \4 |  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;, b8 x) Z- ~/ E. G& H8 ?5 e
, ]4 O$ a8 r# r: L4 K( X$ W
  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
- x+ q% e& ?: Q* V- O3 p: [/ F0 \

3 X7 F5 E+ |/ Q- K! _  U  编程实现5 G2 L- h" i: P% n. P

7 e+ a! b  l: C+ [* s3 \  U2 b% v" t1 `' M6 s
  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。# }5 w2 v! I2 s% Q' ~

0 Y: a9 i- z: J4 m- V  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:
. ?0 T* @+ Y' W! n0 b5 N, M; R7 H2 y( G# F. ~2 A- C
  class CWipeImageDlg : public CDialog
7 v; p. w3 k8 e. u; c( o! o; S
% A, u* p" H- G' @7 S& E( d  {! g& h4 [$ w- d* K) l

; x$ k+ V7 O4 m4 {+ v  // Construction5 |' p, }- \. `5 h2 C/ q! x
) f4 \9 S) [% c! U* L
  public:, K6 T: s4 U+ u
" l% h, K4 f% ^0 g/ E/ ~
   BLENDFUNCTION m_bf;
4 \9 \7 N9 ?/ t0 e0 ]! c/ w  n) H- Y& R6 ?: f1 D2 I' }
   CBitmap cross,lantern;
4 D; w  Q. f' F+ n9 {4 o8 o8 D
, I* h6 P* k' q   BITMAP bmp;
* r) G+ W8 w5 s) i$ q; n1 d1 C# Y$ \( [# O$ v# s, ^( K
   int bmpWidth,bmpHeight;- t0 F! i/ p3 G7 O+ ~5 }5 A; c
8 _2 O8 n6 Q, y+ J$ E7 k
   CDC dcForCross,dcForLantern;; b9 z2 X1 B1 p; }5 Q8 {+ F. [

: C' v4 H/ Y& n' r, H   CDC? dc;
3 b5 B. w+ V/ U
' M/ U( p& B; c/ `& l: ^& K   BOOL bShowLantern;
' d% K. D' P% E4 s$ g6 _! C1 B, C$ \; }4 J) t$ y; q8 j
  ………(系统自动生成部分)
6 L0 l3 y' H8 P
" |, w% f, A1 [7 B% i9 r  }; ! S4 O1 n# m3 Z1 d
, f! v% `0 `; i  p" g' j. g
  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:- J- F! ?' i8 R( C: C
1 i6 p7 w) x4 b4 s/ ?. R: x- P
  BOOL CWipeImageDlg::OnInitDialog()) {( N2 J. \% R& R

2 W9 e# R: t) N  {4 @: M. f) ^1 v5 P$ l: m

% j4 `5 D  b: b% k  ………(系统自动生成部分). m) @" g! n3 B' y

' F% }9 x9 C0 g* {7 R   // TODO: Add extra initialization here& h8 _) C0 i6 b# Q) g) r& K" C

4 ^5 S7 P+ Y( a! r   //初始化全局成员变量0 y8 b$ Q& f' Y5 r5 R
0 r1 D1 z) B' T
   this->bShowLantern=TRUE;
  H: }* E# h2 h. ~+ z% N
) O! j: Q6 B5 v4 ~! E   m_bf.BlendOp = AC_SRC_OVER;7 a/ J8 T/ x3 D) `' _7 |

$ |' _; G- {9 v6 r* w/ r   m_bf.BlendFlags = 0;
( Z/ L$ G0 G, z! |
4 |1 w1 S1 H8 _3 S   m_bf.SourceConstantAlpha =10;, S7 K; T9 |3 E

: V8 v4 h. @6 w- X   m_bf.AlphaFormat = 0;
$ h. O" s* Z3 u0 y& U  p$ m- r8 J" `/ }" V9 `
   //为节约篇幅,以下代码中略去对操作不成功的处理代码# F9 z- G' L: z+ q+ @( C; L

4 ]3 c0 ~: g. ]7 H, o   if(!cross.LoadBitmap(IDB_CROSS))
  d$ e& k: B; A! j- v  s  C! O) E9 G* ?" i' r
   {# V4 q3 b( K/ @& T' Y/ |

, b" ~; x7 \( F' y4 V! B   AfxMessageBox("装载位图出错!");" L" D! w2 b+ n# w4 S, U! q# a- A
, l# w/ G' |5 z) K
   return FALSE;1 b) ~! t' M1 n$ p# T; x" r+ B
/ M7 J2 t7 B% \& L
   }+ M6 X1 o$ H( m. V6 h
% H: _& F" e9 W& H
   cross.GetBitmap(&&bmp);
; j! M: ?) M  R5 {
8 Z. Z* p5 j2 q2 x   lantern.LoadBitmap(IDB_LANTERN);
- r$ ~- a* ^1 r
5 Y2 K1 Y( l+ Y/ Q7 r) r+ P/ [   cross.GetBitmap(&&bmp);
! d" j- B3 D3 x* z' I0 ^* B/ D% E" _; ?; [. L. E0 t0 l* x$ g# x6 s
   //获得位图的大小信息
1 b# t$ `7 l! Y) J7 |; O
4 ^7 i& a/ S6 H; e! m6 d9 |. |   bmpWidth=bmp.bmWidth;, D, m& H: |0 |+ O% C

. [0 _0 L+ R& h" e1 q   bmpHeight=bmp.bmHeight;
* {% B) E5 O. ~8 o% c7 O( O
' n; w0 Q' D# z' V" r% G  dc=this->GetDC();
% @( X+ S& ^7 m/ F% J9 p/ y9 n: H/ B8 _8 u+ Q! Q# f+ L; d
   dcForCross.CreateCompatibleDC(dc);
0 J+ m8 }; w- Q. f1 e; e
4 a8 [! Z8 s0 M% j# t) i   dcForLantern.CreateCompatibleDC(dc);
9 y8 I1 r8 C9 j0 S% U% ?* i8 M4 r( X* i3 j2 z$ e4 O
   //将位图装入设备环境句柄
8 p. x7 l  u5 ^
" ^; f  j6 I2 g- O# S9 n   dcForCross.SelectObject(&&cross);
& I& ~( ]; R8 b1 ~& A) v5 `) q0 u% H# A8 L( b7 ?& A
   dcForLantern.SelectObject(&&lantern);
  W2 U, B# x) K. j6 v6 F9 \: ]9 o- r) n+ x( J' ~( P8 P9 h; R
   //打开计时器
3 M; a* v+ F& b$ l
7 O6 w6 i$ ^) D' \# q! `   SetTimer(1000,50,NULL);
+ m1 j4 D, G9 \, V- T2 n1 n' W; f9 n* r& V
   return TRUE; 3 S& n+ R% [% N7 X
/ |) v; J. z* T! l
  }
, W3 ]% I  E- A8 w1 Q  G+ V! Q5 l6 m, ^8 [# Y' b  t4 q
  void CWipeImageDlg::OnTimer(UINT nIDEvent)* ~/ L& x4 J* o) d6 ?( `* ?/ t+ z
$ K- e3 h1 ?* W- Y/ }! F2 e
  {& T( q; v% T1 q1 v5 ?
  B/ K3 E- O8 v; x
   //图片透明度每次递增5点! q  {. V$ H" N3 G- V

; L' [1 W/ Y6 D3 P   m_bf.SourceConstantAlpha+=5;# I' U& S, R9 R2 A( S5 w4 `8 O

) |  Y+ b$ _+ J  D% v; ?   //当第一幅图片完全可见之后,显示另一张图片
# s+ ~# \: ]' \* ^  a' G) l+ m. k2 z3 s$ S7 f
   if(m_bf.SourceConstantAlpha>=200)
- n; ^( Y3 i/ A# L) k
5 Q) E1 A0 O5 b$ m0 E7 ~   {
" x1 l1 s9 g! R5 ]+ Y5 I( s+ R* f: S3 i  C2 ~* ]3 p3 {% w, H
   m_bf.SourceConstantAlpha=10;/ c- _. L) ]* n" U/ f" H  C
1 f. k6 g' A* R2 A" T: C! k7 E
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 
3 \# i4 f/ B7 u, \  Z2 q! k; @& S  ?+ h5 o
   bShowLantern=!bShowLantern;
9 a. _6 O0 [3 X/ }% O, D% _
/ f0 E) l9 j3 u" l   }
0 }" n% g  ?0 g! ]8 I9 L" q: O, }+ c' `
   if(bShowLantern)
  u! Z2 t8 v$ d6 C( N6 ^9 Y+ Q- ~' ^/ `1 _6 a
   {2 P# {- T8 l9 q
$ n# z) x3 r) O; y) O$ T) @+ }5 a" Y
   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);4 ], m$ F) g/ Z! z
0 `, Z" S" \6 j. p+ W; z
   }! h3 I; Z4 z5 ^: u+ e. Y
- ]# L+ u9 W: }  p4 [
   else, s5 O5 M/ h6 G8 L5 R" _
# R" r* I  s9 ?; s$ ?* x
   {
- L& O, e9 k. w
# r# ~% ~& K; X+ X, G   //按透明度递增的方式显示“背景”图片
. j/ ^5 d0 H! X% F0 Z$ Q8 Q$ x* F. [% B, q7 w: U5 P; Q2 Q% ?
   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);. e7 r1 r, k8 i, f
% B8 ^/ Y& s$ ~1 v9 k0 b) }* a) I6 q+ p8 {
   }
/ ?, I" m7 I6 i" ?  @7 |, h1 h, [9 J
   CDialog::OnTimer(nIDEvent);. ]9 V5 t9 ^& K3 S. s9 v3 R
" o8 j( E7 W3 Z) o
  }
0 f9 ?9 a2 c+ R1 L3 H* g" g  h! l, m  Y1 G2 y0 d8 K) D

7 X/ R) y1 F5 w' \* X, G, ]; l  编译说明
. s% \3 |: Y% ^7 {3 u" ^% C& V6 d3 z" k! f0 I& O% Z% a

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

本版积分规则

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

GMT+8, 2026-6-18 08:08 , Processed in 0.017714 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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