Drawing a buffer to the canvas, while maintaining vectors (instead of rasterizing the buffer)?

Is there a way to 1) make it so a buffer stores vector shape data, and also 2) draw that buffer to the screen later while maintaining vector shape data, instead of rasterizing the contents of the buffer?

Currently, if I create some vector shapes inside a PGraphics buffer, and then use image() to draw that buffer to the main canvas later, the image() call rasterizes the buffer. (Or maybe the PGraphics buffer is raster to begin with? I’m not sure.)

EDIT: Here’s a simple step-by-step example of the behavior and results I’m after:

  1. In the main drawing window, draw a red vector circle.
  2. In a hypothetical offscreen canvas / buffer, draw a green vector triangle, and a small raster image of a cat. Don’t display these in the main drawing window yet.
  3. In the main drawing window, draw a blue vector square.
  4. In the main drawing window, add the contents of the offscreen canvas / buffer from earlier. The green triangle should be drawn as a vector, not pixelated / rasterized. On the other hand, the raster image of the cat will of course remain rasterized.

And then at the end of all the above, I should be able to for example export the contents of the drawing window to a PDF, and the PDF would then consist of a red vector circle, a blue vector square, a green vector triangle (not raster), and a raster image of a cat.

I’m aware that I can use a PShape group to store some vector shapes and draw them later, and that I can do something similar using the Geomerative library. But I’m wondering if there’s a more straightforward way, as outlined above.

Interesting question!

My understanding is that a PGraphic is really just a buffer. So anything drawn will be done so at the resolution defined by the initial constructor parameters.

Like you mentioned before, your best route would be to store your paths in an svg and scaling to the desired size when you want to export.

What exactly are you trying to do?

Nothing in particular, but I’ve run into situations before where it would have been useful to control the draw order of objects by using buffers, at the end of which I’d need to output a vector file. E.g., I’m looping through an array of coordinates and drawing a bunch of interconnected lines among those coords, and then I want to draw a bunch of squares at those same coords, on top of the lines. Looping through the coord array twice just to control the draw order seems inefficient and messy. It’s much cleaner to draw the squares to a buffer at the same time as drawing the lines to the main canvas, then stamp the buffer onto the main canvas later. Stuff like that.

Terminology-wise, does a “buffer” always mean a grid of pixels, in Processing?

Re: storing paths in an SVG, are you saying to write to an SVG file, then read that file back in and draw it to the canvas on a second run-through of draw()? Or are you saying there’s a way to store a temporary SVG-formatted buffer and then draw it?

Would using multiple canvases help?

With canvases you can update it without necessarily viewing it. However getting the pixels does incur computational costs. Also with a canvas you can use shaders instead to speed things up.

Can you point me to some info about using multiple canvases? Or would it be easy to put together a simple example?

EDIT: maybe some of these are relevant … although they looked geared towards p5, and I’m working with the original Java-based Processing: https://forum.processing.org/two/discussions/tagged?Tag=%23multiple+canvas

not sure it would help, as I dont know what you are trying to achieve. Ive used canvases before normally when creating guis as I wanted to disable overflow. Note that I have not explored pShapes as an alternative.

PShapes and PGraphics are nice in the sense that they are defined by width size or shape and you can load the processing standard shape functions such as ellipse line etc. If you had to do calculations a PGraphics isn’t necessarily the best option unless you are using shaders ( I think).

anyways here are some examples of how Ive used PGraphics.

One caveat to PGraphics is you have to specify a renderer and FX2D is not available. if no renderer is specified then the default will be used but note that using different renderers can cause some graphic quality issues.

