1

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");
      }
      }

  }
}

closed as too broad by user40980, Kilian Foth, durron597, GlenH7, user22815 Oct 8 '15 at 20:09

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • I don't have a lot of experience with Android, but a couple of things look very suspicious here - in particular imageC = image; //Create a copy of display image to be our modifyable image, which doesn't actually make any copy. Hence it seems that you are modifying the actual working displayed image in your update method. The other thing is that apparently the bulk setPixels method will offer a huge speedup. Likely because the image is processed after each modification. – Ordous Sep 15 '15 at 13:18
  • In regards to imageC, you are suggesting I need to load my image again. I was under the impression doing imageC =image; made two seperate objects with identical data not share data. But I'll research that to better understand. I think you have a really good point with the bulk setPixels. I'll try to incorporate that when I get the chance as well. Thank you for the suggestions. – Jager7 Sep 15 '15 at 15:39
  • Just made that change to imageC and it at least tripled the frame rate thank you again. I completely mis understood what I was doing there. I need to read up on the logic with what I did. I still want to try out the bulk setPixel when I get more time later. Thanks again. – Jager7 Sep 15 '15 at 16:16
  • Doing imageC = image; makes only copy of the reference, it does not copy the object itself. Bitmap has a special method to create a copy of itself, see this – Ordous Sep 15 '15 at 16:17
  • Thank you so much for sharing your knowledge Ordous! You knew exactly what I needed. Both issues you pointed out were slowing my program down. You also corrected my misunderstanding. Thank you again. Can you use the answer button to let me upvote you and give you credit? Or should I answer it myself? – Jager7 Sep 16 '15 at 1:34
0

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.