cansik/processing-postfx

Is it possible to only affect a PGraphics Layer?

cyrillkuhlmann opened this issue · 8 comments

I have this code here where ellipses are drawn on a PGraphics layer. The PGraphics layer is not displayed through draw, but is redrawn by a grid of ellipses - later in draw. Now I want to blur the PGraphics layer so that the "blurred PGraphics" layer is redrawn by the grid. Until now I only managed to blur the whole sketch... Is it possible to blur a PGraphics and then store it this way in the memory?

This is my code for a better understanding:

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;



PGraphics pg;

float size;
float pixel;

ArrayList<Circle> circles;
void setup () {

  size(600, 600, P2D);
  pixel = 50;
  size = width/pixel;
  circles = new ArrayList<Circle>();
  circles.add(new Circle());
  frameRate(30);

  rectMode(CENTER);
  fx = new PostFX(this);
  pg = createGraphics(width, height);
}

void draw() {
  background(0);

  pg.beginDraw();
  pg.background(255);

  for (int i = circles.size()-1; i >= 1; i--) {

    Circle circle = circles.get(i);
    circle.display(pg);
  }
  pg.endDraw();

  //DRAW THE GRID
  for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {

      color c = pg.get(int(size*x), int(size*y));
      float motionsize = map(brightness(c), 0, 255, size, 0);
      pushMatrix();
      translate(size/2, size/2);
      translate(x*size, y*size);
      fill(255);
      noStroke();
      ellipse(0, 0, motionsize, motionsize);
      popMatrix();
    }
  }
  // fx.render()
  //    .blur(10, 10)
  //    .compose();
}
void mousePressed() {
  circles.add(new Circle());
}

class Circle {

  int x = int(random(width));
  int y = int(random(height));

  Circle() {
  }
  void display(PGraphics pg) {
    pg.fill(0);
    pg.noStroke();
    pg.ellipse(x, y, 100, 100);
  }
}

Thank you! Yes i am working with this example right now … but

 fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose();

always displays it too – I want the PGraphics canvas really to be affected by it… so that if I later say
image(canvas, 0, 0);

it shows up blurred…

This is my experiment so far – or am I missing something? The fx.render() function also always display it? Am I right?

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics canvas;

void setup()
{
  size(500, 500, P2D);

  fx = new PostFX(this);  
  canvas = createGraphics(width, height, P2D);
}

void draw()
{
  
  background(0);
  // draw a simple rotating cube around a sphere
  canvas.beginDraw();
  canvas.background(0);



  canvas.noStroke();
  canvas.fill(255);
  canvas.ellipse(width/2, height/2, 200, 200);
  canvas.endDraw();
  
// image(canvas, 0, 0);

// add bloom filter

  fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose();

  
}

+++ EDIT +++

I think i did it – this is what I was trying to achieve:

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics canvas;
float size;
float pixel;

void setup()
{
  size(500, 500, P2D);
  
    pixel = 20;
  size = width/pixel;

  fx = new PostFX(this);  
  canvas = createGraphics(width, height, P2D);

    rectMode(CENTER);
}

void draw()
{
  
  background(0);
  // draw a simple rotating cube around a sphere
  canvas.beginDraw();




  canvas.noStroke();
  canvas.fill(255);
  canvas.ellipse(width/2, height/2, 200, 200);
  canvas.endDraw();
  
// blendMode(BLEND);

    fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose(canvas);
    
    for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {

      color c = canvas.get(int(size*x), int(size*y));
      float motionsize = map(brightness(c), 0, 255, 0, size);
      pushMatrix();
      translate(x*size, y*size);
      rotate(radians(45));
      fill(255);
      noStroke();
      ellipse(0, 0, motionsize, motionsize);
      popMatrix();
    }
  }

    
  
}

The thing was to write the PGraphics name into compose(); – is that correct?

Render does not display anything, it's the compose method. And as documented (not with an example), it is possible to compose the buffer onto something else than the main-canvas:

  fx.render(pg)
    .blur(10, 10)
    .compose(pg);

You would do this after drawing your mask pg.
But there are several other issues with your sketch:

First of all, I would recommend to only use P2D specific PGraphics objects, otherwise you are mixing Java2D and P2D:

// set the mode of this PGraphics object to P2D in setup
pg = createGraphics(width, height, P2D);

Second, never use get(x, y) to read pixel values from an image. It is slow, even slower if you deal with framebuffers. Always try to load the framebuffer into CPU space once (pg.loadPixels()) and then use the pixels array which is available:

// load pixels before loop
pg.loadPixels();
for (int y = 0; y < pixel; y++) {
  for (int x = 0; x < pixel; x++) {

    // access pixels through the pixels array by using index access
    int ix = int(size*x);
    int iy = int(size*y);
    color c = pg.pixels[iy * width + ix];
Here the full sketch, running with 30 FPS.
import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics pg;
PGraphics pgResult;

float size;
float pixel;

ArrayList<Circle> circles;
void setup () {

  size(600, 600, P2D);
  pixel = 50;
  size = width/pixel;
  circles = new ArrayList<Circle>();
  circles.add(new Circle());
  frameRate(30);

  rectMode(CENTER);
  fx = new PostFX(this);
  pg = createGraphics(width, height, P2D);
}

void draw() {
  background(0);

  pg.beginDraw();
  pg.background(255);

  for (int i = circles.size()-1; i >= 1; i--) {

    Circle circle = circles.get(i);
    circle.display(pg);
  }

  pg.endDraw();

  fx.render(pg)
    .blur(10, 10)
    .compose(pg);


  //DRAW THE GRID
  pg.loadPixels();
  for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {
      int ix = int(size*x);
      int iy = int(size*y);
      color c = pg.pixels[iy * width + ix];
      float motionsize = map(brightness(c), 0, 255, size, 0);
      pushMatrix();
      translate(size/2, size/2);
      translate(x*size, y*size);
      fill(255);
      noStroke();
      ellipse(0, 0, motionsize, motionsize);
      popMatrix();
    }
  }

  surface.setTitle("FPS: " + frameRate);
}

void mousePressed() {
  circles.add(new Circle());
}

class Circle {

  int x = int(random(width));
  int y = int(random(height));

  Circle() {
  }
  void display(PGraphics pg) {
    pg.fill(0);
    pg.noStroke();
    pg.ellipse(x, y, 100, 100);
  }
}

And last but not least, what you are trying to do is using an image mask to mask out another image. Try to use blendMode operations to do that, it is way faster.

WOW! Thank you so much! This helps a lot… Also with all the other explanations! I did not know that get() is this slow…
I have one more little question according… I know that:

pixels[x+y * width] (or in our case):

      int ix = int(size*x);
      int iy = int(size*y);
      color c = pg.pixels[iy * width + ix];

lets us run through each pixel… if my sketch size is squared… like:
size(1080,1080);
but i can not get the math right if it is rectangular … like:
size(1920, 1080);

How do I then have to calculate?

You should ask these questions in the processing forum, because this is not related with my library. But as a hint, you are setting size = width/pixel;, which you should calculate for width & height separately.

Ah yes! This is it... I have less rows than columns – thank you so much!

I am closing this issue as it seems to be solved.