void displayTab(PGraphics scene) {
    
    tab t = states.get(state);
    
    if (toggle) {
      t.mouse = mouse2;
      mouse = mouse2;
      canvas.beginDraw();
      canvas.background(50);
      logic();
      t.drawBorder();
      t.boundingBox();
      
      t.drawButtons();
      t.drawMenus();
      t.drawTextboxes();
      t.drawTextareas();
      t.drawTextBlocks();
      t.drawSliderBoxes();
    if(t.title!=null&&t.visible){
      t.title.toggle=1;
      t.drawTitle();
      t.drawBorder();
      t.drawDmenu();
    }
      //t.drawImages();
    if(t.scrollable)t.drawSlider();
      //t.drawWindows();
      canvas.endDraw();
      scene.image(canvas,x,y);
    }
  };

as you can see the tab class here already has a canvas of its own, but the function takes another canvas, and then you can merge both together.

I’m pretty sure PShape works in the same way but someone more knowledgeable may have correct me.

Here’s a simple step-by-step example of the behavior and results I’m after:

  1. Draw a red vector circle in the main drawing window.
  2. In a hypothetical magical offscreen canvas / buffer, draw a green vector triangle, and a small raster image of a cat. Don’t display these in the main drawing window yet.
  3. Draw a blue vector square in the main drawing window.
  4. Add the contents of the offscreen canvas from earlier, into the main drawing window. The green triangle should be drawn as a vector, not pixelated / rasterized. On the other hand, the raster image of the cat will of course remain rasterized.

And then at the end of all the above, I should be able to for example export the contents of the drawing window to a PDF, and the PDF would then consist of a red vector circle, a blue vector square, a green vector triangle (not raster), and a raster image of a cat.

You mentioned PShape, and yes, I know that as a workaround I can create a PShape group and add some vector shape children to it, and then draw that group to the main window at a time of my choosing. I know I can also do something similar with the Geomerative library. But I’m wondering if there’s a more straightforward way, as in my magical offscreen canvas example above. I know the very basics of using PGraphics, but not enough to know if they’d work for this purpose, and I poked around your example code without success.

1 Like

give this a try. Its only a demo, but it should give you an understanding of what you can do with canvases.

PGraphics c1,c2;
boolean mdown;
int mode;

void setup(){
  size(800,600);
  c1 = createGraphics(width,height);
  c2 = createGraphics(width,height);
};

void draw(){
  background(0);
  mouse();
  fill(255);
  rect(10,10,100,100);
  
  c1.beginDraw();
  c1.fill(255,0,0);
  c1.rect(10,10,100,100);
  c1.endDraw();
  
  c2.beginDraw();
  c2.fill(255,255,0);
  c2.rect(10,10,100,100);
  c2.endDraw();
  
  if(mode==0){
    image(c1,0,0);
  }else if(mode==1){
    image(c2,0,0);
  }
  fill(255);
  text(mode,10,10);
};

void mouse(){
  if(mousePressed&&!mdown){
    mdown = true;
    mode++;
  }
  if(!mousePressed) {
    mdown = false;
  }
  if(mode>2)mode = 0;
}

it seems like what you want though might instead be threads. These are processes which you can call which run alongside the main thread, and execute in the background. You CANNOT use draw with thread but you can do calculations, and so long as you have variables to track what has been calculated or not, the thread should not revisit the same elements.

Threads do have some limitations though, mainly they can be a bit difficult to implement for beginners and they run asynchronously. meaning if you have more than one thread they’re wont run in the order you want unless you create a synchronized thread.

This will mean that you cannot combine thread and canvas as no draw calls can be made with thread, but you should be able to store as many calculations as you want, and providing a flag is set when done, you can then retrieve the coordinates and raster it wherever you want.

Thanks for the indepth description. If I understand correctly, your ultimate goal is to not lose quality between, say, displaying a 480x360 sketch, then exporting a full-size PDF?

Unfortunately, like you mentioned before, I think your best bet would be to keep track of all your vector graphics, and scale them up appropriately.

When you call save(), there’s not much under the hood; the raw pixels in the canvas are exported into a png. The only person who knows that the pixels on the canvas came from a vector is you.

If I misunderstood let me know

2 Likes