PGraphics Empty with copy() Function

Hi,

I made a Slit-Scan effect in Processing, now I’m trying to export the pdf of the scan effect with only render the new image, and I thought about PGraphics.

The problem is that PGraphics looks empty, I don’t know why copy() function doesn’t work.

import processing.pdf.*;

// Image Array
int num = 4;
int index = 0;
PImage[] img = new PImage[num];

// PGraphics
PGraphics pg;

// Declare variable
int spacing = 20;
int velocity = 1;
int w, h, pos, speed, mouse;
int portion = 3;

// Scale image
float scala = 0.35;

void setup() {
  size(760, 565);
  smooth();
  stroke(236, 38, 140);
  noFill();
  strokeCap(CORNER);
  strokeWeight(portion);

  // Load and scale the image (1500px x 1000px)
  for (int i = 0; i < img.length; i++) {
    img[i] = loadImage("0" + i + ".jpg");
  }

  w = round(img[index].width * scala);
  h = round(img[index].height* scala);

  pg = createGraphics(w, h);

  // Speed and Position
  speed = 0;
  pos   = (spacing*2) + w;
}

void draw() {

  // Change image
  index = constrain(index, 0, img.length);
  if (index == num) index = 0;
  image(img[index], spacing, spacing, w, h);

  // Render 
  pg.beginDraw();
  render(pg);
  pg.endDraw();
  
  image(pg, pos, spacing);
  
}

void render(PGraphics gfx) {
  // Constrain mouse inside the load image
  mouse = constrain(mouseY, spacing, h+spacing);

  // Condition
  boolean cond1 = mouseX > spacing;
  boolean cond2 = mouseX < spacing + w;
  boolean cond3 = mouseY > spacing;
  boolean cond4 = mouseY < spacing + h;

  if (cond1 && cond2 && cond3 && cond4) {
    copy(spacing, mouse, w, portion, pos, spacing+speed, w, 1);
    line(spacing, mouse, spacing+w-1, mouse);
    speed+=velocity;
    if (speed > h) speed = h+100;
  }
}

void keyPressed() {
  if (key == 'q') speed = 0;
  if (keyCode == UP) index++;
  if (keyCode == DOWN) index--;
  if (key == 's') {
    String filename = "out/" + System.currentTimeMillis() + ".pdf";
    PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(pg.width, pg.height, PDF, filename);
    pdf.beginDraw();
    render(pdf);
    pdf.dispose();
    pdf.endDraw();
    println("PDF " + filename + " Saved!");
  }
}

You mean gfx.copy()?

Can you reduce your code to an mcve?

Kf

I already tried add gfx.copy() but it doesn’t work.

Try this approach.

Kf

import processing.pdf.*;

// Image Array
int num = 4;
int index = 0;
PImage[] img = new PImage[num];

// PGraphics
PGraphics pg;

// Declare variable
int spacing = 20;
int velocity = 1;
int w, h, pos, speed, mouse;
int portion = 3;

// Scale image
float scala = 0.35;

void setup() {
  size(760, 565);
  smooth();
  stroke(236, 38, 140);
  noFill();
  strokeCap(CORNER);
  strokeWeight(portion);

  // Load and scale the image (1500px x 1000px)
  for (int i = 0; i < img.length; i++) {
    img[i] = loadImage("pic" + (i+6) + ".jpg");
  }

  w = round(img[index].width * scala);
  h = round(img[index].height* scala);

  pg = createGraphics(w, h);

  // Speed and Position
  speed = 0;
  pos   = (spacing*2) + w;
}

void draw() {

  // Change image
  index = constrain(index, 0, img.length);
  if (index == num) index = 0;
  image(img[index], spacing, spacing, w, h);

  // Render 
  pg.beginDraw();
  render(pg);
  pg.endDraw();

  if (tgrRec) {
    String filename = "out/" + System.currentTimeMillis() + ".pdf";
    PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(pg.width, pg.height, PDF, filename);
    pdf.beginDraw();
    pdf.image(get(),0,0);
    //render(pdf);
    pdf.dispose();
    pdf.endDraw();
    println("PDF " + filename + " Saved!");
    
    tgrRec=false;
  }

  image(pg, pos, spacing);
}

