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

图形擦除技术及编程应用

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

! M+ I. M+ \. l4 A. \9 n3 f, }1 e
4 Q4 T6 y1 X8 @6 R3 h  解决方案
* r* x% t2 ]6 T# X3 h* w" ~/ Q% T. ]1 |5 U8 n7 r/ `4 l$ C

# {7 l- z" f; Z/ P1 z$ J  不同于Windows 95中的GUI(图形用户接口),在Windows 98以后的版本中,GUI增加了对Alpha Blending(通道混合)的支持,Alpha Blending在概念上最为明显的就是对“通道”的应用。熟悉图形处理的朋友对“通道”这个概念并不会感到陌生, Alpha通道是用来表示数字图像的透明度,改变各种通道的特性,就相当于改变各种基本颜色的浓度。通常情况下,Alpha通道使用8位(Byte)二进制数,可以表示256级灰度,即256级的透明度。假设我们想要在目标区(Dst)里显示一个像素(Src:Alpha通道值为Src.Alpha),并且要求系统进行“通道合成”运算,那么,进行合成运算的具体公式为:- _% M$ _  y# ?1 [; f

! F+ Q1 l/ U3 b  Dst.Red   = Src.Red+ (1 - Src.Alpha) ? Dst.Red , q! z" [/ M+ l8 ?* H9 n
! y) b9 l5 \$ J
  Dst.Green = Src.Green+ (1 - Src.Alpha) ? Dst.Green
  p; t4 w5 o9 z, G  j  K! n6 n" O1 |+ n
  Dst.Blue = Src.Blue + (1 - Src.Alpha) ? Dst.Blue
8 O" x* }4 v( \1 ]& ?0 z3 W# W; R, l; ]4 w# ~
  从上面的公式可以看出,在进行合成运算之后,更新显示后的目标区域颜色值(RGB)并不完全是源位图的RGB值的拷贝,而是源位图和目标区域进行了“合成”之后的RGB值。和BitBlt函数的像素运算不同,Alpha Blending强调的是源位图的透明度,正是利用这样一种合成运算,我们能够达到图像“透明”的效果。* V3 l- ^# E9 F% i2 Q: i1 k
$ U. i1 _$ c1 C7 F; D, q' J% Z
  在VC中,系统提供了AlphaBlend 函数来实现位图的通道合成运算,AlphaBlend 函数主要用来显示透明或半透明的位图,其调用格式如下:
- _/ }* c7 b+ Q- e* Q3 P+ ~: V9 z6 A& B5 _' h8 h
  BOOL AlphaBlend(
7 Y9 d$ Q4 P3 m) x) [
$ s2 J, b; s" i" M* _# h! w   HDC hdcDest,
2 e: ?1 l- F! g
. y1 B' y5 T5 I! B2 \4 f' x  // 目标设备环境句柄
; D) ^; d0 ^+ b! k! Z
) L3 t  B1 f3 G. y( V$ I   int nXOriginDest,
* n  c$ O- F' X
+ T7 \# V; j9 S7 `  // 目标坐标x. W0 |  s% H* W
& E5 D; A6 \! c! w
   int nYOriginDest, ) w! p: s, l" J9 H& x

2 b9 `: h' R, G( D8 o+ z  // 目标坐标y
8 k$ s; q. f* F; u! S8 y# l8 A& X
   int nWidthDest,
2 N3 H6 ~3 E8 T9 U8 y# C4 Z1 V3 j5 C1 ]2 n7 Q. Q* p$ k+ j( a
  // 目标宽度; C3 L6 i8 s+ l7 a

3 ]- {9 L& E: t/ H8 B+ x6 E7 k+ }. T   int nHeightDest, 4 d: x) @" Z) P: l
. j% [+ ?: T: Q
  //目标高度
+ j6 i0 ^( x( }5 a8 P8 j5 l, d0 S; i
  |9 b& Y' c! y   HDC hdcSrc,
; v( E/ s$ F( u7 L; k+ d
4 D' f) Y) W8 z" [% Y1 i& z0 A  //源设备环境句柄
  I, s! ]1 v3 [/ j( ^2 k( F- i; c; M
! S; q4 l1 e& M8 o, O$ L   int nXOriginSrc,
+ Z: K$ B- S6 K9 K9 h9 u+ d
$ l4 b4 j& W+ B3 H2 O  // 源坐标x
# Z; }  P% g  c+ _5 J, M& B$ g$ l. ^& c" ~. M
   int nYOriginSrc, 7 Z. w6 L& l7 Z' S6 B4 X* |

: ]$ o% ?+ p+ s: J5 q  // 源坐标y0 e+ q+ g* U- Y' ~
+ ~4 F* [8 p! A
   int nWidthSrc,
7 U$ z! z8 G* z2 ?* ]
7 Z& D2 }. I3 w0 h( u  //源宽度
8 B* m: I$ v: P0 s' z$ B) B; f% q& o  u' q
   int nHeightSrc, ! S, {: y+ t* |  `& S& E
