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

图形擦除技术及编程应用

[复制链接]
发表于 2003-10-12 23:35:05 | 显示全部楼层 |阅读模式
周鸣扬 7 F8 j/ m) O3 u2 N8 m

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

4 f  {; L4 w1 V/ `& H( R" v# _) b
4 E0 y, I* |8 M. U. v  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:
; T9 ]  H8 l. _8 M
! s6 L4 z% n: J# s( P# P6 T  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red
& m) Q7 L1 x! N# t7 T, N+ I! Y# X5 y
  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
! ?6 N! H/ k) i
5 Z1 J5 b' |' k% [$ w% s: Z# q  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue
5 O; r: Y/ d) m: t, g& j# S7 k
1 U5 R7 F# L  S6 x( j" q! H  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。7 ]  [2 l! q3 p$ w2 O

2 }# N& \0 h- C( @: N/ b+ Y* X  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:& _) z$ E+ A( N3 c3 H5 R
+ V4 y1 t7 v5 E  G4 F! E
  BOOL AlphaBlend(
* M2 X9 Q! ]$ ]0 J' |+ Z% Z
# e3 x+ j4 t3 ^7 u   HDC hdcDest, ; z7 D( \8 o1 {
9 d  m3 G5 B- @2 d& U" w
  // 目标设备环境句柄
+ ^' g0 m1 u2 E2 ]9 m+ y2 @9 N* y4 B9 r& A0 w8 ~% X) C
   int nXOriginDest, ; p9 I: ~+ i- \
% ?7 w- o* u0 A7 o0 ?
  // 目标坐标x$ H$ d& P9 [* |4 Y1 ]

! ^  F/ C6 z; K) a& W   int nYOriginDest, * U: \8 a: X8 H( E9 i: d

2 l( H1 [. L+ X  // 目标坐标y
" }5 M: O4 _. b) G7 U  r, L5 }
) ?$ t$ V% C" I6 @   int nWidthDest,
4 B% |* o& Q# K2 V! Q# e
4 o" L- e* X7 V- d% [* ]# L5 _  // 目标宽度
% y- a1 M5 W: E
* o- G* b; P. C: l& ^   int nHeightDest,
0 ?" L& z5 `  d- m
) c3 g' |7 n1 T. O2 j/ x  //目标高度6 _; r$ Y8 }' v8 y1 L% C8 z- L
, F! s; C7 B4 S0 E- @& e: D: c* v3 X+ H
   HDC hdcSrc, / p/ w- @' N# z+ t( Z+ S% e1 f& b; C

! L- w2 Y/ Q5 B- L. ~6 h  //源设备环境句柄
5 S- r- Z0 {5 q/ \6 k4 z
/ Q' K! S2 E2 t, z1 e8 l   int nXOriginSrc, $ b1 }/ G" m) O  o$ R

! G! f' C" u# e' u; i9 l  // 源坐标x. L% u3 m4 ~& i( z! P6 O

0 T. P- j# [: O- r   int nYOriginSrc,
, H6 H: H8 Q$ ]0 O1 E; _  Q' @( W7 F4 l8 v1 K% Y/ e. G8 r
  // 源坐标y" `/ J# b) u& J6 p- r9 S1 a
1 K0 [: p% v, m- W4 d4 a
   int nWidthSrc, ; {# Z9 ?  [- q' F: ^& F% V
0 b: D! f! P6 Z+ w
  //源宽度7 k; [/ @2 r! W2 S  ~5 Q- m
+ ^5 k" H+ p$ b3 B& m3 E. c
   int nHeightSrc, 1 Y4 A& m: r$ h( [* x% v

/ J5 M/ F7 |& v$ q6 S  //源高度
' Y2 W/ z) ~5 O, L" _# S$ e& e+ S5 T
$ e, H) }5 a  S- W# S( P$ S   BLENDFUNCTION blendFunction 8 @% \; D- n+ e0 X- ]
& {) b3 |8 F; O" P- i: a5 q
  // 合成方式具体数据结构0 Q- k, o- R  t" ]- R
; D1 ^& D4 p; T! d) O) k
  );
  ^7 A+ k2 y+ f4 {: D- N% D) h9 i) U4 u. |' F
  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:. }* c4 \  Q6 M$ V2 O/ k
* K6 k7 S7 a4 ^
  typedef struct _BLENDFUNCTION {
# U: T; [6 w+ w1 r5 z( Q1 D# `8 _# g' @/ K& p6 Q
   BYTE BlendOp;
4 `9 A! K& _* e: h( X; r3 |* f/ j' h7 A. ?1 ^& s; @: ~8 G0 s" }
   BYTE BlendFlags;//必须为零3 y/ f% y! D- `1 q3 e
, d1 c) z9 l6 i2 v' ?5 }) f
   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示
  z+ @$ n# a  h) S0 s4 ]% Q
2 ]5 G3 G8 l5 f   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
2 A: W: A8 y6 C8 y3 F' b2 w: p+ l9 s
  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;! ?4 ]1 a2 v/ C; c5 A1 v9 M& j