void render(PGraphics gfx) {
  // Constrain mouse inside the load image
  mouse = constrain(mouseY, spacing, h+spacing);

  // Condition
  boolean cond1 = mouseX > spacing;
  boolean cond2 = mouseX < spacing + w;
  boolean cond3 = mouseY > spacing;
  boolean cond4 = mouseY < spacing + h;

  if (cond1 && cond2 && cond3 && cond4) {
    copy(spacing, mouse, w, portion, pos, spacing+speed, w, 1);
    line(spacing, mouse, spacing+w-1, mouse);
    speed+=velocity;
    if (speed > h) speed = h+100;
  }
}


boolean tgrRec=false;

void keyPressed() {
  if (key == 'q') speed = 0;
  if (keyCode == UP) index++;
  if (keyCode == DOWN) index--;

  if (key == 'r' && !tgrRec) tgrRec=true;

  //if (key == 's') {
  //  String filename = "out/" + System.currentTimeMillis() + ".pdf";
  //  PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(pg.width, pg.height, PDF, filename);
  //  pdf.beginDraw();
  //  render(pdf);
  //  pdf.dispose();
  //  pdf.endDraw();
  //  println("PDF " + filename + " Saved!");
  //}
}

It’s works but export the original image, not the PGraphics objects, and the quality isn’t good.

It seems you do not copy anything into your PGraphics because you forget to draw into it
If you see the reference for PImage.copy() it says :
pimg.copy(src, sx, sy, sw, sh, dx, dy, dw, dh)

So by writing copy(spacing, mouse, w, portion, pos, spacing+speed, w, 1); you just drawn on the main graphic context of processing and not on your offscreen buffer.

where pimg is your destination layer and src is the source layer. So I beleive you need to rewrite your render function. Someting like

gfx.copy(img[index], spacing, mouse, w, portion, pos, spacing+speed, w, 1); //gfx is your destination layer and img is your source layer
//line(spacing, mouse, spacing+w-1, mouse); //forget this line, you need to draw this as a debug tool outside of your render function. On the draw loop.

You need to create a small program to explore and understand how the PDF exporter works. The quality of the image might be related to the original size of the PDF PGraphics and the size (or resize) of the source image. Having a small script on the size that focus on only this task is the best thing to do here.

Kf

Hi Alexr,

It’s a good idea, but it doesn’t work yet.
The console doesn’t print any error, but the PGraphics is empty.

in fact you don’t need beginDraw()/endDraw() while using copy(); and if you want to save your slit scan into your PDF you need to draw the destination image into it.

Here a working sample. You just have to replace the loadImage() by your image :

import processing.pdf.*;

PImage src;
PGraphics dst;

void setup() {
  size(1024, 512, P2D);
  src = loadImage("lena.png"); 
  dst = createGraphics(src.width, src.height, P2D);
}

void draw() {
  background(127);

  int posy = mouseY;
  int frameMod = frameCount % dst.height;
  dst.copy(src, 0, posy, src.width, 1, 0, frameMod, dst.width, 1);
  image(src, 0, 0);
  image(dst, 512, 0);
  
  strokeWeight(5);
  stroke(255,0, 0);
  line(0, posy, src.width, posy);
  stroke(0,255, 0);
  line(width/2, frameMod, width, frameMod);
}

void keyPressed() {
  
  String filename = "out/" + System.currentTimeMillis() + ".pdf";
    PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(dst.width, dst.height, PDF, filename);
    pdf.beginDraw();
    pdf.image(dst, 0, 0);
    pdf.dispose();
    pdf.endDraw();
    println("PDF " + filename + " Saved!");
}

