Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image() causes PImage reference to break when using PGraphics #5760

Open
JackWitherell opened this issue Jan 21, 2019 · 2 comments
Open

image() causes PImage reference to break when using PGraphics #5760

JackWitherell opened this issue Jan 21, 2019 · 2 comments

Comments

@JackWitherell
Copy link

JackWitherell commented Jan 21, 2019

Last minute note- I waited a day or so for 3.5 to come out to test to see if this was an issue isolated to 3.4 and it's not. This behavior is still present in 3.5

ANOTHER NOTE: I've gained more insight in trying to figure out this issue. Take a look at the second post for a better example of this behavior that isolates the problem more.

Description

I couldn't find any other cases where this is happening and I've spent the past few days trying to narrow down what causes this behavior. I have a few PGraphics objects and depending where the main PImage is called from when calling image() I'm getting different results for what it looks like.

Made a post on Processing's forums and we've started looking into possible reasons why this is happening and solutions.

Example Code Description

Try running this code example. In this example, 'container' is of a class MajorObject that stores
a. a graphics buffer
b. several objects of type "Object"

Each of the Objects contains
a. a PImage object

setup starts things out by creating an image of a loading bar in a global PGraphics object and then sends that image into the constructor of two "Object" objects that are then stored inside of MajorObject. The draw loop then asks MajorObject to draw those objects onto the screen, starting with the global PGraphics object.

Example Code

PGraphics spriteImage;
MajorObject container;

class MajorObject{
  ArrayList<Object> contains;
  PGraphics localCanvas;
  MajorObject(){
    contains=new ArrayList<Object>();
    localCanvas=createGraphics(180,30);
  }
  void addObject(Object takingInObject){
    contains.add(takingInObject);
  }
  PImage displayObjects(){
    localCanvas.beginDraw();
    localCanvas.clear();
    for(int i=0; i<contains.size();i++){
      image(contains.get(i).getImage(),10,30); //18 draws the internal reference to the image onto the screen
      localCanvas.image(contains.get(i).getImage(),0,20*i); //19 draws the image locally to the internal canvas
    }
    localCanvas.endDraw();
    return localCanvas;
  }
  void printContainer(){
    image(displayObjects(),10,50);
  }
}

class Object{
  PImage storedImage;
  Object(PImage takingInImage){ //31 taking in PGraphics object
    storedImage=takingInImage;
  }
  PImage getImage(){
    PImage tempImage=storedImage;
    return tempImage;
  }
}

void settings(){
  size(200,150);
}

void setup(){
  frameRate(2);
  spriteImage=createGraphics(180,10);
  spriteImage.beginDraw();
  spriteImage.stroke(255);
  spriteImage.fill(0);
  spriteImage.rect(0,0,179,9);
  spriteImage.endDraw();
  container=new MajorObject();
  container.addObject(new Object(spriteImage));
  container.addObject(new Object(spriteImage));
}

void draw(){
  background(0);
  spriteImage.beginDraw();
  spriteImage.line(frameCount,0,frameCount,10);
  spriteImage.endDraw();
  image(spriteImage,10,10);                  //59    display from global object
  container.printContainer();                //60    draw global object's reference in buffer
}

Expected Behavior

I expect either
a. the original PGraphics object to be sent into the constructor and be copied and stored inside of each Object in their respective PImages, therefore to remain unchanged and stay the way they are from the beginning of the program.
or
b. a reference to the original PGraphics object's PImage to be sent in on line 31 to be maintained as a reference to the external global PGraphic's internal PImage. Any changes made towards that PGraphics object to be preserved and shown in any internal calls to the PImages stored in the Objects inside of MajorObject's ArrayList of Objects.

Current Behavior

Depending on which image() function gets called first, either the global reference to the original PGraphics object is severed internally with preliminary changes being apparent (showing that changes to the original PGraphics object did effect the internal PImage at some point) or, if the internal ones get called first, the external reference to the original PGraphics object is severed and isn't updated correctly anymore in draw()

demon

Steps to Reproduce

  1. Copy and Paste the code above into processing
  2. Notice how only the external call to draw the PGraphics object and the internal one to directly draw the internal objects inside of the MajorObject are actually updating along with the original PGraphics object. The internal reference being drawn to the internal buffer in MajorObject isn't updating anymore past the first frame.
  3. Remove lines 18 and 59. Notice how when there's no attempt to draw the global PGraphics object or the reference to it internally, the internal PImages can be drawn to MajorObjects buffer and will update alongside the global PGraphics Object.
    OPTIONALLY, and for insight: place lines 18 19 59 and 60 inside of an if(key=='a') block. What you'll find is that the PGraphics global object will be updating in the background, but as soon as you press the a key and they draw, half of the PImage references get suspended at the progress that the global PGraphics object is at.

Your Environment

