Why do I have to call pg.beginDraw() pg.endDraw(0 on a PGraphics I'm copying, not drawing on?

edited August 2015 in Questions about Code

This first code fails unless you uncomment the spurious canvas.beginDraw() canvas.endDraw().

Why is that needed? I've drawn to it with beginDraw() and endDraw() but I can't copy it unless I call them again.

PGraphics canvas;
PGraphics hidden;
PGraphics temp;

void setup(){
     canvas = createGraphics(400,400);
     hidden = createGraphics(400,400);
     temp = createGraphics(400,400);
     size(400,400);
     canvas.beginDraw();
     canvas.background(100);
     canvas.endDraw();
     hidden.beginDraw();
     hidden.fill(150,200,0);
     hidden.rect(150,150,100,100);
     hidden.endDraw();
}
void draw(){

     image(canvas,0,0);
}  
void keyPressed(){
     if(key == '1'){
    //      canvas.beginDraw();  //uncomment these lines and it works.
   //       canvas.endDraw();
          temp.beginDraw();
          temp.image(canvas,0,0);
          temp.endDraw();
          canvas.beginDraw();
          canvas.image(hidden,0,0);
          canvas.endDraw();
          hidden.beginDraw();
          hidden.image(temp,0,0);
          hidden.endDraw();
     }
}

Answers

  • edited August 2015

    Every PGraphics needs to be initialized w/ beginDraw() at least once.
    We can do that right after createGraphics() within setup() for example.

    After that, endDraw() alone is enough in order to update any modifications on them.
    That is, at least for JAVA2D PGraphics types.... :-\"

  • Canvas was initialized in setup ( line 10, 11, and 12). But in order to copy it one must make the two calls or it doesn't copy whether its been initialized or not.

  • edited August 2015

    Seems like what you wanna accomplish is swapping the contents of canvas & hidden, right?
    For that I think PImage's copy() method is a good strategy:
    https://Processing.org/reference/PImage_copy_.html

    Here's my attempt. Be aware that I didn't test it yet. Might be buggy, sorry: X_X

    static final void swapImages(PImage a, PImage b, PImage tmp) {
      if (tmp == null || tmp.width != a.width || tmp.height != a.height)  tmp = a.get();
    
      tmp.copy(a, 0, 0, a.width, a.height, 0, 0, tmp.width, tmp.height);
      a.copy(b, 0, 0, b.width, b.height, 0, 0, a.width, a.height);
      b.copy(tmp, 0, 0, tmp.width, tmp.height, 0, 0, b.width, b.height);
    }
    
  • edited August 2015

    However, the simplest solution is having a variable that holds current image to display.
    Then switch it between 1st & 2nd images. Check it out: O:-)

    PGraphics canvas, hidden, currBG;
    
    void setup() {
      size(400, 400, JAVA2D);
      noLoop();
    
      canvas = createGraphics(width, height, JAVA2D);
      canvas.beginDraw();
      canvas.smooth(4);
      canvas.background(#FFFF00);
      canvas.endDraw();
    
      hidden = createGraphics(width, height, JAVA2D);
      hidden.beginDraw();
      canvas.smooth(4);
      hidden.background(#00FFFF);
      hidden.endDraw();
    
      currBG = canvas;
    }
    
    void draw() {
      background(currBG);
    }
    
    void mousePressed() {
      currBG = currBG == canvas? hidden : canvas;
      redraw = true;
    }
    
    void keyPressed() {
      mousePressed();
    }
    
  • edited August 2015

    I tried to make the example as simple and to the point as possible. In my real program, there's a bunch of of "hidden" PGraphics. They are undo buffers. My undo and redo functions were acting flakey and I finally traced it to the fact that you can't copy a PGraphic to another PGraphic ( with pg.image(pg2,0,0)) without first calling the beginDraw and endDraw on the image you are going to copy. That seems awfully strange to me and I'm guessing that means there parts of PGraphics I don't get. I'm going to try the copy and see if its different. In my case, it has to be very fast. When the pen touches the tablet or the mouse if first clicked, I have have 6 different layers saving undos before the mouse is dragged. There can be no stutter in the saving undo process.

    But I would still love to know is there a reason one must call beginDraw and endDraw on a PGraphics if they only intend copying it - not drawing on it. It doesn't make sense to me and the only way I found the bug is hotwire a portion of code to display thumbnails of my undo PGraphics. Thats where I saw they weren't copying. And that was only hours of going through the code to make sure indices weren't becoming corrupted.

  • Does PGrahpics have a copy()?

  • edited August 2015

    Here's a more involved example. Also I have to find a better way to do a light stroke. This looks horrible.

    PImage img = null;
    PGraphics []layers;
    PGraphics temp;
    int curLayer = 0;
    int numLayers = 1;
    PGraphics []uLayers;
    int numUndo = 5;
    int curUndo  = 0;
    int undoInstances = 0;
    int numRedo = 5;
    int curRedo = 0;
    int redoInstances = 0;
    int workSize = 600;
    int oX,oY,mX,mY;
    
    void setup() {
         size(workSize,workSize);
         layers = new PGraphics[numLayers];
         uLayers = new PGraphics[numUndo*numLayers];
         for(int i = 0; i < numLayers;i++){
              layers[i] = createGraphics(workSize,workSize);
              layers[i].beginDraw();
              layers[i].background(255);
              layers[i].stroke(0,60);
              layers[i].strokeWeight(25);
              layers[i].endDraw();
    
    
    
         }
         for(int i = 0; i < numLayers *numUndo;i++){     
              uLayers[i] = createGraphics(workSize,workSize);
              uLayers[i].beginDraw();
              uLayers[i].background(0);
              uLayers[i].endDraw();
         }
         temp = createGraphics(workSize,workSize);
         temp.beginDraw();
         temp.endDraw();
    }
    void draw(){
    
              layers[curLayer].beginDraw();
              layers[curLayer].endDraw();          
              image(layers[curLayer], 0,0);  
    } 
    void mousePressed(){
         save_undo();
         oX= mouseX; 
         oY=mouseY;
         for(int i = 0; i < numLayers;i++){
              layers[i].beginDraw();
              layers[i].line(oX,oY,oX,oY);
              layers[i].endDraw();
         }
    }
    void mouseDragged(){
         mX = mouseX; mY = mouseY;
         for(int i = 0; i< numLayers; i++){
              layers[i].beginDraw();
              layers[i].line(oX, oY, mX, mY);    
              layers[i].endDraw();
         }      
         oX = mX; oY = mY;  
    }
    void save_undo(){
        for(int i = 0; i < numLayers;i++){
              layers[i].beginDraw(); // comment these spurious calls out
              layers[i].endDraw();    // and it gets flakey      
              uLayers[i * numUndo +curUndo].beginDraw();
              uLayers[i * numUndo + curUndo].image(layers[i],0,0);
              uLayers[i * numUndo + curUndo].endDraw();
              println( i*numUndo+curUndo);
    
          } 
    
          curUndo++;
          undoInstances++;
          if(undoInstances > numUndo)
               undoInstances = numUndo;
          if(curUndo == numUndo){ 
               curUndo = 0;
          }
    
    }
    void undo(){
    
         if(undoInstances == 0)
              return;
         if(curUndo == 0) 
              curUndo = numUndo;
         for(int i = 0; i < numLayers; i++){
              uLayers[i*numUndo+curUndo-1].beginDraw();
              uLayers[i*numUndo+curUndo-1].endDraw();
              layers[i].beginDraw();
              layers[i].endDraw(); //seems you need to set the pixels
              temp.beginDraw();
              temp.image(layers[i],0,0);
              temp.endDraw();
              layers[i].beginDraw();
              layers[i].image(uLayers[i*numUndo+curUndo-1],0,0);
              layers[i].endDraw();
              uLayers[i*numUndo+curUndo-1].beginDraw();
              uLayers[i*numUndo+curUndo-1].image(temp,0,0);
              uLayers[i*numUndo+curUndo-1].endDraw();      
         }     
         curUndo--; 
         undoInstances--;
         redoInstances++;
         if(redoInstances > numUndo)
              redoInstances = numUndo;
         curRedo++;
         if(curRedo > numUndo)
              curRedo = 0;
    
    }
    void redo(){
         if(redoInstances == 0)
              return;
    
         for(int i = 0; i < numLayers;i++){
              uLayers[i*numUndo + curUndo].beginDraw();
              uLayers[i * numUndo + curUndo].endDraw();
              layers[i].beginDraw();
              layers[i].endDraw();
              temp.beginDraw();
              temp.image(layers[i],0,0);
              temp.endDraw();
              layers[i].beginDraw();
              layers[i].image(uLayers[curUndo],0,0);
              layers[i].endDraw();
              uLayers[i*numUndo+curUndo].beginDraw();
              uLayers[i*numUndo+curUndo].image(temp,0,0);
              uLayers[i*numUndo+curUndo].endDraw();
         }
         redoInstances--;
         undoInstances++;
    
         curUndo++;
         if(curUndo == numUndo) curUndo = 0;
    }
    void keyPressed(){
         switch(key){  
              case 'u':
              case 'U':
                   undo();
                   break; 
              case 'r':
              case 'R':
                   redo();
                   break;
    
         }
    
    } 
    
  • edited August 2015

    Also note, the object of this program is to paint on multiple layers at once. In this example, I set the layer number to one, because it had nothing to do the trouble I was having with the undo functions. Feel free to set it to 5 or 6, and you'll see it draws on all layers the same. That why in the mouse functions I assign mouseX and mouseY to variables. To make sure all layers get the exact same mouse position.

  • About that horrible light stroke. I found this code and modified it. As soon as I figure out everything its doing, I'm sure I'll be able to modify it some more.

    Original code is here :

    http://forum.processing.org/one/topic/controlled-blur-or-edge-detect-effect-using-convolution-kernel.html

    I modified it to paint instead of blur:

    PGraphics test;
    float[][] kernel = {{1,2,1},{2,4,2},{1,2,1}};
    int blurRect = 20;
    float blurRage = dist(blurRect-10,blurRect-10,0,0); //don't change this
    color drawcolor = color(0);
    int brushstr = 1;
    float red ;
    float green ;
    float blue;
    
    void setup(){
        red = red(drawcolor);
        green = green(drawcolor);
      blue = blue(drawcolor);
      test = createGraphics(600,600);
      test.beginDraw();
      test.background(255);
      test.endDraw();
      size(600,600);
    }
    void draw(){
      image(test,0,0);
    }
    void mouseDragged(){
    
       test.beginDraw();
      for(int y = max(mouseY-blurRect-1,1);y<min(mouseY+blurRect+1,height-1);y++){
        for(int x = max(mouseX-blurRect-1,1);x<min(mouseX+blurRect+1,width-1);x++){
          float rsum = 0;
          float gsum = 0;
          float bsum = 0;
          float a = min((dist(mouseX,mouseY,x,y)+5)/blurRage,1);
          for(int h = 0; h<3;h++){
            for(int w = 0; w<3;w++){
              color c = test.get(x+w-1,y+h-1);
              rsum += red(c)*kernel[w][h];
              gsum += green(c)*kernel[w][h];
              bsum += blue(c)*kernel[w][h];
            }  
          }
          color c = test.get(x,y);
          rsum = (red + 10/brushstr * red(c))/((10/brushstr)+1);
          gsum = (green + 10/brushstr * green(c))/((10/brushstr)+1);
          bsum = (blue + 10/brushstr * blue(c))/((10/brushstr)+1);
          rsum = (rsum*(1-a)+red(c)*a);
          gsum = (gsum*(1-a)+green(c)*a);
          bsum = (bsum*(1-a)+blue(c)*a);
          test.set(x,y,color(rsum,gsum,bsum));
        }
      }
      test.endDraw();
    }
    
  • edited August 2015

    I think using shifts instead of the red(), green() and blue() above will also help the appearance of that stroke.

Sign In or Register to comment.