Okey, I think It’s works, but I have NullPointerException when the mouse is on the currentImage.

Add P2D for render, and the NullPointerException disappear but the PGraphics is black in preview and render image.

import processing.pdf.*;

// Image Array
int num = 4;
int index = 0;
PImage[] img = new PImage[num];

// PGraphics
PGraphics pg;

// Declare variable
int spacing = 20;
int velocity = 1;
int w, h, pos, speed, mouse;
int portion = 3;

// Scale image
float scala = 0.35;

void setup() {
  size(760, 565);
  smooth();
  stroke(236, 38, 140);
  noFill();
  strokeCap(CORNER);
  strokeWeight(portion);

  // Load and scale the image (1500px x 1000px)
  for (int i = 0; i < img.length; i++) {
    img[i] = loadImage("0" + i + ".jpg");
  }

  w = round(img[index].width * scala);
  h = round(img[index].height* scala);

  pg = createGraphics(w, h);

  // Speed and Position
  speed = 0;
  pos   = (spacing*2) + w;
}

void draw() {

  // Change image
  index = constrain(index, 0, img.length);
  if (index == num) index = 0;
  image(img[index], spacing, spacing, w, h);

  //line(spacing, mouse, spacing+w-1, mouse);

  // Constrain mouse inside the load image
  mouse = constrain(mouseY, spacing, h+spacing);

  // Condition
  boolean cond1 = mouseX > spacing;
  boolean cond2 = mouseX < spacing + w;
  boolean cond3 = mouseY > spacing;
  boolean cond4 = mouseY < spacing + h;

  if (cond1 && cond2 && cond3 && cond4) {
    pg.copy(img[index], spacing, mouse, w, portion, pos, spacing+speed, w, 1);
    speed+=velocity;
    if (speed > h) speed = h+100;
  }
  image(pg, pos, spacing);
}

void keyPressed() {
  if (key == 'q') speed = 0;
  if (keyCode == UP) index++;
  if (keyCode == DOWN) index--;
  if (key == 's') {
    String filename = "out/" + System.currentTimeMillis() + ".pdf";
    PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(pg.width, pg.height, PDF, filename);
    pdf.beginDraw();
    pdf.image(pg, 0, 0);
    pdf.dispose();
    pdf.endDraw();
    println("PDF " + filename + " Saved!");
  }
}

You forgot to set the renderer context of your PGraphics in the setup. It should be like :

 pg = createGraphics(w, h, P2D);

Your copy methods is also false on the area computation. Remember your copy area of the image not of the programm, so you don’t need to offset the area. Here the right one :

 if (cond1 && cond2 && cond3 && cond4) {
    pg.copy(img[index], 0, mouse, img[index].width, 1, 0, speed, pg.width, 1);
    //pg.copy(img[index], spacing, mouse, w, portion, pos, spacing+speed, w, 1);

    speed+=velocity;
    if (speed > h) speed = h+100;
  }

Thank’s man, it works.

I really appreciate your help.

Hi Alexr,

I have a little problem with coordinate offset.

The blue line is where I would pick up the pixel, but the copy function copy a different pixel location.

The PDF render works perfectly, thank’s a lot.

import processing.pdf.*;

// Image Array
int num = 4;
int index = 0;
PImage[] img = new PImage[num];

// PGraphics
PGraphics pg;

// Declare variable
int spacing = 20;
int velocity = 4;
int w, h, pos, speed, mouse, portion;

// Scale image
float scala = 0.5;

void setup() {
  size(1060, 800, P2D);
  smooth();

  stroke(236, 38, 140);
  noFill();
  strokeCap(CORNER);
  strokeWeight(velocity);

  // Load and scale the image (1500px x 1000px)
  for (int i = 0; i < img.length; i++) {
    img[i] = loadImage("0" + i + ".jpg");
  }

  w = round(img[index].width  * scala);
  h = round(img[index].height * scala);

  pg = createGraphics(img[index].width, img[index].height, P2D);

  // Speed and Position
  portion = velocity;
  speed = 0;
  pos   = (spacing*2) + w;
}