Windows 10 newest build, happened on last build of windows too, Processing 3.4 and 3.5

Possible Causes / Solutions

My theory is that the image() function itself is messing with references. I'm not sure if this is a feature inherent to the way java works or if it's even part of Processing's implementation and that it's like this on purpose, but it seems to go against the way java references work.

@JackWitherell
Copy link
Author

I've isolated the problem a bit more effectively. It seems like using the image() function on a PGraphics object causes all other image() calls to other PGraphics objects to either fail or draw whatever the "first" PGraphics object had stored when it first got drawn.

New replication steps:

  1. draw to “first” PGraphics object
  2. draw “first” PGraphics object to another “second” PGraphics object (either cast it to a PImage first or don’t, up to you)
  3. display “second” object and observe that it shows the same as the “first” after the “first” has been drawn to
  4. draw to “first” PGraphics object more
  5. display “first” PGraphics object (this step is crucial)
  6. draw “first” PGraphics object to “second” PGraphics object again
  7. display “second” object and observe how it hasn’t changed because either something went wrong with step 6 or it worked but the version of the “first” PGraphics object that the “second” received was incorrect and unchanged from the “first” PGraphics object’s last draw run.

New code:

PGraphics dataStream;
PGraphics offscreenBuffer;

size(280,280);
background(0);
dataStream = createGraphics(100, 100);
offscreenBuffer = createGraphics(100, 100);

//Part One
//draw unmodified offscreenBuffer to screen
text("unmodified", 0, 20);
text("offscreenBuffer", 0, 40);
image(offscreenBuffer, 0, 40);

//Part Two
//modify dataStream
dataStream.beginDraw();
dataStream.fill(255);
dataStream.rect(0,0,50,100);
dataStream.endDraw();

//draw dataStream to offscreenBuffer
offscreenBuffer.beginDraw();
offscreenBuffer.image(dataStream,0,0);
offscreenBuffer.endDraw();

//draw offscreenBuffer to screen
text("modified dataStream", 140, 20);
text("drawn to offscreenBuffer", 140, 40);
image(offscreenBuffer, 140, 40);

//Part Three
//modify dataStream again
dataStream.beginDraw();
dataStream.fill(255);
dataStream.rect(50,0,50,100);
dataStream.endDraw();

//draw dataStream to screen
text("modified dataStream", 0, 160);
text("drawing dataStream", 0, 180);
image(dataStream, 0, 180);

//draw dataStream to offscreenBuffer
offscreenBuffer.beginDraw();
offscreenBuffer.image(dataStream,0,0);
offscreenBuffer.endDraw();

//draw offscreenBuffer to screen again
text("drawn datastream", 140, 160);
text("to offscreenBuffer", 140, 180);
image(offscreenBuffer, 140, 180);

Running this code displays the following.

57e7fac9fb967d394c9ff4a7c3d25deb88d082fa

Expected output should have the bottom two images look more alike.

@neilcsmith-net
Copy link

Did some playing with the code posted on the forum for this - it looks like with the default Java2D renderer caching is not correctly cleared when calling endDraw() on an offscreen graphics, but only when drawing it to a second offscreen graphics (not the primary). Works correctly with P2D (well, it flickers). Adding an updatePixels() line fixes this from what I can tell - eg.

PGraphics dataStream;
PGraphics offscreenBuffer;

size(280,280);
background(0);
dataStream = createGraphics(100, 100);
offscreenBuffer = createGraphics(100, 100);

//Part One
//draw unmodified offscreenBuffer to screen
text("unmodified", 0, 20);
text("offscreenBuffer", 0, 40);
image(offscreenBuffer, 0, 40);

//Part Two
//modify dataStream
dataStream.beginDraw();
dataStream.fill(255);
dataStream.rect(0,0,50,100);
dataStream.endDraw();

//draw dataStream to offscreenBuffer
offscreenBuffer.beginDraw();
offscreenBuffer.image(dataStream,0,0);
offscreenBuffer.endDraw();

//draw offscreenBuffer to screen
text("modified dataStream", 140, 20);
text("drawn to offscreenBuffer", 140, 40);
image(offscreenBuffer, 140, 40);

//Part Three
//modify dataStream again
dataStream.beginDraw();
dataStream.fill(255);
dataStream.rect(50,0,50,100);
dataStream.endDraw();

//draw dataStream to screen
text("modified dataStream", 0, 160);
text("drawing dataStream", 0, 180);
image(dataStream, 0, 180);

dataStream.updatePixels(); // <---- ADD THIS!

//draw dataStream to offscreenBuffer
offscreenBuffer.beginDraw();
offscreenBuffer.image(dataStream,0,0);
offscreenBuffer.endDraw();

//draw offscreenBuffer to screen again
text("drawn datastream", 140, 160);
text("to offscreenBuffer", 140, 180);
image(offscreenBuffer, 140, 180);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants