noLoop not working as expected in v3.5.4

void setup() {
  // Set the size of the window and background color
  size(1024, 1024);
  background(0, 0, 0);
  noLoop();
}

float plotFromNormalized(float x, float y) { // Takes inputs in range [-1, 1] where it must also output in the range [0, 1]
  return exp(-x*x - y*y);
}

float evaluateNormalized(int x, int y) { // Outputs a value in the range [0, 1]
  float cx = (width - 1) / 2, cy = (height - 1) / 2; // Centre pixel position
  float u = (x - cx) / cx, v = (y - cy) / cy; // Convert to normalized coordinates where u and v is in the range [-1, 1]
  return plotFromNormalized(u, v);
}

void draw() {
  loadPixels(); // Loads the pixel data of the current display window into the pixels[] array
  
  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
      
      // Update the pixel at position x, y
      int loc = x + y * width;
      pixels[loc] = color(255, 255, 255, evaluateNormalized(x, y) * 255);
    }
  }
  
  updatePixels(); // Updates the display window with the data in the pixels[] array
  noLoop();
}

As you can see in the link above, it seems to continuously draw over the previous generated image.

According to the documentation, noLoop() should prevent draw from being repeatedly executed so I’m confused as to why it’s not working here.

Hello,

I put some println() statements to show the progression of each loop for setup() and draw().

Your code is working as expected and the nested for() loops execute once in one loop of draw().

I removed your code for the example but same result.

:)

I also get the same output but regardless, this weird fade-in effect of the pixels takes place? It should just display a single frame on run and not continuously get brighter (see video below)
Updated Code:

void setup() {
  // Set the size of the window and background color
  size(1024, 1024);
  background(0, 155, 0);
  noLoop();
}

float plotFromNormalized(float x, float y) { // Takes inputs in range [-1, 1] where it must also output in the range [0, 1]
  return exp(-x*x - y*y);
}

float evaluateNormalized(int x, int y) { // Outputs a value in the range [0, 1]
  float cx = (width - 1) / 2, cy = (height - 1) / 2; // Centre pixel position
  float u = (x - cx) / cx, v = (y - cy) / cy; // Convert to normalized coordinates where u and v is in the range [-1, 1]
  return plotFromNormalized(u, v);
}

void draw() {
  println(frameCount);
  loadPixels(); // Loads the pixel data of the current display window into the pixels[] array
  
  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
      
      // Update the pixel at position x, y
      int loc = x + y * width;
      pixels[loc] = color(255, 255, 255, evaluateNormalized(x, y) * 5);
    }
  }
  
  updatePixels(); // Updates the display window with the data in the pixels[] array
}

What I see:

Notice how it sort of continuously fades in despite the console confirming only 1 frame has been produced.

2 Likes

Using a PGraphics object solved the issue!

PGraphics pg;

void setup() {
  // Set the size of the window and background color
  size(1024, 1024);
  background(0, 0, 0);
  pg = createGraphics(1024, 1024);
  noLoop();
}

float plotFromNormalized(float x, float y) { // Takes inputs in range [-1, 1] where it must also output in the range [0, 1]
  return exp(-x*x - y*y);
}

float evaluateNormalized(int x, int y) { // Outputs a value in the range [0, 1]
  float cx = (width - 1) / 2, cy = (height - 1) / 2; // Centre pixel position
  float u = (x - cx) / cx, v = (y - cy) / cy; // Convert to normalized coordinates where u and v is in the range [-1, 1]
  return plotFromNormalized(u, v);
}

void draw() {
  pg.beginDraw();
  
  pg.loadPixels(); // Loads the pixel data of the current display window into the pixels[] array
  
  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
      
      // Update the pixel at position x, y
      int loc = x + y * width;
      pg.pixels[loc] = color(255, 255, 255, evaluateNormalized(x, y) * 255);
    }
  }
  
  pg.updatePixels(); // Updates the display window with the data in the pixels[] array
  
  pg.endDraw();
  
  image(pg, 0, 0);

  pg.save("SavedImage" + day() + "-" + month() + "-" + year() + "-" + hour() + "-" + minute() + "-" + second() + ".png");
}
2 Likes

@RosesAreBlue – I’ve honestly never seen anything like this before. If this isn’t a hardware issue (or even if it is) it might be a bug in Java2D mode that should be reported. I’ll take a look.

Reproduced this in both 3.5.4 and in 3.4 with a simpler sketch: a single frame noLoop() sketch that sets transparent pixels to the main canvas via updatePixels does not set a stable screen as expected, but instead slowly fades to a different color over the course 2-10 seconds, despite draw no longer being called.

void setup() {
  size(1024, 1024);
  background(0, 128, 0);
  noLoop();
}
void draw() {
  println(frameCount);
  loadPixels();
  for (int i=1; i<pixels.length; i++) {
    pixels[i] = color(255, 255, 255, 5);
  }
  updatePixels();
}

My initial thoughts:

  1. It only happens if size is called with the default / JAVA2D renderer, not P2D.

  2. According to the documentation, the Processing base canvas does NOT support alpha. I’m honestly surprised that writing into the alpha channel of pixels[] worked with alpha worked at all. I would have expected it to set each pixel to white, not blend with alpha onto the existing pixel value. Yet that is what happens.

  3. I think (apparently?) that the behavior of noLoop() depends on the assumption that everything visible on the canvas is in a non-alpha layer, and so the existing pixels can be applied directly on the old pixels, replacing them. So draw really isn’t being called, but the 5% alpha is still being applied over and over anyway by the display logic. But this is a guess looking just at the bug effect, I haven’t dug into the renderer.

  4. Yes, PGraphics is the workaround, as described above.

Any other thoughts on this from anyone before attempting to open a bug report? Although honestly at this point if this gets any traction it may be in Processing 4, not a fix for 3.x.

3 Likes