Loading...
Logo
Processing Forum
I'm using this code with P2D (java2D) in Processing 2.0a1... the FPS always drops to about 20 or less. I have preset 30 fps.

Copy code
  1. loadPixels();
  2.   pg.loadPixels();
  3.  
  4.   for(int x = 0; x < width; x++){
  5.     for(int y = 0; y < height; y++){
  6.       if (pg.pixels[y*pg.width+x] != color(255)){
  7.         pixels[y*width+x] = lerpColor(pg.pixels[y*pg.width+x], pixels[y*width+x], 0.6);
  8.       }
  9.     }
  10.   }
  11.  
  12.   updatePixels();
Width is 700 and height is 600. PG is PGraphics with black background and white "shape" drawn in it.
When I del this part of code sketch runs ok at 30 fps.
Is there a way to make it quicker?

Replies(21)

lerpcolor is probably the bottleneck. roll your own integer based rgb blending function.  you can do away with the two for-loops and replace with just one.  replace (y*pg.width+x) with an incrementing pixelIndex variable. it would also help if you pasted the entire sketch.  what is the end goal?  you can reach ultimate speed you run this through pixelshader such as  GLGraphics or  victamin

loop once;  i++ {
pixels[i]
}

for more definitive answer you can run your code through a profiler.

Lerp Color works fine... I was testing the code out with this