void draw() {

  background(120);

  //Change image
  index = constrain(index, 0, img.length);
  if (index == num) index = 0;
  image(img[index], spacing, spacing, w, h);

  // Constrain mouse inside the load image
  mouse = constrain(mouseY, -spacing, (h+spacing)-(portion/2));

  // Condition
  boolean cond1 = mouseX > spacing;
  boolean cond2 = mouseX < spacing + w;
  boolean cond3 = mouseY > spacing;
  boolean cond4 = mouseY < spacing + h;

  stroke(0, 0, 255);
  line(spacing, mouse, spacing+w, mouse);

  if (cond1 && cond2 && cond3 && cond4) {
    pg.copy(img[index], 0, mouse, img[index].width, portion, 0, speed, pg.width, portion);
    speed+=velocity;
    if (speed >= spacing + pg.height) speed = pg.height+1000;
  }
  image(pg, pos, spacing, w, h);
}

void keyPressed() {
  if (key == 'q') speed = 0;
  if (keyCode == UP)   index++;
  if (keyCode == DOWN) index--;
  if (key == 's') {
    String filename = "out/" + System.currentTimeMillis() + ".pdf";
    PGraphicsPDF pdf = (PGraphicsPDF) createGraphics(pg.width, pg.height, PDF, filename);
    pdf.beginDraw();
    pdf.image(pg, 0, 0);
    pdf.dispose();
    pdf.endDraw();
    println("PDF " + filename + " Saved!");
  }
}

// Interesting Effects with 'h' parameter -> Adjust velocity to 4
// pg.copy(img[index], 0, mouseY-spacing, img[index].width, h, 0, speed, pg.width, portion);

You need to take consideration of the scale you’ve applied on the display.
When you copy a portion of an image the area are based on the area of the image, which is not scale down on your program. You only use the ````scala``` varibale for the display. So if you want to retreive the correct position value you will need to divide the mouse position and spacing set on the display by your scaling value in order to have it on the image area size.

Here the correct mouse position on for the copy image :

void draw() {

  background(120);

  //Change image
  index = constrain(index, 0, img.length);
  if (index == num) index = 0;
  image(img[index], spacing, spacing, w, h);

  spacing = 20;
  mouse = mouseY;

  // Condition
  boolean cond1 = mouseX > spacing;
  boolean cond2 = mouseX < spacing + w;
  boolean cond3 = mouseY > spacing;
  boolean cond4 = mouseY < spacing + h;

  portion = 50;
  
  strokeWeight(1);
  stroke(0, 0, 255);
  line(spacing, mouse, spacing+w, mouse);
  line(spacing, mouse + portion * scala, spacing+w, mouse + portion * scala);

  if (cond1 && cond2 && cond3 && cond4) {
    pg.copy(img[index], 0, int(mouse / scala) - int(spacing/scala) , img[index].width, portion, 0, speed, pg.width, portion);
    //speed+=velocity;
    if (speed >= spacing + pg.height) speed = pg.height+1000;
  }
  image(pg, pos, spacing, w, h);
  
  ellipse(spacing, spacing, 10, 10);
  ellipse(spacing + w, spacing+h, 10, 10);
}

As you sse I changed the mouse position as mouseY (we want the position on the screen so we don’t need to constrain it) and recompute this position on the larger image area by this : int(mouse / scala) - int(spacing/scala) on the copy() methods

1 Like

Thank you, I really appreciate your help. I’m a beginner in Processing Environment, and I’m trying to replicate some stuff I found on the internet and learn by doing.

Have a nice day !

It’s one of the best way to learn :wink:
Have fun on our learning journey