Loading...
Logo
Processing Forum

Problem with PImage resize

in Android Processing  •  8 months ago  
Hi everyone,

I'm trying to display an image with the same resolution of the screen. For this I use this code:

Copy code
  1.   PImage img;
  2.    public void setup() {
  3.      size(displayWidth, displayHeight);
  4.      img=loadImage("image.jpg");
  5.       fondo.resize(width,height);
  6.         }
  7.     public void draw() {
  8.      image(img,0,0);
  9.       }
But I get this:

FATAL EXCEPTION: Animation Thread
java.lang.NullPointerException
    at android.graphics.Bitmap.checkPixelsAccess(Bitmap.java:834)
    at android.graphics.Bitmap.setPixels(Bitmap.java:892)
    at processing.core.PGraphicsAndroid2D.imageImpl(Unknown Source)
    at processing.core.PGraphics.image(Unknown Source)
    at processing.core.PApplet.image(Unknown Source)
    at processing.test.takeone.TakeOne.draw(TakeOne.java:27)
    at processing.core.PApplet.handleDraw(Unknown Source)
    at processing.core.PGraphicsAndroid2D.requestDraw(Unknown Source)
    at processing.core.PApplet.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:1019)


If I comment the line of resize, this problem doesn't appear.

Note: In the Processing IDE, the resize method is turned in bold, but any other method not.

Thanks a lot

Replies(11)

I can't guess where that variable fondo come from?!

What I see is that you store a loaded image reference into variable img.

Perhaps you should try to resize() that rather than fondo!
Sorry, i copied the wrong code. I use the resize method in 'img'
You can resize directly in the image() call.

image(img,0,0,width,height);


Perfect! That has solved the problem!


Thanks a lot.
I try to bring this up one more time, with a performance question

I am having the same problem, but wonder if this is the best solution on a phone. Isn't it a bad idea to resize on the image every time it is drawn, or does it only resize it once, and then no more extra work for the cpu?

the code
Copy code
  1. image(img,0,0,width,height);
is called all the time since it is in my draw method, but the resize could be called just once in the setup, so if it resizes the image every time i draw something, this sounds like a bad solution to me?
it's true it's not a good idea to resize every frame. And as to my experience, that's exactly what image() does - resizes every frame. 
This ability of image() to resize on the fly i guess has two purposes:
  1. help novices to do what they want with easy to use funciton
  2. make simple sketches more concise.
In "production" code you should never resize anything inside draw(). It should be done in the setup() of your sketch (or in background thread).

The possible solution for you may be in doing your resizes in the part of your code which loads images to memory. 
So if now you have:

Copy code
  1. PImage myBg;
  2. PImage myPlayer;
  3. PImage myEnemy;
  4. void setup(){
  5.    loadAllMyImages();
  6. }

  7. void loadAllMyImages(){
  8.     myBg = loadImage("my_bg.png");
  9.     myPlayer = loadImage("my_player.png");
  10.     myEnemy = loadImage("my_enemy.png");
  11. }

  12. void draw(){
  13.     image(myBg, 0,0 , width, height);  
  14.             // inside of loadAllMyImages() this "my_bg.png" may be 100x100 or may be 3000x2000 pixels large)
  15.             // so resizing large image every frame will consume a lot of CPU and will drop your framerate

  16.     image(myPlayer, 10,10, 64,64);
  17.             // same problem here, image of player may be 2048x2048 and resizing it to 64x64 would again consume
  18.             // your CPU and drop frame rate
  19.     image(myEnemy, 200,200, 32,32);
  20. }
so instead you should do this:


Copy code
  1. PImage myBg;
  2. PImage myPlayer;
  3. PImage myEnemy;
  4. void setup(){
  5.    loadAllMyImages();
  6. }

  7. /**
    *  Loads AND RESIZED images
    */
    void loadAllMyImages(){
  8.     myBg = loadImage("my_bg.png");
  9.     myBg.resize(width, height); // match scree nsize

  10.     myPlayer = loadImage("my_player.png");
  11.     myPlayer.resize(64,64); // we know that player is alwasy 64x64

  12.     myEnemy = loadImage("my_enemy.png");
  13.     myEnemy.resize(32,32); // we decided that enemy will always be 32x32
  14. }

  15. void draw(){
  16.     image(myBg, 0,0); // now this will be very quick, as the image is alredy right size
  17.     image(myBg, width, height); // even this one will be same quick, becaue processing will know
  18.                                           // that image size already is same as 'width' and 'height' and will not perform
  19.                                           // any resizing

  20.     image(myPlayer, 10,10, 64,64);  // we can still use this code, as no conversion happens if image is already 64x64
  21.     image(myPlayer, 10,10); // but better to use this code. Then if you forget to resize something, it will 
  22.                                     // be visible on screen straight away. And if you would have left the code above
  23.                                     // then image will be anyways resized and you will not see that you forgot to 
  24.                                     // resize the image. Only peformance will drop. 
  25.           
  26.             image(myEnemy, 200,200); // that's a right way
  27. }

So in the above example you "prepare" all the PImages to be the right size. 
That should be enough for your purposes. If you want to go further and try to do it the way professional games do, you need to prepare your pictures for different screen sizes (screen densities). So you will have one set of images for Mac with Retina and another set of images to run your sketch on 3-inch android device.