( _. Q$ ~- B8 J5 d) [/ J8 \& v  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
5 |* Z; [; [3 I2 H* }! ]5 L% l* I: W3 E+ D3 b9 [+ K( `9 ~

' t6 U. [# V) _! B+ k- W  编程实现
2 h; B- D$ ]7 {& Z; _  W$ u6 ]9 J  b6 v7 @

: }1 v( N3 E, l/ B. z  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。
! j0 p) U/ z; _) Y, x9 q" K3 d8 o
  U) v. l7 @( m7 i" e  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:
5 O! I( C- i) ~1 U; u2 U: D" q% e7 k; j
  class CWipeImageDlg : public CDialog
4 n  s4 x! y* c; ~& c3 A: J; w/ Q# H
  {
: f$ T+ v" d& e4 f$ U+ p0 z. _$ w; s, ?4 |: F5 t
  // Construction
& p4 Q. c2 [4 {6 \9 V" T" P, s/ E$ c  n: T
  public:
& g: Q) O/ o' D0 z* w. `4 b  S/ l; ?( S6 i0 K5 x
   BLENDFUNCTION m_bf;
  `8 {: z0 s! r: p( p, c9 N# m; w4 h% b
   CBitmap cross,lantern;. S9 |# ~. p$ w( c

6 |, j( x/ S/ _% |3 C   BITMAP bmp;
# s; `8 M, t$ D. _! ]% [1 G) w" |. n1 t0 h  t: I
   int bmpWidth,bmpHeight;
% @" ]5 ^8 r8 w8 x% J1 i* Z" ]4 w8 |' {4 c9 n
   CDC dcForCross,dcForLantern;8 T- Y5 P* V$ k5 W6 _6 a

" u" s- r  p- G/ l+ n0 W9 M4 \9 r( v   CDC? dc;6 Z: p1 p% d) E! M6 g, B
1 m) A7 s, u  N) {
   BOOL bShowLantern;
) F. j$ A! L5 I7 ?/ V; ?1 N4 h* S7 T! O4 q" v
  ………(系统自动生成部分)& C, J2 K9 B1 n2 ]6 t
2 L1 E/ s5 w/ M! O, e
  }; 9 f3 i9 ^  g. z" E0 N4 {3 ~
3 [$ f' k/ y' o% ^' @1 y3 ]% H
  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
; N, {) }0 j* c5 D" I4 U+ }& O9 O% F7 i& i( W# Q  b
  BOOL CWipeImageDlg::OnInitDialog()
6 x# B% k( J4 C! N% M7 M
4 c. x! i; h8 _4 h% u# K  {
# S  {' E' N) F0 O6 E" `; C: P2 O. J& N
  ………(系统自动生成部分)9 \! e" H4 V% h
+ \# l  Z  S- i; |
   // TODO: Add extra initialization here
2 f7 v$ H4 x( ]5 H
3 T. Y. ~# j" j* |) M/ j" p   //初始化全局成员变量
5 @5 d& ~0 v5 y. ^4 X, B+ p& g2 ?. `/ v0 Y' h
   this->bShowLantern=TRUE;
5 r* ]  x# P$ m7 Y( ~6 l9 }' |: w0 q$ f8 W, P
   m_bf.BlendOp = AC_SRC_OVER;
( I) S% T! A1 n3 }& J
. z0 D, ~( L7 D) B$ R: F0 d   m_bf.BlendFlags = 0;6 v" C: I1 r, Q  j. \2 }
* s$ A( L+ A$ A2 s
   m_bf.SourceConstantAlpha =10;
3 {/ j/ a! u" f  r1 y0 _7 G( \  L/ V" e) V* S/ ^8 p: I+ ~) a
   m_bf.AlphaFormat = 0;
& y6 @  ?6 v! f2 u7 B/ U0 \( E& ?: J2 R- E
   //为节约篇幅,以下代码中略去对操作不成功的处理代码. j0 B$ b5 F, p

3 n3 P, r/ G4 I: I   if(!cross.LoadBitmap(IDB_CROSS))
9 h( Y% d2 `$ p! j' b3 _! f/ K1 [" c+ t- K6 C- a/ a
   {0 M  d- S1 A1 y6 l$ U

$ y5 ^! \7 G7 Q' k+ y   AfxMessageBox("装载位图出错!");
# \# @# D5 I- B- T6 ?" a6 v
! S& Q8 R- \# Y& d, ^   return FALSE;) C9 G8 X# K' _. T  q- R
3 j; @0 I: K4 p/ L' W1 _$ Y1 {  T
   }, n% E, \, P' l1 O) }! V9 M
