[SOLVED] Running a sketch at a low resolution in fullscreen

Hello everyone,

Is there a good way to run a sketch at a low resolution, say with size(640, 480), but then run that low resolution size at a full screen size? In addition it would be important to be able to run on a second monitor. I’ve created a sketch that eats up frames at high resolutions, and I’m wondering if there is a way to still get a fullscreen display but process less pixels.

Thank you.

Here is the sketch in case that helps:

PShape ellipse;  // The PShape object
float scale = 1;
int[] lfoIncrement =  {1, 1, 1, 1};

//true feedback variables

PImage[] feedbackBuffer = new PImage[100];
int feedbackCount = 1;
int feedbackMax;

//i'm adding this now
int feedbackDelay = 10;
float feedbackLevel = 0;
float addBright = 0;

void setup() {
  //fullScreen(P2D);
  size(640, 480, P2D);

  for (int i = 0; i < feedbackBuffer.length; i++) {
    feedbackBuffer[i] = createImage(width, height, RGB);
  }

  //feedbackDelay = feedbackBuffer.length;
}

void draw() {
  background(0);

  ellipse = createShape(ELLIPSE, 0, 0, width, height);
  //ellipse.translate(width/2, height/2);
  ellipse.setStroke(color(255, 255, 255, 255));
  ellipse.setFill(color(0, 0, 0, 0));

  feedbackDisplay(feedbackBuffer[feedbackCount]);

  feedbackLevel = map(mouseX, 0, width, 0, 1);
  feedbackDelay = int(map(mouseY, 0, height, 1, 100));
  //int size = int(map(mouseY, 0, height, 0, height)) * 2;
  scale = lfo(scale, 0.01, 1);
  ellipse.scale(scale * 2);
  int rotate = 0;
  ellipse.rotateZ(radians(rotate));
  rotate = (rotate + 2) % 360;

  shape(ellipse, width/2, height/2);
  shape(ellipse, 0, 0);
  shape(ellipse, width, 0);
  shape(ellipse, 0, height);
  shape(ellipse, width , height);

  println(frameRate);

  feedbackCapture(feedbackBuffer[feedbackCount]);
  feedbackCount = ((feedbackCount + 1) % feedbackDelay);
}

//Feedback Functions

void feedbackDisplay(PImage output) {

  tint(255, (255 * (feedbackLevel + addBright))); 
  image(output, 0, 0);
}


void feedbackCapture(PImage output) {
  loadPixels();
  output.loadPixels();

  for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {

      int location = x + (y * width);

      output.pixels[location] = pixels[location];
    }
  }

  output.updatePixels();
  updatePixels();
}

float lfo(float input, float speed, int lfoNumber) {
 
 if(input >= 1) {
   lfoIncrement[lfoNumber] = -1;
 } else if(input <= -1) {
   lfoIncrement[lfoNumber] = 1;
 }
 
 input = input + (lfoIncrement[lfoNumber] * speed);
 
 return input;
}
1 Like

You could draw your frames to a PGraphics instance. The createGraphics() function is your friend.

Or you could use the scale() function.

Or you could use the width and height variables to calculate how large your shapes should be.

Ok, it is official. I am starting my new copy&paste message to remember all new users to format their code in this new forum. Everybody is very welcome to use it. Here it goes:

Keyword: kf_keyword _format _greeting


### *Please format your code* :blush:  

It consist on these two steps: 

1. In your code editor (PDE, VS code, Eclipse, etc) ensure you execute the beautifier function. This function automatically indents your code. Auto-indenting makes your code easier to read and helps catching bugs due to mismatch parenthesis, for instance. In the PDE, you use the key combination: `ctrl+t`
2. You copy and paste your code in the forum. Then you select the code and you hit the formatting button aka. the button with this symbol: `</>`  

That's it!  Please notice you do not create a new post in case you need to format something you already posted. You can edit your post, copy the code to the PDE, indent the code properly there and then past it back here, format the code and >> **save** << the edits.

## Extra info:

Formatting your code makes everybody's life easier, your code looks much better plus it ensures your code integrity is not affected by the forum's formatting (Do you know the forum processes markup code?) Please visit the sticky posts or the FAQ section/post to learn about this, other advantages and super powers you can get in this brand new forum. 

Kf
5 Likes

Would you use createGraphics like this?

PGraphics pg;

void setup() {
  //size(200, 200);
  fullScreen();
  pg = createGraphics(100, 100);
}

