user7349781
user7349781

Reputation: 11

Drawing image(PGraphics) gives unwanted double image mirrored about x-axis. Processing 3

The code is supposed to fade and copy the window's image to a buffer f, then draw f back onto the window but translated, rotated, and scaled. I am trying to create an effect like a feedback loop when you point a camera plugged into a TV at the TV.

I have tried everything I can think of, logged every variable I could think of, and still it just seems like image(f,0,0) is doing something wrong or unexpected.

What am I missing?

Pic of double image mirror about x-axis:

should only be one trail of squares

PGraphics f;
int rect_size;
int midX;
int midY;

 void setup(){
  size(1000, 1000, P2D);
  f = createGraphics(width, height, P2D);
  midX = width/2;
  midY = height/2;
  rect_size = 300;
  imageMode(CENTER);
  rectMode(CENTER);
  smooth();
  background(0,0,0);
  fill(0,0);
  stroke(255,255);
}

void draw(){
  fade_and_copy_pixels(f); //fades window pixels and then copies pixels to f
  background(0,0,0);//without this the corners dont get repainted.
  //transform display window (instead of f)
  pushMatrix();
  float scaling = 0.90; // x>1 makes image bigger
  float rot = 5; //angle in degrees
  translate(midX,midY); //makes it so rotations are always around the center
  rotate(radians(rot));
  scale(scaling);
  imageMode(CENTER);
  image(f,0,0); //weird double image must have something not working around here
  popMatrix();//returns window matrix to normal
  int x = mouseX;
  int y = mouseY;
  rectMode(CENTER);
  rect(x,y,rect_size,rect_size);
}

//fades window pixels and then copies pixels to f
void fade_and_copy_pixels(PGraphics f){
  loadPixels();  //load windows pixels. dont need because I am only reading pixels?
  f.loadPixels(); //loads feedback loops pixels
  // Loop through every pixel in window
  //it is faster to grab data from pixels[] array, so dont use get and set, use this

  for (int i = 0; i < pixels.length; i++) {
      //////////////FADE PIXELS in window and COPY to f:///////////////
      color p = pixels[i];

      //get color values, mask then shift
      int r = (p & 0x00FF0000) >> 16;
      int g = (p & 0x0000FF00) >> 8;
      int b =  p & 0x000000FF; //no need for shifting

      // reduce value for each color proportional 
      // between fade_amount between 0-1 for 0 being totallty transparent, and 1 totally none
      // min is 0.0039 (when using floor function and 255 as molorModes for colors)
      float fade_percent= 0.005; //0.05 = 5%

      int r_new = floor(float(r) - (float(r) * fade_percent));
      int g_new = floor(float(g) - (float(g) * fade_percent));
      int b_new = floor(float(b) - (float(b) * fade_percent));
      //maybe later rewrite in a way to save what the difference is and round it differently, like maybe faster at first and slow later, 
       //round doesn't work because it never first subtracts one to get the ball rolling
      //floor has a minimum of always subtracting 1 from each value each time. cant just subtract 1 ever n loops
      //keep a list of all the pixel as floats? too much memory?
      //ill stick with floor for now
      // the lowest percent that will make a difference with floor is 0.0039?... because thats slightly more than 1/255

      //shift back and or together
      p = 0xFF000000 | (r_new << 16) | (g_new << 8) | b_new; // or-ing all the new hex together back into AARRGGBB

      f.pixels[i] = p;
      ////////pixels now copied
  }
  f.updatePixels(); 

}

Upvotes: 0

Views: 281

Answers (2)

An easier one, and works like a charm:

add f.beginDraw(); before and f.endDraw(); after using f:

loadPixels();  //load windows pixels. dont need because I am only reading pixels?
  f.loadPixels(); //loads feedback loops pixels
  // Loop through every pixel in window
  //it is faster to grab data from pixels[] array, so dont use get and set, use this

  f.beginDraw();

and

f.updatePixels(); 
  f.endDraw();

Processing must know when it's drawing in a buffer and when not.

In this image you can see that works

Upvotes: 0

Kevin Workman
Kevin Workman

Reputation: 42176

This is a weird one. But let's start with a simpler MCVE that isolates the problem:

PGraphics f;
void setup() {
  size(500, 500, P2D);
  f = createGraphics(width, height, P2D);
}

void draw() {
  background(0);
  rect(mouseX, mouseY, 100, 100);
  copyPixels(f); 
  image(f, 0, 0);
}

void copyPixels(PGraphics f) {
  loadPixels();
  f.loadPixels(); 

  for (int i = 0; i < pixels.length; i++) {
    color p = pixels[i];
    f.pixels[i] = p;
  }
  f.updatePixels();
}

This code exhibits the same problem as your code, without any of the extra logic. I would expect this code to show a rectangle wherever the mouse is, but instead it shows a rectangle at a position reflected over the X axis. If the mouse is on the top of the window, the rectangle is at the bottom of the window, and vice-versa.

I think this is caused by the P2D renderer being OpenGL, which has an inversed Y axis (0 is at the bottom instead of the top). So it seems like when you copy the pixels over, it's going from screen space to OpenGL space... or something. That definitely seems buggy though.

For now, there are two things that seem to fix the problem. First, you could just use the default renderer instead of P2D. That seems to fix the problem.

Or you could get rid of the for loop inside the copyPixels() function and just do f.pixels = pixels; for now. That also seems to fix the problem, but again it feels pretty buggy.

If somebody else (paging George) doesn't come along with a better explanation by tomorrow, I'd file a bug on Processing's GitHub. (I can do that for you if you want.)

Edit: I've filed an issue here, so hopefully we'll hear back from a developer in the next few days.

Edit Two: Looks like a fix has been implemented and should be available in the next release of Processing. If you need it now, you can always build Processing from source.

Upvotes: 0

Related Questions