Copy code
  1. pixels[y*width+x] = color(#ff0000);
and it was the same slow result. But I can try the one loop thing.

EDIT: I tryed it didn't help.

The code is from my game and should be doing Fog Of War ... in the PG I have the white circles at position of my "heroes" and black at rest. The map/field is image and I need to make some pixels in it darker. ( Video from game play.)
I was using the Geomerative and doing rect with ellipse holes filling it up with fill(0, 80) and using it as overlay... but it's so slow with java2D. I read that writing the pixel data should be fast. So I tryed it out.
I would use OpenGL but that doesn't work with Processing 2 so far.
I don't know how to solve your problem, but I have been playing with Geomerative a lot.

One thing I figured out very quickly was that using either Processing's translate() functions or Geomerative's RG.shape(RShape, float, float) was very slow. This is because, in both cases, you are translating your shapes every frame, whether they are "moving" or not.

Because I am mostly interested in still images, my solution was to "pre-calculate" these translations.

What I mean by that was that I created another class that either extended RShape or contained an RShape. I have an RPoint field called position in the class. Whenever I need to move the shape, I translate the shape itself and add to the RPoint position. This only happens once, for the one shape I need to move. As opposed to every shape, every frame. And, because I keep track of the translations, I can still move the shape wherever I need to.

This probably won't work for your situation, but I hope it helps.


you can improve speed a little bit, by defining your color just once (outside the loop).

Copy code
  1.   loadPixels();
  2.   pg.loadPixels();
  3.     int col = color(255);//define it outside the loop
  4.     for(int i = 0; i < pg.pixels.length; i++){
  5.       if (pg.pixels[i] != col){
  6.         pixels[i] = lerpColor(pg.pixels[i], pixels[i], 0.6f);
  7.       }
  8.     }
  9.   updatePixels();


here's the lerp function (copied from the processing source), i dont think theres much to improve:
Copy code
  1. static public final int lerpColor_v2(int c1, int c2, float amt) {
  2.   float a1 = (c1 >> 24) & 0xff;
  3.   float r1 = (c1 >> 16) & 0xff;
  4.   float g1 = (c1 >>  8) & 0xff;
  5.   float b1 = c1 & 0xff;
  6.  
  7.   float a2 = (c2 >> 24) & 0xff;
  8.   float r2 = (c2 >> 16) & 0xff;
  9.   float g2 = (c2 >>  8) & 0xff;
  10.   float b2 = c2 & 0xff;
  11.   return (((int) (a1 + (a2-a1)*amt) << 24) |
  12.           ((int) (r1 + (r2-r1)*amt) << 16) |
  13.           ((int) (g1 + (g2-g1)*amt) <<  8) |
  14.           ((int) (b1 + (b2-b1)*amt)));
  15. }
what also might work for you is, working with threads:

heres the code sample. its a little bit faster than without threads, but therefor the code is a little bit more complicated.





Copy code
  1. PGraphics pg;
  2. int size_x = 700;
  3. int size_y = 700;

  4. public void setup() {
  5.   size(size_x, size_y );
  6.  
  7.   pg = createGraphics(size_x, size_y , JAVA2D);
  8.   pg.beginDraw();
  9.   pg.background(0);
  10.   pg.fill(255);
  11.   pg.ellipse(size_x/2, size_y/2, size_x/2, size_y);
  12.   pg.endDraw();
  13.  
  14.   frameRate(120);
  15. }

  16. public void draw() {
  17.   loadPixels();
  18.     lerpGraphicsWithThreads(pg, 4);
  19.     //lerpGraphics(pg);
  20.   updatePixels();
  21.   println(frameRate);
  22. }

  23. // common version
  24. public void lerpGraphics(PGraphics graphics){
  25.   graphics.loadPixels();
  26.   int col = color(255);
  27.   for (int i = 0; i < graphics.pixels.length; i++) {
  28.     if (graphics.pixels[i] != col) {
  29.       pixels[i] = lerpColor(graphics.pixels[i], pixels[i], 0.6f);
  30.     }
  31.   }
  32. }

  33. // threaded version
  34. public void lerpGraphicsWithThreads(final PGraphics graphics, final int number_of_threads){
  35.   class MyThread implements Runnable{
  36.     int col_ = 0xFFFFFFFF;
  37.     int from_, to_;
  38.     Thread thread_;
  39.     public MyThread(int from, int to){
  40.       from_ = from;
  41.       to_ = to;
  42.       thread_ = new Thread(this);
  43.       thread_.start();
  44.     }
  45.    
  46.     public void run(){
  47.       for (int i = from_; i < to_; i++) {
  48.         if (graphics.pixels[i] != col_) {
  49.            pixels[i] = lerpColor(graphics.pixels[i], pixels[i], 0.6f);
  50.         }
  51.       }
  52.     }
  53.   }
  54.  
  55.   graphics.loadPixels();
  56.   int number_of_pixels = graphics.pixels.length/number_of_threads;
  57.   MyThread mt[] = new MyThread[number_of_threads];
  58.   for(int i = 0; i <number_of_threads; i++){
  59.     mt[i] = new MyThread(number_of_pixels*i, number_of_pixels*(i+1));
  60.   }
  61.   for(int i = 0; i < number_of_threads; i++){
  62.     try{
  63.       mt[i].thread_.join() ;
  64.     } catch(InterruptedException e){
  65.       e.printStackTrace();
  66.     }
  67.   }
  68. }






Let me suggest an alternative which perhaps could be of use here: tint(). Below is a basic example, which could be adapted in several ways (i.e. draw to the fog pgraphics differently, include multiple visibility circles, different level of darkening). But the example below will show you the basic technique. On my computer it runs at about 60 fps.

Code Example
Copy code
  1. PImage img;
  2. PGraphics fog;
  3. color black = color(0);
  4. color white = color(255);
  5.  
  6. void setup() {
  7.   size(700, 600);
  8.   img = loadImage("http://www.salepaintings.com/uploads/4/1/9/7/4197246/8232646_orig.jpg");
  9.   fog = createGraphics(width, height, JAVA2D);
  10. }
  11.  
  12. void draw() {
  13.   background(img); // random image from google ;-)
  14.   drawFog();
  15.   println(int(frameRate));
  16. }
  17.  
  18. void drawFog() {
  19.   fog.beginDraw();
  20.   fog.background(0);
  21.   fog.smooth();
  22.   fog.noStroke();
  23.   fog.fill(255);
  24.   fog.ellipse(mouseX, mouseY, fog.height/2, fog.height/2);
  25.   fog.endDraw();
  26.   loadPixels();
  27.   fog.loadPixels();
  28.   for (int i=0; i<fog.pixels.length; i++) {
  29.     color pixel = fog.pixels[i];
  30.     if (pixel == white) {
  31.       fog.pixels[i] &= 0x00FFFFFF; // make white transparent
  32.     } else if (pixel != black) {
  33.       fog.pixels[i] = lerpColor(fog.pixels[i], pixels[i], 0.6f); // for the edge areas
  34.     }
  35.   }
  36.   fog.updatePixels();
  37.   tint(255,125); // second value is the level of darkening
  38.   image(fog,0,0);
  39. }
It runs on my with ~31 fps ... but when I put it into game its 20 again. And white is not transparent just tinted.
Apparantly there are other issues with your game code. Perhaps you should look into optimizing the whole code, not just this aspect. However with regard to this issue, do you make use of colorMode() somewhere in your code? Because that could cause this and is also known for taking a chunk of your precious fps.
No I'm not using colorMode anywhere. And the code runs smooth when I...

don't do pixel color change
or when I use Geomerative rect with holes but draw it without alpha.

But I'm really startled why isn't it working when it was working elsewhere. Maybe I have some glitch there or what not.
martiinko, after having watched your  video I think you are following the wrong strategy.

Why do you run your shading algorithm over the whole pixel field? This is waste of CPU time.

You just need to know the boundaries of your combined circles that you are drawing into pg.
Then you run the shader loop just within the rectangle with these boundaries. This will save you a lot of checks for black color.
Sorry, I saw that you are making the outside of your "heroes" darker.
However, you should do the reverse. Make the inside lighter, which saves you a lot CPU ops, as I said in my previous post. See the attached image. The loop just must be carried out within the yellow boundaries.


It's actually something like this... :) there is more "white" then black...


And you can see the problem where there is white just tinted not all transparent.
Yes, it all depends on the position of your objects you would like to highlight. In your video, the circles were collated. Still, you can save CPU time by finding first the boundaries and just doing the comparisons within them, whether you use the "negative" or the "positive" stamp method.





Still it would be best to use OpenGl and geomerative... I have to wait for Processing 2.0a2 :D I hope they fix it.
Turning into quite a thread here. Let me chime in with another method.

I've ditched tint() in favor of using: fast xy-pixel array, bit shift transparency, semi-transparent black, fast distance function.

With 4 heroes it runs at about 60 fps on my computer in Processing 2.0a1. Let me know how this works for you.

Code Example
Copy code
  1. PImage img, fog;
  2. color black = color(0, 125);
  3. PVector[] vecs = new PVector[4]; // don't create PVectors in the draw loop
  4. int visibilityRadius = 150;
  5.  
  6. void setup() {
  7.   size(700, 600);
  8.   img = loadImage("http://www.salepaintings.com/uploads/4/1/9/7/4197246/8232646_orig.jpg");
  9.   fog = createImage(width, height, ARGB); // don't create fog image in the draw loop
  10.   visibilityRadius *= visibilityRadius; // for use with fast 2d distance function
  11.   for (int i=0; i<vecs.length; i++) {
  12.     vecs[i] = new PVector();
  13.   }
  14. }
  15.  
  16. void draw() {
  17.   background(img);
  18.   vecs[0].set(mouseX, mouseY, 0); // hero 1
  19.   vecs[1].set(width-mouseX, height-mouseY, 0); // hero 2
  20.   vecs[2].set(frameCount % width,140,0); // hero 3
  21.   vecs[3].set(500,frameCount%height,0); // hero 4
  22.   drawFog(vecs);
  23.   println(int(frameRate)); // runs at 59 fps for me
  24. }
  25.  
  26. void drawFog(PVector[] vecs) {
  27.   fog.loadPixels();
  28.   for (int i=0, y=0; y<fog.height; y++) {
  29.     for (int x=0; x<fog.width; x++, i++) {
  30.       if (checkDistances(x, y, vecs)) {
  31.         fog.pixels[i] &= 0x00FFFFFF;
  32.       } else {
  33.         fog.pixels[i] = black;
  34.       }
  35.     }
  36.   }
  37.   fog.updatePixels();
  38.   image(fog,0,0);
  39. }
  40.  
  41. boolean checkDistances(int x, int y, PVector[] vecs) {
  42.   boolean cd = false;
  43.   for (int i=0; i<vecs.length; i++) {
  44.     if (d(vecs[i].x, vecs[i].y, x, y) < visibilityRadius) {
  45.       cd = true;
  46.       break; // if it's true for one, stop searching
  47.     }
  48.   }
  49.   return cd;
  50. }
  51.  
  52. float d( float x1, float y1, float x2, float y2) {
  53.   return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
  54. }

Its 33 fps here. You certainly have a more powerful computer :D I tryed it out in game, worked fine with one circle, with 4 dropped at about 15 fps. :-/
But great code. I tried out the game code with just the the fog in draw... still only 18 fps... its really weird though.
I have to use the thread somehow. What is weird that when I used the OpenGL it gave me same results + 30 seconds after OutOfMemory error.
Nice method. You could even use the distance to blur out the fog.
Is it possible to blur just some area? For example about some object? :D Thats actually not related to first question.
All right, if all else fails there is one last fps cheat you can use...

Remove line 38 and replace line 22 by the following code...
Copy code
  1.   if (frameCount % 2 == 0) { drawFog(vecs); }
  2.   image(fog,0,0);
...where you can even increase the number 2 to something higher. At 2 or 3 it's almost unnoticeable, however when you go higher you will be able to see the consequence. But it will give you some fps back.

Another reason why the same code is slower in your game might be the use of smooth(). You could check that.
amnon.owed's algorithm yields 43fps on my computer.

I rewrote it a little bit and now obtain between 53 and 55 fps. Actually, it just calculates the regions of interest, not the whole canvas.

Amnon.owed's algorithm uses about 844000 loop runs (4 circles with a total of 150*150*PI pixels = 282735 pixels times 2.5 distance checks on average, plus (700*600 - 282735) times 4 distance checks).

My algorithm uses 720000 loop runs (4 * 300 * 300 with distance checks and 4 * 300 * 300 for resetting the color) in the worst case, when all rings are completely within the canvas. 

With regards to the question that was not asked, blurring: One can use color(0, distance / visibilityRadius) (or the squared distance/radius) instead of 0x00FFFFF. I would then recommend, however, to use amnon.owed's algorithm, since mine does not include "collision detection" and hence would yield Moiré effets.  
 

Copy code
  1. PImage img, fog;
  2. color black = color(0, 125);
  3. PVector[] vecs = new PVector[4]; // don't create PVectors in the draw loop
  4. int visibilityRadius = 150;
  5. int visibilityRadius2 = visibilityRadius * visibilityRadius; // for use with fast 2d distance function
  6.  
  7. void setup() {
  8.   size(700, 600);
  9.   img = loadImage("http://www.salepaintings.com/uploads/4/1/9/7/4197246/8232646_orig.jpg");
  10.   // img = loadImage("8232646_orig.jpg");
  11.   fog = createImage(width, height, ARGB); // don't create fog image in the draw loop
  12.   fog.loadPixels();
  13.   for (int i=0, y=0; y<fog.height; y++) {
  14.     for (int x=0; x<fog.width; x++, i++) {
  15.       fog.pixels[i] = black;
  16.     }
  17.   }
  18.   fog.updatePixels();
  19.   
  20.   for (int i=0; i<vecs.length; i++) {
  21.     vecs[i] = new PVector();
  22.   }
  23. }
  24.  
  25. void draw() {
  26.   background(img);
  27.   vecs[0].set(mouseX, mouseY, 0); // hero 1
  28.   vecs[1].set(width-mouseX, height-mouseY, 0); // hero 2
  29.   vecs[2].set(frameCount % width,140,0); // hero 3
  30.   vecs[3].set(500,frameCount%height,0); // hero 4
  31.   drawFog(vecs);
  32.   println(int(frameRate)); // runs at 59 fps for me
  33. }
  34.  
  35. void drawFog(PVector[] vecs) {
  36.   fog.loadPixels();
  37.   for (int i=0; i<vecs.length; i++) {
  38.     float vec_x = vecs[i].x;
  39.     float vec_y = vecs[i].y;
  40.     int v_left = int(vec_x - visibilityRadius);
  41.     int v_right = int(vec_x + visibilityRadius);
  42.     int v_top = int(vec_y - visibilityRadius);
  43.     int v_bottom = int(vec_y + visibilityRadius);
  44.     
  45.     int x_start = max(0,v_left);
  46.     int x_end = min(width,v_right);
  47.     int y_start=max(0,v_top);
  48.     int y_end=min(height,v_bottom);
  49.     
  50.     for (int y = y_start; y < y_end; y++) {
  51.       for (int x = x_start; x < x_end; x++) {
  52.         int j = y * width + x;
  53.         if (d(vec_x, vec_y, x, y) < visibilityRadius2) {
  54.           fog.pixels[j] &= 0x00FFFFFF;
  55.         }
  56.       }
  57.     }
  58.   }


  59.   fog.updatePixels();
  60.   image(fog,0,0);
  61.   fog.loadPixels();
  62.   
  63.   for (int i=0; i<vecs.length; i++) {
  64.     float vec_x = vecs[i].x;
  65.     float vec_y = vecs[i].y;
  66.     int v_left = int(vec_x - visibilityRadius);
  67.     int v_right = int(vec_x + visibilityRadius);
  68.     int v_top = int(vec_y - visibilityRadius);
  69.     int v_bottom = int(vec_y + visibilityRadius);
  70.     
  71.     int x_start = max(0,v_left);
  72.     int x_end = min(width,v_right);
  73.     int y_start=max(0,v_top);
  74.     int y_end=min(height,v_bottom);
  75.     
  76.     for (int y=y_start; y < y_end; y++) {
  77.       for (int x=x_start; x < x_end; x++) {
  78.         int j = y * width + x;
  79.         fog.pixels[j] = black;
  80.       }
  81.     }
  82.   }
  83.   fog.updatePixels();
  84. }

  85.  
  86. float d( float x1, float y1, float x2, float y2) {
  87.   return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
  88. }
I finally figured it out. The main problem was the alpha channel. Because the Java2D was unable to quickly calculate the exact color underneath (1. layer background image -> 2. layer alpha image = some color).

The thing I did was that I grabbed the 1. layer ... the image copied it into temporary image, then I made "mask" in PGraphics and compared that with black/white... and put lerp colors into temp image. Draw the image. With no aphla channel.

This demo can make at my computer 70fps. So it's definitely the fastest. But thank you all for ideas and snippets :) I used some stuff.

Code:


Copy code
  1. PGraphics pg;
  2. PImage fog, bg;

  3. void setup(){
  4.   size(600, 400, JAVA2D); // adjusted because of background image
  5.   bg = loadImage("http://www.freefoto.com/images/07/10/07_10_2---Grass_web.jpg"); // some image or texture
  6.   pg = createGraphics(width, height, JAVA2D); 
  7.   fog = createImage(width, height, ARGB);
  8. }

  9. void draw(){
  10.   fogTh();
  11.   image(fog, 0, 0);
  12.   println(round(frameRate));
  13. }

  14. void drawLight(){
  15.   // add here some for loop with vector's data or some class data, with position of hero
  16.   // instead of mouseX, mouseY

  17.   int los = 100; // line of sight
  18.   pg.ellipse(mouseX, mouseY, los, los);
  19.   pg.ellipse(width-mouseX, height-mouseY, los*2, los*2);
  20. }

  21. void fogTh(){
  22.   color redish = color(#ff0000);
  23.   color greenish = color(#00ff00);
  24.   color blackish = color(0);
  25.   color col;
  26.  
  27.   pg.beginDraw();
  28.   pg.background(0);
  29.   pg.noStroke();
  30.   pg.fill(255);
  31.  
  32.   drawLight();
  33.  
  34.   pg.endDraw();
  35.  
  36.   pg.loadPixels();
  37.  
  38.   fog = bg.get(0,0,width,height);
  39.   fog.loadPixels();
  40.       for (int i=0, y=0; y<fog.height; y++) {
  41.         for (int x=0; x<fog.width; x++, i++) {
  42.           col = pg.pixels[i];
  43.           if (col == blackish){
  44.             fog.pixels[i] = lerpColor(fog.pixels[i], blackish, 0.6);
  45.           }
  46.         }
  47.       }
  48.    fog.updatePixels();
  49. }