android 通过操作像素来实现水波效果(外加四个经典的android面试题)


本文来自安卓航班网

在工程代码中找到GameView.java文件。在Android中,每一个图像像素通过一个4字节整数来展现:最高位字节用作alpha通道,接下来的事Red,依次类推,接下来的两个字节对应实现Green和Bule。

要达到现实的水波效果比较难,这里一切从简了。

先 复习一下物理学。在一滩平静的水面(所有点的振幅为0),扔上一个半径为r的圆形石头,则第一时间水面上被石头打到的那部分水就会往下沉(振幅变为负)。 然后,每一个被打到的点都会把这刚刚获取的能量往四周扩散(在这个例子中,假设只有上下左右四个方向的点受到中心的影响,说了一切从简的),同时,由于扩 散的过程当中的能量损失,振幅会变得越来越小,直至整个水面恢复平静。

折 射,在一张背景图片模拟水波效果的重点在于模拟水波的折射效果。出现水波的时候,相邻两个点之间的高度不一致,出现了一定的高度差,假定我们从正上方看这 个水波,这个高度差就会产生一个折射效果,即我们看到的点应该在实际位置的偏下位置。一切从简的话,这个位置偏移多少就直接由这个高度差来决定算了。就简 单模拟一下,其实运行之后的效果也不是那么的差。

一段段代码的分析:

buf1和buf2分别用来存储一个像素点在一次渲染前和渲染后的振幅,BitMap1,BitMap2用来存储获得的图片像素

   扔石头,如上所述
则第一时间水面上被石头打到的那部分水就会往下沉(振幅变为负)。
  1. void DropStone(int x,// x坐标
  2. int y,// y坐标
  3. int stonesize,// 波源半径
  4. int stoneweight)// 波源能量
  5. {
  6. for (int posx = x - stonesize; posx < x + stonesize; posx++)
  7. for (int posy = y - stonesize; posy < y + stonesize; posy++)
  8. if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize
  9. * stonesize)
  10. buf1[BACKWIDTH * posy + posx] = (short) -stoneweight;
  11. }
  12. 这一段则是现实扩散的过程。buf2为扩散之后的振幅,由于只考虑上下左右四个方向对中心振幅的影响,那么影响一个点在一次扩散之后的振幅为四周的振幅和 自己上一次的振幅。四周的影响假定相同。设X为中心处得振幅,上下左右振幅为X1,X2,X3,X4,X’为一次扩散之后的振幅则
    X’
    =(X1 +X2+X3+X4)*a+X*b。 这个扩散是相对的,四个方向同样要受到中心点的影响,非常粗虐的计算,根据能量守恒,同时把这个局部当做整体(忽略边缘)
    X+
    X1 + X2
    + X3 +X4=X'+X1'  +X2'
    + X3' + X4'。代入之后得出结果  4a+b=1。取一组合理的解为a=1/2,b=-1。为了提高效率,将除以2变成移位。得到新的振幅之后,执行衰减。

  1. void RippleSpread() {
  2. for (int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) {
  3. // 波能扩散
  4. buf2[i] = (short) (((buf1[i - 1] + buf1[i + 1]
  5. + buf1[i - BACKWIDTH] + buf1[i + BACKWIDTH]) >> 1) - buf2[i]);
  6. // 波能衰减
  7. buf2[i] -= buf2[i] >> 5;
  8. }
  9. // 交换波能数据缓冲区
  10. short[] ptmp = buf1;
  11. buf1 = buf2;
  12. buf2 = ptmp;
  13. }
  14. 扩散一次之后,则根据相邻像素点之间的高度差(即振幅差)计算出偏移量Xoff和Yoff,上面说过的,偏移就直接等于高度差算了,一切从简。然后将偏移加到具体的像素里头,新的像素为原来的像素加上偏移之后的像素。
    1. /* 渲染你水纹效果 */
    2. void render() {
    3. int xoff, yoff;
    4. int k = BACKWIDTH;
    5. for (int i = 1; i < BACKHEIGHT - 1; i++) {
    6. for (int j = 0; j < BACKWIDTH; j++) {
    7. // 计算偏移量
    8. xoff = buf1[k - 1] - buf1[k + 1];
    9. yoff = buf1[k - BACKWIDTH] - buf1[k + BACKWIDTH];
    10. // 判断坐标是否在窗口范围内
    11. if ((i + yoff) < 0) {
    12. k++;
    13. continue;
    14. }
    15. if ((i + yoff) > BACKHEIGHT) {
    16. k++;
    17. continue;
    18. }
    19. if ((j + xoff) < 0) {
    20. k++;
    21. continue;
    22. }
    23. if ((j + xoff) > BACKWIDTH) {
    24. k++;
    25. continue;
    26. }
    27. // 计算出偏移象素和原始象素的内存地址偏移量
    28. int pos1, pos2;
    29. pos1 = BACKWIDTH * (i + yoff) + (j + xoff);
    30. pos2 = BACKWIDTH * i + j;
    31. Bitmap2[pos2++] = Bitmap1[pos1++];
    32. k++;
    33. }
    34. }
    35. }
    36. 最后,把这些加到线程里头,DropStone方法在onKeyUp事件中调用,线程绘图了,每过50ms就扩撒一次,直至水面平静。
      1. public void run() {
      2. while (!Thread.currentThread().isInterrupted()) {
      3. try {
      4. Thread.sleep(50);
      5. } catch (InterruptedException e) {
      6. Thread.currentThread().interrupt();
      7. }
      8. RippleSpread();
      9. render();
      10. // 使用postInvalidate可以直接在线程中更新界面
      11. postInvalidate();
      12. }
      13. }
      14. 知名公司java面试题之一
        http://www.apkway.com/forum.php?mod=viewthread&tid=1913&extra=page%3D1
        Android企业面试题
        http://www.apkway.com/forum.php?mod=viewthread&tid=1914&extra=page%3D1
        史上最全的android面试题库
        http://www.apkway.com/forum.php?mod=viewthread&tid=1915&extra=page%3D1
        网易博客中超给力的android面试题
        http://www.apkway.com/forum.php?mod=viewthread&tid=1916&extra=page%3D1
posted @ 2011-07-19 22:07  迷失的幽灵  阅读(665)  评论(0编辑  收藏  举报