Question

I am trying to create a simple 2d game to learn on my own. My current task is creating a lighting scheme.

My goal is to make the screen appear dark except where I create lights. I have a light class that only tracks the position and radius of the light. The code I feel I need help in improving is my per pixel lighting of images that come into the lights radius.

The code I have loads two images. The true image and a bumpmap. It also creates to a copy of the original to be the image to be modified by the light (referencing the originals pixels) and drawn. My code also sets two arrays. One holds the originals pixels to have the rgb pulled from them later. The other stores the calculated normals of the pixels to be used later in the class.

My problem is the update method thatgoes through each pixel using the position, dot product and normals to determine how the light shades the pixel. The update method pulls the frame rate from 60 to around 10 on a single 100x100 image.

I'm expecting to be told it's too much for an Android and better off used on a desktop game, but is that a correct assumption? If so, what are my alternatives? Code in question below.

package com.sevensoftware.simple2d;
import org.apache.http.*;
import android.graphics.*;
import android.content.*;
import android.util.*;

public class MyImage
{
  Bitmap image;
  Bitmap imageC;

  private Bitmap imageB;

  private int width;

  private int height;

  private int[][] map;

  private Point[][] nmap;

  private int power;

  private double softness;
  public MyImage(String name, Context context){

      //Load display image as original reference
      image = ImageLoader.loadImage(name,context);

      width = image.getWidth();
      height = image.getHeight();

      //Create a copy of display image to be our modifyable image
      imageC = image;
      //imageC.setConfig(Bitmap.Config.ARGB_8888);
      //Load Bumpmap
      Log.d("@myImage","loading bumpmap");
      imageB = ImageLoader.loadImage(name+"b", context);
      Log.d("@myImage","loaded bumpmap");
      power = 14;
      softness = Math.pow(2,power);
      map = new int[height][width];
      nmap = new Point[height][width];
      //Create normals
      createNormals();

  }

  public void setImage(Bitmap newImage){
      image = newImage;
  }

  public Bitmap getImage()
  {

      return imageC;
  }

  public int getHeight()
  {

      return height;
  }

  public int getWidth()
  {

      return width;
  }

  private void createNormals(){
      Log.d("@myImage","beginning normals");
      for(int i = 0; i < height; i++){
          for(int j = 0; j < width; j++){
              int c = (imageB.getPixel(j,i)>>16) &0xff;

              map[i][j] = c;

              nmap[i][j] = new Point(c, c);
          }
      }
      for(int i = 0; i < height; i++){
          for(int j = 0; j < width; j++){
              if(i==0|i==height-1|j==0|j==width-1){
                  continue;
                  }
              nmap[i][j].x = map[i][j-1]-map[i][j+1];
              nmap[i][j].y = map[i-1][j]-map[i+1][j];
              }
          }

      Log.d("@myImage","Created normals");

  }

  public void update(Light light, int imagePosX, int imagePosY){

      Log.d("@myImage","beginning update");
      int lx = light.x;
      int ly = light.y;
      int rad = light.radius;

      for(int i = 0; i < height; i++){
          for(int j = 0; j < width; j++){
      //normal vectors
      double AX = nmap[i][j].x;
      double AY = nmap[i][j].y;
              Log.d("@myImage","retrieved normal vetors");

      //light vector
      double BX = lx - (imagePosX+j);
      double BY = ly - (imagePosY+i);
              Log.d("@myImage","light vectors complete");

      //Dot product
      double dp = AX*BX + AY*BY;
              Log.d("@myImage","dp complete");

      int pixel = image.getPixel(j,i);
              Log.d("@myImage","got pixel");
      //int a = (pixel>>24) & 0xff;
      int r = (pixel>>16) & 0xff;
      int g = (pixel>>8) & 0xff;
      int b = (pixel) & 0xff;
              Log.d("@myImage","found rgb");

      double dist = Math.sqrt(BX * BX + BY * BY);
      double percent = (dp+softness/2)/softness;
      double percent2 = dist/rad;

      int lumen = (int)(percent*255);
      int shade = (int)(percent2*255);

      r+=lumen;
      g+=lumen;
      b+=lumen;
      r-=shade;
      g-=shade;
      b-=shade;

      if(r<0){r=0;}
      if(g<0){g=0;}
      if(b<0){b=0;}
      if(r>255){r=255;}
      if(g>255){g=255;}
      if(b>255){b=255;}

      Log.d("@myImage","attempting to set pixel");
      imageC.setPixel(j,i,Color.argb(255,r,g,b));
      Log.d("@myImage", "updated pixel");
      }
      }

  }
}
Was it helpful?

Solution

There are a couple of things in this code that strike me as wrong or inefficient:

First, imageC = image; //Create a copy of display image to be our modifyable image doesn't actually create a copy of the image. It creates a copy of the reference to the image, hence you are modifying the actual image, rather than a copy. This is actually a thing throughout Java, only primitive values can be copied like this, objects require more work. In particular the correct way to make a copy of a bitmap is described here (ignore the accepted answer, and look at the highest-voted one instead).

Second, setPixel and getPixel are native methods that do quite a bit of stuff. There are much more performant bulk methods for that - setPixels and getPixels. It would be best to get all pixels you need in the beginning of the method and set them all in the end, after the computation is done.

Licensed under: CC-BY-SA with attribution
scroll top