But then we are back to the original problem. For some reason the resize method leads to a fatal and nulpointer exception on android.
I only load in the image when the actual object needing it is created, therefore i just placed the resize in the constructor, which should only be called on creation. It should work fine on normal processing, but in Android mode this just seems to be broken
Perhaps loadImage() takes a little more time to accomplish in Android. And consequently, it's null for awhile.
What about keep checking the resultant PImage until it's not null anymore.
And only then, issue a resize() on it?

Re-modeled the examples above to use a separate Thread for each PImage loading.
Dunno if it's enough though. Check it out for yourselves. Got no Android here: 
Copy code
    // http://forum.processing.org/topic/problem-with-pimage-resize
    
    final static int IMGS = 3;
    PImage[] images = new PImage[IMGS];
    
    void setup() {
      size(displayWidth, displayHeight);
      noLoop();
      loadAllMyImages();
      delay(2000);
    }
    
    void draw() {
      background(images[0]);
      image(images[1], 100, 100);
      image(images[2], width-100, height-100);
    }
    
    void loadAllMyImages() {
      new LoadAndResize(0, images, "my_bg.png", width, height).start();
      new LoadAndResize(1, images, "my_player.png", 64, 64).start();
      new LoadAndResize(2, images, "my_enemy.png", 32, 32).start();
    }
    
    class LoadAndResize extends Thread {
      final PImage[] picts;
      final String path;
      final int idx, w, h;
    
      static final int PAUSE = 200;
    
      LoadAndResize(int number, PImage[] imgs, String name, int wdt, int hgt) {
        idx = number;
        picts = imgs;
        path = name;
        w = wdt;
        h = hgt;
      }
    
      void run() {
        picts[idx] = loadImage(path);
        while (picts[idx] == null)   delay(PAUSE);
        picts[idx].resize(w, h);
      }
    }
    
Hello GoTo,

 I was trying to run this code and have some findings:

This code always evalutates to false, thus this line is redundant. 
loadImage() is a blocking call.
  while (picts[idx] == null)   delay(PAUSE);  


Another problem is that there seems to be some kind of processing bug either in image() (in JAVA2D) or in resize() as this sketch it doesn't work properly in JAVA2D (bg image doesn't get resized). Neither it works in android 2D mode.

debug:
imageIfLoaded(processing.core.PImage@412f3090, 100.0, 100.0
FATAL EXCEPTION: Animation Thread
java.lang.NullPointerException
at android.graphics.Bitmap.checkPixelsAccess(Bitmap.java:980)
at android.graphics.Bitmap.setPixels(Bitmap.java:1038)
at processing.core.PGraphicsAndroid2D.imageImpl(Unknown Source)
at processing.core.PGraphics.image(Unknown Source)
at processing.core.PApplet.image(Unknown Source)
at processing.test.lazyimageloading.lazyImageLoading.imageIfLoaded(lazyImageLoading.java:51)
at processing.test.lazyimageloading.lazyImageLoading.draw(lazyImageLoading.java:35)
at processing.core.PApplet.handleDraw(Unknown Source)
at processing.core.PGraphicsAndroid2D.requestDraw(Unknown Source)
at processing.core.PApplet.run(Unknown Source)
at java.lang.Thread.run(Thread.java:856)


It works in Java P2D and in Android P2D modes perfectly.


Here's github link:


Well, I've never done anything to Android yet. T'was just a silly idea that perhaps in Android,
loadImage() was threaded, and would take some time to return a non- null value, or something like that.

That's the reason for the line:
while (picts[idx] == null)   delay(PAUSE);

Await till loadImage() kicks in. Or so I thought.

What I wanted to avoid is what you just added to the program -> imageIfLoaded() 

In my thought, if a resize() took place, it's not null anymore, right?  
I think that the general idea is that Android mode processing tries to mimic as much as possible normal Processing. Of course there're some areas where this mimicing wouldn't work (eg. multitouch, GPS access, accelometers), but still the if something runs in pure java-mode it should be 100% compatible with android mode. 

Thus allowing loadImage() to perform "lazy image load" on anrdoid would be a serious deviation from the processing API and would break many sketches (normal processing sketches would be broken then in Android).  So this is why loadImage() always returns image instance, unless error has happened loading and then and only then it woule return null.

There's though another processing API function : requestImage()  which performs lazy image loading and which returns PImage with value --1 (minus one) as it's dimensions. requestImage() under the hood uses threads, so if you use requestImage() you don't have to worry about starting your own threads. Simple use case of requestImage() would be similar to the one shown at the page:


However, my point was that it seems that   resize() method has some kind of problems with implementation. In Processing 2.b7 resize() would only work if you were to increase the size of the image, and would crash if you try to downsize image. This bug has been fixed in the consequent releases, however it seems there's still some funny stuff happening.

The sketch example I have linked 

works fine in regular Processsing Desktop P2D and Android P2D implementation, however when using regular Processing Desktops JAVA2D or Android regular 2D, it seems to break and show some weird results.
I was wondering if anyone has noticed similar weirdnesses with in resize() behaviour.

When I run sketch with regular JAVA2D renderer it results in error:




but when runnign with P2D renderer it scales correctly. 


but another problem appears: image transparency doesn't get displayed correctly.

I wonder if anyone else has come across similar problem?