我们在玩游戏时经常会看到一些图像的特效,比如半透明等效果。要实现这些效果并不难,只需要对图像本身的像素执行操作。Android中的 Bitmap同样提供了操作像素的方法,可以通过getPixels方法来获得该图像的像素并放到一个数组中,我们处理这个像素数组就可以了,最后通过 setPixels设置这个像素数组到Bitmap中。
在Android中,每一个图像像素通过一个4字节整数来展现:最高位字节用作Alpha通道,即用来实现透明与不透明控制,255代表完全不透明,0则代表完全透明;接下来的一个字节是Red红色通道,255代表完全是红色。依次类推,接下来的两个字节相应地实现绿色和蓝色通道。
下面的示例通过对图像像素的操作来模拟水纹效果,如图5-11所示。
实现代码如下所示:
001 | package com.yarin.android.Examples_05_10; |
002 | |
003 | import android.content.Context; |
004 | import android.graphics.Bitmap; |
005 | import android.graphics.BitmapFactory; |
006 | import android.graphics.Canvas; |
007 | import android.view.KeyEvent; |
008 | import android.view.MotionEvent; |
009 | import android.view.View; |
010 | |
011 | public class GameView extends View implements Runnable |
012 | { |
013 | int BACKWIDTH; |
014 | |
015 | int BACKHEIGHT; |
016 | |
017 | short [] buf2; |
018 | |
019 | short [] buf1; |
020 | |
021 | int [] Bitmap2; |
022 | |
023 | int [] Bitmap1; |
024 | |
025 | public GameView(Context context) |
026 | { |
027 | super (context); |
028 | |
029 | /** 装载图片 */ |
030 | Bitmap image = BitmapFactory.decodeResource( this .getResources(),R.drawable.qq); |
031 | BACKWIDTH = image.getWidth(); |
032 | BACKHEIGHT = image.getHeight(); |
033 | |
034 | buf2 = new short [BACKWIDTH * BACKHEIGHT]; |
035 | buf1 = new short [BACKWIDTH * BACKHEIGHT]; |
036 | Bitmap2 = new int [BACKWIDTH * BACKHEIGHT]; |
037 | Bitmap1 = new int [BACKWIDTH * BACKHEIGHT]; |
038 | |
039 | /** 加载图片的像素到数组中 */ |
040 | image.getPixels(Bitmap1, 0 , BACKWIDTH, 0 , 0 , BACKWIDTH, BACKHEIGHT); |
041 | |
042 | new Thread( this ).start(); |
043 | } |
044 | |
045 | |
046 | void DropStone( int x, // x坐标 |
047 | int y, // y坐标 |
048 | int stonesize, // 波源半径 |
049 | int stoneweight) // 波源能量 |
050 | { |
051 | for ( int posx = x - stonesize; posx < x + stonesize; posx++) |
052 | for ( int posy = y - stonesize; posy < y + stonesize; posy++) |
053 | if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize * stonesize) |
054 | buf1[BACKWIDTH * posy + posx] = ( short ) -stoneweight; |
055 | } |
056 | |
057 | |
058 | void RippleSpread() |
059 | { |
060 | for ( int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) |
061 | { |
062 | // 波能扩散 |
063 | buf2[i] = ( short ) (((buf1[i - 1 ] + buf1[i + 1 ] + buf1[i - BACKWIDTH] + buf1[i + BACKWIDTH]) >> 1 ) - buf2[i]); |
064 | // 波能衰减 |
065 | buf2[i] -= buf2[i] >> 5 ; |
066 | } |
067 | |
068 | // 交换波能数据缓冲区 |
069 | short [] ptmp = buf1; |
070 | buf1 = buf2; |
071 | buf2 = ptmp; |
072 | } |
073 | |
074 | /** 渲染你水纹效果 */ |
075 | void render() |
076 | { |
077 | int xoff, yoff; |
078 | int k = BACKWIDTH; |
079 | for ( int i = 1 ; i < BACKHEIGHT - 1 ; i++) |
080 | { |
081 | for ( int j = 0 ; j < BACKWIDTH; j++) |
082 | { |
083 | // 计算偏移量 |
084 | xoff = buf1[k - 1 ] - buf1[k + 1 ]; |
085 | yoff = buf1[k - BACKWIDTH] - buf1[k + BACKWIDTH]; |
086 | |
087 | // 判断坐标是否在窗口范围内 |
088 | if ((i + yoff) < 0 ) |
089 | { |
090 | k++; |
091 | continue ; |
092 | } |
093 | if ((i + yoff) > BACKHEIGHT) |
094 | { |
095 | k++; |
096 | continue ; |
097 | } |
098 | if ((j + xoff) < 0 ) |
099 | { |
100 | k++; |
101 | continue ; |
102 | } |
103 | if ((j + xoff) > BACKWIDTH) |
104 | { |
105 | k++; |
106 | continue ; |
107 | } |
108 | |
109 | // 计算出偏移象素和原始象素的内存地址偏移量 |
110 | int pos1, pos2; |
111 | pos1 = BACKWIDTH * (i + yoff) + (j + xoff); |
112 | pos2 = BACKWIDTH * i + j; |
113 | Bitmap2[pos2++] = Bitmap1[pos1++]; |
114 | k++; |
115 | } |
116 | } |
117 | } |
118 | |
119 | public void onDraw(Canvas canvas) |
120 | { |
121 | super .onDraw(canvas); |
122 | |
123 | /** 绘制经过处理的图片效果 */ |
124 | canvas.drawBitmap(Bitmap2, 0 , BACKWIDTH, 0 , 0 , BACKWIDTH, BACKHEIGHT, false , null ); |
125 | } |
126 | |
127 | // 触笔事件 |
128 | public boolean onTouchEvent(MotionEvent event) |
129 | { |
130 | |
131 | return true ; |
132 | } |
133 | |
134 | |
135 | // 按键按下事件 |
136 | public boolean onKeyDown( int keyCode, KeyEvent event) |
137 | { |
138 | return true ; |
139 | } |
140 | |
141 | |
142 | // 按键弹起事件 |
143 | public boolean onKeyUp( int keyCode, KeyEvent event) |
144 | { |
145 | DropStone(BACKWIDTH/ 2 , BACKHEIGHT/ 2 , 10 , 30 ); |
146 | return false ; |
147 | } |
148 | |
149 | |
150 | public boolean onKeyMultiple( int keyCode, int repeatCount, KeyEvent event) |
151 | { |
152 | return true ; |
153 | } |
154 | |
155 | /** |
156 | * 线程处理 |
157 | */ |
158 | public void run() |
159 | { |
160 | while (!Thread.currentThread().isInterrupted()) |
161 | { |
162 | try |
163 | { |
164 | Thread.sleep( 50 ); |
165 | } |
166 | catch (InterruptedException e) |
167 | { |
168 | Thread.currentThread().interrupt(); |
169 | } |
170 | RippleSpread(); |
171 | render(); |
172 | //使用postInvalidate可以直接在线程中更新界面 |
173 | postInvalidate(); |
174 | } |
175 | } |
176 | } |