void draw() {
  pg.beginDraw();
  pg.background(102);
  pg.stroke(255);
  pg.ellipse(0, 0, 100, 100);
  pg.endDraw();
  image(pg, 0, 0, width, height); 
}

that seems to get the desired effect.

Hi
Thanks for formatting your code.

Please check the resize() function in the reference. I believe that is what you need.

If you want to get a section of an image, you can use get() with four parameters. You can find the parameters’ description in the reference. In your case, the strategy will be like this:

PImage mainImage=loadImage("Your_image.jpg");  //Let's say you have a 1000x1000 px image
PImage fragment=mainImage.get(0,0,100,100);  //Gets top left corner
fragment.resize(0,0,width,height);

//Now draw the image
image(fragment,0,0);  //Resizes to your sketch dimensions as defined by size()

I advise to use resize() only when you clip the image. If you are updating your image every 10 seconds, you should call resize() every 10 seconds and not in draw() as it is called 60fps.

Kf

That works as well but if possible, I will resize the image (or PGraphics) only when needed. However, you can find that for your purposes this can be enough.

Related to your multiple displays, check fullScreen() in the reference and also have a look at Most pixels ever, a library by Dan Shiffman that works with multiple displays. I haven’t use it myself so I hope it does what I think it does… You can install it through the Contribution Manage in the PDE.

Kf

Thanks for the idea about using PGraphics. I ended up using that to resize a sketch at an effective resolution of 640 x 480 to fullscreen. Doesn’t look quite as nice, but gets pretty good framerates most of the time. Here is the code in case anyone is interested.

PGraphics pg;
int pgWidth = 640;
int pgHeight = 480;

PShape ellipse;  // The PShape object
//lfo variables
float scale = 1;
float blue = 0;
float red = 0;
float elHei = 1;
float elWi = 1;
int[] lfoIncrement =  {1, 1, 1, 1, 1, 1, 1, 1};

//true feedback variables

PImage[] feedbackBuffer = new PImage[100];
int feedbackCount = 1;
int feedbackMax;

//i'm adding this now
int feedbackDelay = 10;
float feedbackLevel = 0;
float addBright = 0;

void setup() {
  fullScreen(P2D, 2);
  //size(640, 480, P2D);
  pg = createGraphics(pgWidth, pgHeight, P2D);

  for (int i = 0; i < feedbackBuffer.length; i++) {
    feedbackBuffer[i] = createImage(pgWidth, pgHeight, RGB);
  }

  //feedbackDelay = feedbackBuffer.length;
}

void draw() {
  pg.beginDraw();

  pg.background(0);

  elHei = lfo(elHei, pgWidth * 0.3, pgWidth * 2, 0.3, 3);
  elWi = lfo(elWi, pgWidth * 0.2, pgHeight * 2, 0.1, 4);


  ellipse = createShape(ELLIPSE, 0, 0, elWi, elHei);
  //ellipse.translate(width/2, height/2);
  blue = lfo(blue, 0, 255, 0.5, 1);
  red = lfo(red, 0, 50, 0.3, 2);

  ellipse.setStroke(color(red, 0, blue, 255));
  ellipse.setFill(color(0, 0, 0, 0));

  feedbackDisplay(feedbackBuffer[feedbackCount]);

  feedbackLevel = map(mouseX, 0, width, 0, 1);
  feedbackDelay = int(map(mouseY, 0, height, 1, 100));
  //int size = int(map(mouseY, 0, height, 0, height)) * 2;
  scale = lfo(scale, 0, 2, 0.005, 0);
  ellipse.scale(scale);

  pg.shape(ellipse, pgWidth/2, pgHeight/2);
  pg.shape(ellipse, 0, 0);
  pg.shape(ellipse, pgWidth, 0);
  pg.shape(ellipse, 0, pgHeight);
  pg.shape(ellipse, pgWidth, pgHeight);

  println(frameRate);

  feedbackCapture(feedbackBuffer[feedbackCount]);
  feedbackCount = ((feedbackCount + 1) % feedbackDelay);

  pg.endDraw();
  image(pg, 0, 0, width, height);
}

//Feedback Functions

void feedbackDisplay(PImage output) {

  pg.tint(255, (255 * (feedbackLevel + addBright))); 
  pg.image(output, 0, 0, pgWidth, pgHeight);
}