2 S% U. R' S2 F  L+ [- o9 M
  //源高度
0 D, a! K8 E: s' D3 a9 L) ]4 a, I. s6 T5 G) N
   BLENDFUNCTION blendFunction $ W3 e( V# ]4 G$ |7 ~5 _
8 U8 ^  p* k8 C0 J/ m
  // 合成方式具体数据结构0 Y9 x+ N4 l( w5 f# ^7 ?$ v3 ]
! u  d! ~3 `4 D3 w! M
  );$ X& ~3 d% v& {5 p7 F
! Y3 H% Z  P* E. I
  BLENDFUNCTION定义了在源位图和目标位图之间进行合成的具体方式,其具体数据成员及含义如下:: `0 b+ D8 }, B0 G! \
; }) X' ?4 V$ w4 u1 D. S0 G
  typedef struct _BLENDFUNCTION {+ E# ~2 D3 G" x2 T2 F3 q; p
8 k- Z, d7 s# B0 n& v/ i7 G
   BYTE BlendOp;! o# }3 f& E2 r, M3 _& S$ }

5 U: a. r  [5 u' x! e2 \& Z   BYTE BlendFlags;//必须为零4 S$ H: ?) x# R* @5 d( T2 u

( }+ |* v, R$ R6 p; ^/ e* j0 @   BYTE SourceConstantAlpha;//位图使用的透明度,0为完全透明、255为正常方式显示9 I2 Z. s7 z7 I! I4 v2 m

3 ]1 a) W, ]1 @- Y  d( j   BYTE AlphaFormat;//通常为零,如果源位图为32位真彩色,此值可取为AC_SRC_ALPHA
7 V/ u! [! [+ T% K( G$ E0 g0 b- A. U5 `+ r
  }BLENDFUNCTION, ?PBLENDFUNCTION, ?LPBLENDFUNCTION;$ n: m" s9 K% o
8 L+ |: m; w' U
  由上面的函数说明我们知道,AlphaBlend能够以特定的透明度来显示一幅位图,那么,如果让AlphaBlend以不同的通道值(从0到255)不断地交替显示两幅图片,这样就实现了“擦除”效果。
% d* Y$ f* Z8 s1 t  _- o$ n; @. ?7 `' R3 y! P; D7 c2 H+ p

/ y* P) w+ ~' U) z1 h5 G  N  编程实现
% N6 m% p/ A' l, t
- {# ^' m6 ~* L/ j  r$ C7 A
  w7 d) z( ~6 }7 A  了解了上述原理,编程中的具体运用就不会再是难事了,下面以在VC中为例,说明这种图形处理技术在编程中的具体实现。1 r/ Q6 Y$ y* H# J. E5 [8 ]7 H* U

( e8 a  x, Q9 B) x1 p  首先在VC中新建一基于对话框的项目WipeImage。准备好两幅等大的图片(IDB_CROSS、IDB_LANTERN),并将图片引入资源管理器。在CWipeImageDlg类中加入以下的全局成员变量声明:
9 k/ C/ ?9 [; l& N$ {- A6 R& Q
/ _8 i, Q" H4 y" w6 b  class CWipeImageDlg : public CDialog2 Q/ c5 d! B' ]- ]7 O* g

( W$ r" b" S, |) D  {; d6 n, P& ?( I7 @# v

$ I: o$ G% H. V/ d2 @  // Construction" W' ~' j8 _9 ?/ ]) f9 |
1 z) [" |! p3 L2 u4 z: v9 i
  public:
. y+ N. ?$ ~' t. y  ~( p& K" e; ?* n! E8 S5 o! F
   BLENDFUNCTION m_bf;. c% X# V4 f7 U/ K' b2 r# V! p4 ]
. E: B$ {! z( {$ a; z) j
   CBitmap cross,lantern;
1 Q* q. ~* L2 }8 B1 U$ W+ s& _9 @7 g& b" u) |: T/ p
   BITMAP bmp;
4 Q+ `' G6 Z2 W# q  d
) X* C  J/ L, p, J  \( Q   int bmpWidth,bmpHeight;
& V9 L: A) N2 b5 P3 c! l9 A9 F6 X8 I" z& \
   CDC dcForCross,dcForLantern;) \  ~: @! r& I3 {( O- i

- m8 \$ y) f3 r- d% z' L   CDC? dc;4 N! v0 G4 Y7 k/ t

. \/ |$ S5 N  c& V& n   BOOL bShowLantern;4 ~+ j1 S. {3 l) r8 e, f  V4 P
) p& W5 L1 {0 n! B
  ………(系统自动生成部分)/ w( u4 m0 S" Y8 m4 {# M
, T" @3 m& y3 [. D
  }; 
' k) [/ a+ p7 n: w( V4 W4 d& U6 ^
& K# g9 L: _1 o4 O' Y  接着在类向导中加入对WM_INITDIALOG和WM_TIMER消息的响应,其响应代码分别如下:
9 c# T+ I# q' Q( Q9 J# s
$ I9 h* s1 `' P+ u  BOOL CWipeImageDlg::OnInitDialog()
9 s6 w( [$ U, B+ C  W, a: a/ V+ R) n0 T; ?
  {
) H  Y: _0 i! z* U6 e3 k
$ w( A- R' r" Z0 F9 I8 p  ………(系统自动生成部分)
" @  s3 M& B+ m5 w3 j1 T2 s+ i
9 O' }; I4 H, Y; J   // TODO: Add extra initialization here
( ?! O1 X* c5 Q  }/ _& N, \7 q, V% M! o* L
   //初始化全局成员变量
; C7 c+ w8 w% |5 A) K, ]! N; g5 P. T* h3 t$ @
   this->bShowLantern=TRUE;8 {+ r& d) E0 \( _) `  ~% [

* B* s' o" {3 }' W, Q- g6 }# i   m_bf.BlendOp = AC_SRC_OVER;
: L$ A( q& o: k/ H2 h9 ]4 U& ^8 ?3 }! _
   m_bf.BlendFlags = 0;
* b3 k. f+ D4 e# Y: d# d9 W5 F8 P) p/ M0 b- I- ^
   m_bf.SourceConstantAlpha =10;* w) u6 x  a- L2 E; I" S

4 x- ?, }8 S5 J( e6 j3 z   m_bf.AlphaFormat = 0;
! ^9 j; H/ u6 q! Q* t" J4 d3 ?5 [" l9 [! q: D! b
   //为节约篇幅,以下代码中略去对操作不成功的处理代码. T- m, V- }3 W5 Y' j

% ^8 {+ p" l( i   if(!cross.LoadBitmap(IDB_CROSS))3 m) R; C) ?) a6 m7 s3 \1 @

. F( O) D- Z. a2 i' i/ s' S   {
  N+ ~, v; \2 r! Z# _
- m: Q8 }& r( J' }4 z/ H, d   AfxMessageBox("装载位图出错!");3 O0 w4 a, w/ N) j. ]

% J1 k6 d! o& K6 d3 }   return FALSE;
* j0 }- O% h/ c" |# |. k- j* y6 a6 ~6 ?
   }
: ^2 ]% c3 a# r7 S# j+ D% m; M3 @+ e& x8 j0 I" L0 G8 M: e( h6 ?9 U7 {
   cross.GetBitmap(&&bmp);8 |) q8 N8 p7 I" g- O
, x- j8 w& h9 b- f3 Z5 h
   lantern.LoadBitmap(IDB_LANTERN);
8 a7 a4 s7 g* e7 j4 f& g( {9 j) o, @8 H  m
   cross.GetBitmap(&&bmp);$ a5 [/ z, g8 T1 e2 N

4 q9 I$ J6 b0 f! Z5 n9 Z   //获得位图的大小信息
; Z' r" M3 Y6 `5 R+ ]
; C- T- A& G  {1 n+ r5 k3 @   bmpWidth=bmp.bmWidth;
% `% L' g/ H: I
, l, B; d; e$ n! {0 t! f4 g) ?9 z   bmpHeight=bmp.bmHeight;
5 E: X7 U' @. z9 M& T
9 E1 m; }$ A9 D* _  dc=this->GetDC();, V! Q( W" P1 w: x7 T  O

3 M' \- Z! ?# m4 Y% _9 a; W, \. n   dcForCross.CreateCompatibleDC(dc);
0 ~9 C& G$ c5 n1 f' N( C1 N2 ~
( ^7 X  O8 m" _" M6 `+ K   dcForLantern.CreateCompatibleDC(dc);+ s7 }0 y* V' K

% y0 B4 [6 q( V6 N6 c   //将位图装入设备环境句柄1 I% w7 k5 @/ k3 T1 ?+ c

, U" e6 Z* ]7 s& v9 j   dcForCross.SelectObject(&&cross);. f4 d) ^, `# p! ?! X
& K# O/ [2 P6 ^# e
   dcForLantern.SelectObject(&&lantern);) ?, i  r' t& f* Q
1 p/ l7 c: a7 m6 B! Y
   //打开计时器
7 q/ I/ K3 ~3 ~5 x. s/ t; [3 N" p% L9 P# |2 m/ i' S
   SetTimer(1000,50,NULL);
5 c2 _; C8 [: R; |! i7 o% Y! S/ d8 \/ l
   return TRUE;
8 M5 [4 p' L+ y( @5 z4 U) D+ Q; `+ v2 m
  }1 }/ y. e" j( D

- F# I& b9 r: @) a) z2 V  void CWipeImageDlg::OnTimer(UINT nIDEvent)
6 ^* ~, M! {4 N, O
' L/ I: V* w# q) j  {
1 d7 |% y- V( a0 S& _
' s9 E+ Q2 v& n; m8 E   //图片透明度每次递增5点
6 N/ [! f0 h& a# d! f& j, o( p% R* k: w0 Y
   m_bf.SourceConstantAlpha+=5;9 n8 G7 k  i# @6 h1 @1 i
% B" c5 g, T( x' Z
   //当第一幅图片完全可见之后,显示另一张图片/ A- U" M4 e" V. t2 b: D
7 M0 a2 C7 V0 `1 R3 f* d+ \9 X* O' `
   if(m_bf.SourceConstantAlpha>=200)
7 Q" ]5 }- S& w* e: Q% C! y! R
$ d3 D4 C- J: U3 D   {
  J0 l6 E% j  `' u. b
1 ~8 |4 ^6 g" I2 g   m_bf.SourceConstantAlpha=10;
2 _5 j" D4 s$ ~# w! E  u, I. X0 U* c% [
   //将bShowLantern做为显示标志,确认应该显示哪一张图片 
2 Y( n% b) ?: {7 X' g* l3 o
+ }; s( Y1 g+ ^' j   bShowLantern=!bShowLantern;
# m5 N9 v# k; L! l: P' [, W) E6 {, ~/ b
   }  {+ E5 A2 m% {; R6 t2 L/ P8 K

$ s  ]! e- W5 E/ g/ T5 \9 ?   if(bShowLantern)1 _6 \- ?: z1 ?# s2 k
- i; d5 F6 `) F! y* D3 e8 _
   {
. v! I# a# I: G9 H# k/ L" X) M3 Q1 t" Q- D3 N; M, p3 u$ q
   //按透明度递增的方式显示“吊灯”图片 AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForLantern,0,0,bmpWidth,bmpHeight,m_bf);1 E$ }9 W% i7 A
9 h- a0 c. x. Z1 T; a  x6 X
   }
9 q1 J  H* Q2 f) Q8 w9 q1 T; a8 t# @5 ]
   else1 O3 A! o- G6 n3 k# r$ Y& b
# J0 N( I4 w( U2 R0 B
   {
, x' G0 |8 o( @/ k5 d5 V  ^2 Z2 n9 l8 R" X' T' x8 @; C$ D
   //按透明度递增的方式显示“背景”图片
- W, K5 Q  F: F% L4 y$ z' j
) E( [0 h( f* C% {   AlphaBlend(?dc,0,0,bmpWidth,bmpHeight,dcForCross,0,0,bmpWidth,bmpHeight,m_bf);
1 V3 R3 W4 G' R. h0 {* _7 \+ ?2 a
   }
# N% t, ^! X7 ^* H# Q2 ?' {
1 P  k* Q1 v1 C5 F' `. @3 a8 `   CDialog::OnTimer(nIDEvent);
: T% i3 @+ D+ J2 y
% E6 E/ e/ q1 t2 P7 f$ i  }+ y) J% q" F. [5 X; \- [- h' C5 T

+ }9 V8 g2 x  H: |! l" P0 X% j0 u
) c9 n) d# q$ _9 ^) E1 E7 R  编译说明- a% B5 b, @3 h

7 O! [5 J" z5 v: X, x+ x! t$ i* G
6 E) _: R& y  E5 [8 `5 H9 D  由于AlphaBlend函数是在“Msimg32.dll”(对应于Msimg32.lib库文件)中定义的。所以,为了避免LNK2001错误,在编译前应该将“Msimg32.lib”文件加入FadeImage项目,然后运行上面的程序,你会发现,在或明或暗之间,你的两幅位图已经出现在屏幕之上了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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