5 B% P  Y; X% O% i$ `
   cross.GetBitmap(&&bmp);! j( i. I& T" t

, x5 e" a& H# _5 J  x0 o$ o9 D$ R   lantern.LoadBitmap(IDB_LANTERN);
( _7 A5 U! g$ H: [  O! E: H: ~' \- n5 j3 i3 w
   cross.GetBitmap(&&bmp);: }6 Y6 m, j3 K' @7 ~

" q) P; i7 r0 O3 F   //获得位图的大小信息! u6 v$ Q! j3 C  {( P9 |. A
6 ]0 R  h. H+ s
   bmpWidth=bmp.bmWidth;4 ^4 S' I1 S$ ^$ h# m

8 i, x$ j2 U/ P   bmpHeight=bmp.bmHeight;3 O, X0 O) l, m7 H" Y+ H; Q* B/ L
9 J5 Q5 p/ O  J
  dc=this->GetDC();# J# o0 C" f5 P7 Y

! y( r: C2 F- u$ ^   dcForCross.CreateCompatibleDC(dc);
/ A# o# }+ s/ g* n7 d8 d. K# y; A. C1 ]8 R( |
   dcForLantern.CreateCompatibleDC(dc);" [; f& B& P4 k3 ?) s' ]

4 j" U+ J, {+ X( ]5 L8 F! I9 x   //将位图装入设备环境句柄
2 f" N+ H0 w6 g7 K; a" a+ A% r; S- e& M' ~$ ^- y2 B
   dcForCross.SelectObject(&&cross);
% z6 X' z$ m- m, {6 q6 ]8 n: ?+ e
5 W/ j4 i9 {; T% Z4 o- l+ M( W/ D   dcForLantern.SelectObject(&&lantern);, c8 m& e$ f- B5 N# y$ M1 h

" I2 _, s% s/ _! |1 v- D% q3 o   //打开计时器
: V+ q0 l& O) G1 O- ?9 a& X% S! u5 g
   SetTimer(1000,50,NULL);
. V/ E# a1 X/ @1 P4 S0 Z3 f
' U9 g" V! _6 M3 Z) G   return TRUE; 6 j) q% e3 l/ ?4 ], D# a' t
0 G& B- c2 F, v  ]
  }
/ z! R& p# z# r) q  E8 ?8 A7 o$ x
$ ], V( i3 ?7 H/ }0 E  void CWipeImageDlg::OnTimer(UINT nIDEvent)3 {* \" N1 C% D" L5 s5 C4 v6 W/ U( \

0 l. }0 U5 V) P; H; _  {: `, P4 K) M4 X8 U

& r8 w7 s" z& V5 _# g3 D& ~4 z6 h7 \   //图片透明度每次递增5点% @2 t# [, r. }/ V; K  O

. P) z" z/ F# T, U1 T   m_bf.SourceConstantAlpha+=5;
) @" S% y( s+ x$ C: P9 j/ O+ o+ q
7 `( ]. g8 L0 ^2 |# _: }/ ~   //当第一幅图片完全可见之后,显示另一张图片
9 t0 P3 y; F6 o9 y6 }$ p# b9 W& y) h! V2 f) ?9 h
   if(m_bf.SourceConstantAlpha>=200)
1 f4 W# w; O+ u& A% k2 d: o* P
3 }" l9 @( e. @( X  A  X8 e4 r1 o   {
' K* Q( e" f9 l
- P/ O* d8 b. d- q! n   m_bf.SourceConstantAlpha=10;% R* K3 j2 u" D
4 s: E* ]+ ]2 Z- U$ Z
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 . w7 M; g* B6 x1 e
  L: {1 F* a6 O, }9 v
   bShowLantern=!bShowLantern;
6 {) ^: s+ j, e* t0 r$ y, r3 W4 i& e- S7 {$ M0 [6 l( w/ t
   }" x4 D2 e3 I7 @) c) j
/ v  X) d- F  w3 u
   if(bShowLantern)
& @4 ~3 }6 ^! ]" G, g3 T( [! x/ {
6 b9 P- z  y% `. U7 B   {
7 @* W7 P( p* x5 z( s! w% o
* H9 e9 ]/ I4 _2 D: O- s# }6 x   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);" h1 N# y/ i) a

! i( Q) g9 [+ g# J$ d: c6 C: v   }
$ s6 m0 @. O0 [+ Q; K9 A7 t6 C6 P" L  h# Z- l5 p7 t5 o  }
   else
* X" U, e9 Y4 k+ Z
3 q; V0 w0 L  K! S' a   {5 ^" I  G4 o8 [0 @; J' t# h. t
9 H# }& F6 L  u. K) E& B
   //按透明度递增的方式显示“背景”图片
0 {% C% u) `) N) W
; u  I$ a( l* @& m   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);8 W& _5 u4 u$ N8 h2 g! ?+ b
% C1 S7 O; k- Q/ x# i6 m! S
   }0 a& [4 [9 a5 g$ \9 v) t
* T2 P! g3 \) Z4 E- I9 z
   CDialog::OnTimer(nIDEvent);9 Q4 q  z; H) r: |
6 L# z: x3 `- n
  }6 j/ ^0 q: A2 o  [  b

, e5 L$ x+ C$ |4 q9 h7 D& D: D
; C: I& |# w5 U" d8 Z  编译说明: R4 S& E$ O$ i* [3 }: \

& l- _0 X1 l% u  ?- j" n$ n2 {4 `
7 ^8 W5 V6 _9 U0 F5 T/ S) `  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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