void feedbackCapture(PImage output) {
  //loadPixels();
  pg.loadPixels();
  output.loadPixels();

  for (int x = 0; x < pgWidth; x++) {
    for (int y = 0; y < pgHeight; y++) {

      int location = x + (y * pgWidth);

      output.pixels[location] = pg.pixels[location];
    }
  }

  pg.updatePixels();
  output.updatePixels();
  //updatePixels();
}

float lfo(float input, float minimum, float maximum, float speed, int lfoNumber) {

  if (input >= maximum) {
    lfoIncrement[lfoNumber] = -1;
  } else if (input <= minimum) {
    lfoIncrement[lfoNumber] = 1;
  }

  input = input + (lfoIncrement[lfoNumber] * speed);

  return input;
}
1 Like

Great, thxs for sharing your solution. If you seek improvement with the image resolution, you can open a new post and provide a minimal version of your code showing the detail that you want to improve upon.

Kf

Try not to use PImage::loadPixels with the P2D renderer if you want good framerates. You should normally use createGraphics() for your feedback buffer so that there’s no need to download pixels from the graphics card. Although you might struggle with the number you have.

Likewise, prefer drawing parts of or scaled images using image() over using PImage::get for the same reason.

I did not have any issues with frame rates on my laptop when I ran this code. I still would like to know about the performance difference between PImage.get() and image() in P2D. Could you expand on this please @neilcsmith?

Kf

You might not see issues with framerate, but it will use a lot more CPU than it needs to (which eventually leads to framerate issues as code gets more complex!) Using PImage::get vs image() depends on whether you ever draw the original image and whether you’re doing it more than once. The first time you draw an image using P2D the pixel data is uploaded to a texture on the GPU, and after that drawing it, including scaling it and drawing sections of it are “free” (OK, cheap! :smile:) Calling PImage::get has to copy the pixel data in Java and upload that to another GPU texture - it’s wasting CPU and GPU resources. And if you call PImage::get a lot it’s even worse.

It’s even worse using loadPixels on a PGraphics because that also has to download the pixel array from the GPU, which is slower.

2 Likes

@neilcsmith, given this simplified code below, could you show me how you would substitute the createGraphics(), and PImage.get()?

Also, as a really simple observation, P2D renderer always seems to give me a frame rate performance boost, is there a situation related to this sketch that it would be better to just use the default renderer?

PGraphics pg;
int pgWidth = 640;
int pgHeight = 480;

PShape rectangle;
int position = 0;

//feedback variables

PImage[] feedbackBuffer = new PImage[100];
int feedbackCount = 1;
int feedbackMax;

int feedbackDelay = 1;
float feedbackLevel = 0.9;
float addBright = 0;

void setup() {
  fullScreen(P2D);
  pg = createGraphics(pgWidth, pgHeight, P2D);

  for (int i = 0; i < feedbackBuffer.length; i++) {
    feedbackBuffer[i] = createImage(pgWidth, pgHeight, RGB);
  }

}

void draw() {
  pg.beginDraw();

  pg.background(0);

  rectangle = createShape(RECT, 0, pgHeight / 2, pgWidth / 8, pgHeight / 8);

  rectangle.setStroke(color(255, 255, 255, 255));
  rectangle.setFill(color(0, 0, 0, 0));

  feedbackDisplay(feedbackBuffer[feedbackCount]);

  pg.shape(rectangle, position, 0);
  position = (position + 1) % pgWidth;

  println(frameRate);

  feedbackCapture(feedbackBuffer[feedbackCount]);
  feedbackCount = ((feedbackCount + 1) % feedbackDelay);

  pg.endDraw();
  image(pg, 0, 0, width, height);
}

//Feedback Functions

void feedbackDisplay(PImage output) {

  pg.tint(255, (255 * (feedbackLevel + addBright))); 
  pg.image(output, 0, 0, pgWidth, pgHeight);
}


void feedbackCapture(PImage output) {
  //loadPixels();
  pg.loadPixels();
  output.loadPixels();

  for (int x = 0; x < pgWidth; x++) {
    for (int y = 0; y < pgHeight; y++) {

      int location = x + (y * pgWidth);

      output.pixels[location] = pg.pixels[location];
    }
  }

  pg.updatePixels();
  output.updatePixels();
  //updatePixels();
}

