In Android, each image pixel is displayed by a 4-byte INTEGER: the highest byte is used as the alpha channel, and the following things are red, and so on. The next two bytes correspond to green and Bule.
It is difficult to achieve the real water wave effect. Everything is simplified here.
First, review physics. In a calm water surface (the amplitude of all points is 0), place a circular stone with a radius of R, the water that is first hit by a rock on the water surface will sink (the amplitude changes to negative ). Then, each hit point will spread the obtained energy around (in this example, it is assumed that only the points in the top, bottom, and left directions are affected by the center, at the same time, because of the energy loss during the diffusion process, the amplitude will become smaller and smaller until the whole water surface recovers.
Refraction: The focus of simulating water waves in a background image is to simulate the refraction of water waves. When a water wave occurs, the height of the adjacent two points is inconsistent and a certain height difference occurs. If we look at the water wave from the top, this height difference will produce a refraction effect, that is, the point we see should be in the lower position of the actual position. If everything is simplified, the deviation of the position is determined by the height difference. Simply simulate it. In fact, the effect after running is not that bad.
Segment code analysis:
Buf1 and buf2 are used to store the amplitude of a pixel before and after a rendering, respectively. bitmap1 and bitmap2 are used to store the acquired image pixels.
5 |
buf2 = new
short [BACKWIDTH * BACKHEIGHT];
|
6 |
buf1 = new
short [BACKWIDTH * BACKHEIGHT];
|
8 |
Bitmap2 = new
int [BACKWIDTH * BACKHEIGHT];
|
9 |
Bitmap1 = new
int [BACKWIDTH * BACKHEIGHT]; |
Throwing stones, as described above
The water that is first hit by a rock on the water surface will sink (the amplitude changes to negative ).
01 |
void DropStone(
int x, // X coordinate
|
03 |
int stonesize,
// Wave source RADIUS |
04 |
int stoneweight)
// Wave source energy |
06 |
for (
int posx = x - stonesize; posx < x + stonesize; posx++)
|
07 |
for (
int posy = y - stonesize; posy < y + stonesize; posy++)
|
08 |
if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize* stonesize)
|
09 |
buf1[BACKWIDTH * posy + posx] = ( short ) -stoneweight;
|
This section is a process of practical diffusion. Buf2 is the amplitude after diffusion. Because only the upper and lower left directions affect the center amplitude, the amplitude of a point after one diffusion is the amplitude of four weeks and the amplitude of the last one. The four-week impact is assumed to be the same. Set X as the center amplitude. The upper and lower left amplitude is x1, x2, X3, X4, and X' as the amplitude after one diffusion. X' = (X1 + X2 + X3 + X4) * A + x * B. This diffusion is relative, and the four directions are also affected by the central point. The calculation is very crude, and this part is considered as a whole based on conservation of energy. X + X1 + X2 + X3 + X4 = x' + X1 '+ x2' + x3'
+ X4 '. The result is 4A + B = 1. The combination is interpreted as a = 1/2, B =-1. To improve efficiency, divide by 2 to shift. After a new amplitude is obtained, the attenuation is performed.
02 |
for (
int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) {
|
03 |
// Wave energy diffusion |
04 |
buf2[i] = ( short ) (((buf1[i -
1 ] + buf1[i +
1 ]+ buf1[i - BACKWIDTH] + buf1[i + BACKWIDTH]) >>
1 ) - buf2[i]); |
06 |
buf2[i] -= buf2[i] >> 5 ;
|
08 |
// Exchange the Boundary Data Buffer |
After one diffusion, the offset xoff and yoff are calculated based on the height difference (amplitude difference) between adjacent pixels. As mentioned above, the offset is directly equal to the height difference, and everything is simplified. Then, add the offset to the specific pixel. The new Pixel adds the offset to the original pixel.
01 |
/* Render your watermark effect */ |
05 |
for (
int i =
1 ; i < BACKHEIGHT -
1 ; i++) { |
06 |
for (
int j =
0 ; j < BACKWIDTH; j++) {
|
07 |
// Calculate the offset |
08 |
xoff = buf1[k - 1 ] - buf1[k +
1 ]; |
09 |
yoff = buf1[k - BACKWIDTH] - buf1[k + BACKWIDTH];
|
10 |
// Determine whether the coordinates are within the window range |
17 |
if ((i + yoff) > BACKHEIGHT) {
|
27 |
if ((j + xoff) > BACKWIDTH) {
|
32 |
// Calculate the memory address offset of the Offset pixel and the original Pixel |
34 |
pos1 = BACKWIDTH * (i + yoff) + (j + xoff);
|
35 |
pos2 = BACKWIDTH * i + j; |
36 |
Bitmap2[pos2++] = Bitmap1[pos1++]; |
Finally, add these to the thread. The dropstone method is called in the onkeyup event, and the thread is drawn. It is expanded every 50 ms until the water surface is calm.
02 |
while (!Thread.currentThread().isInterrupted()) {
|
05 |
} catch
(InterruptedException e) { |
06 |
Thread.currentThread().interrupt(); |
11 |
// Use postinvalidate to directly update the interface in the thread |