Using an example sketch from this thread, this was the best I could come up with.
I could not eliminate .loadPixels on the PGraphics object, if I do I get a null pointer exception in my feedbackCapture function. This modification doesn’t seem to improve my frame rate performance at all. Any other ideas are welcome!


PGraphics pg;
int pgWidth = 640;
int pgHeight = 480;

PGraphics pgfeedback;

PShape rectangle;
int position = 0;

float feedbackLevel = 0.9;


void setup() {
  fullScreen(P2D, 2);
  pg = createGraphics(pgWidth, pgHeight, P2D);
  pgfeedback = createGraphics(pgWidth, pgHeight, P2D);
}

void draw() {
  pg.beginDraw();

  pg.background(0);

  rectangle = createShape(RECT, 0, pgHeight / 2, pgWidth / 8, pgHeight / 8);

  rectangle.setStroke(color(255, 255, 255, 255));
  rectangle.setFill(color(0, 0, 0, 0));

  feedbackDisplay(pgfeedback);

  pg.shape(rectangle, position, 0);
  position = (position + 1) % pgWidth;

  println(frameRate);

  feedbackCapture(pgfeedback);

  pg.endDraw();
  image(pg, 0, 0, width, height);
}

//Feedback Functions
void feedbackDisplay(PGraphics output) {
  pg.tint(255, (255 * (feedbackLevel))); 
  pg.image(output, 0, 0, pgWidth, pgHeight);
}


void feedbackCapture(PGraphics output) {
  pg.loadPixels();
  output.loadPixels();

  arrayCopy(pg.pixels, output.pixels);

  output.updatePixels();
}
1 Like

Why not this for your draw method? (I removed the feedback* methods because it’s easier to understand, and odd to only pass in half of the arguments you need to a method)

void draw() {
  pg.beginDraw();

  pg.background(0);

  rectangle = createShape(RECT, 0, pgHeight / 2, pgWidth / 8, pgHeight / 8);

  rectangle.setStroke(color(255, 255, 255, 255));
  rectangle.setFill(color(0, 0, 0, 0));

  pg.tint(255, (255 * (feedbackLevel))); 
  pg.image(pgfeedback, 0, 0, pgWidth, pgHeight);

  pg.shape(rectangle, position, 0);
  position = (position + 1) % pgWidth;

  println(frameRate);
  pg.endDraw();
  
  image(pg, 0, 0, width, height);
  
  pgfeedback.beginDraw();
  pgfeedback.image(pg, 0, 0);
  pgfeedback.endDraw();
  
}
3 Likes

And an alternative way to get the same effect, without one of the PGraphics, although depends where you’re going with this -

void draw() {
  pg.beginDraw();

  pg.background(0,(255-(feedbackLevel * 255)));

  rectangle = createShape(RECT, 0, pgHeight / 2, pgWidth / 8, pgHeight / 8);

  rectangle.setStroke(color(255, 255, 255, 255));
  rectangle.setFill(color(0, 0, 0, 0));

  pg.shape(rectangle, position, 0);
  position = (position + 1) % pgWidth;

  println(frameRate);
  pg.endDraw();
  
  image(pg, 0, 0, width, height);
    
}

1 Like

Thank you. Your first example worked very well, so well in fact I’m not running the sketch at a low resolution anymore!

Also, my odd function with half the arguments was a hold out from a time before PGraphics got involved and I was just using loadPixels() and the PImage I passed in the arguments. I’ll do better next time!

1 Like

Great! Good to know.

The comment about method arguments wasn’t meant to be particularly critical - I understand how you get there - it was just confusing me! :smile:

1 Like

Hola Neil :slightly_smiling_face:

I am trying to understand this better. What you presented here is a way is an alternative to the Image:get() method, is this correct?

When you say:

It’s even worse using loadPixels on a PGraphics because that also has to download the pixel array from the GPU, which is slower.

So you do not recommend this?

In a related example, if I did a sketch which does not have dynamic variables for size, and many PGraphics and I need to render it out at a larger size, while maintaining the same proportions / aspect ratio, would a possible solution be to make a PGraphics object of the ENTIRE program and then scale that up?

Many thanks,

Sure! That’s actually how the video pipeline in PraxisLIVE works under-the-hood. We pass PGraphics of the full resolution through the node graph (wrapping every method so you don’t need to call through g), then scale, rotate, etc. to the final display PApplet. Note that if you want to use mouse input, etc. you may also need to reverse the transformation on the events to be useful!

2 Likes