【问题标题】:Draw permanently on PGraphics (Processing)在 PGraphics 上永久绘制(处理)
【发布时间】:2019-09-30 11:02:49
【问题描述】:

我想创建一个画笔,用于使用 Processing 在 PGraphics 元素上绘图。我希望过去的笔触可见。但是,由于 PGraphics 元素是每帧加载的,以前的画笔笔触会立即消失。

然后我的想法是在 setup() 中创建 PGraphics pg,在 void() 中复制它,更改原始图形 pg 并在每一帧更新副本。这会产生 NullPointerException,很可能是因为 pg 是在 setup() 中本地定义的。

这是我目前得到的:

P图形 pg; PFont 字体;

void setup (){
  font = createFont("Pano Bold Kopie.otf", 600);
  size(800, 800, P2D);
  pg = createGraphics(800, 800, P2D);
  pg.beginDraw();
  pg.background(0);
  pg.fill(255);
  pg.textFont(font);
  pg.textSize(400);
  pg.pushMatrix();
  pg.translate(width/2, height/2-140);
  pg.textAlign(CENTER, CENTER);
  pg.text("a", 0 , 0);
  pg.popMatrix();
  pg.endDraw();
}

void draw () {
  copy(pg, 0, 0, width, height, 0, 0, width, height);
  loop();
  int c;

  loadPixels();
  for (int x=0; x<width; x++) {
    for (int y=0; y<height; y++) {
      pg.pixels[mouseX+mouseY*width]=0;
    }
  }
  updatePixels();
}

我还没有尝试实现的最后一个想法是将鼠标触摸的像素附加到列表中,并从该列表中绘制每一帧。但这对我来说似乎相当复杂,因为它可能会导致需要在原始图像之上处理超长数组。所以,我希望有另一种方法!

编辑:我的目标是创建一个涂抹笔刷,因此是一种将图像的一部分区域复制到其他部分的笔刷。

【问题讨论】:

    标签: image-processing processing pgraphics


    【解决方案1】:

    没有必要像那样手动复制像素。 PGraphics class 扩展了 PImage,这意味着您可以简单地使用 image(pg,0,0); 进行渲染。

    你可以做的另一件事是淡化背景的一个老技巧:你可以渲染一个没有笔划的略带不透明的草图大小的矩形,而不是完全清除像素。

    以下是基于您的代码的概念快速证明:

    PFont font;
    PGraphics pg;
    
    void setup (){
      //font = createFont("Pano Bold Kopie.otf", 600);
      font = createFont("Verdana",600);
    
      size(800, 800, P2D);
      // clear main background once
      background(0);
      // prep fading background
      noStroke();
      // black fill with 10/255 transparnecy
      fill(0,10);
    
      pg = createGraphics(800, 800, P2D);
      pg.beginDraw();
      // leave the PGraphics instance transparent
      //pg.background(0);
      pg.fill(255);
      pg.textFont(font);
      pg.textSize(400);
      pg.pushMatrix();
      pg.translate(width/2, height/2-140);
      pg.textAlign(CENTER, CENTER);
      pg.text("a", 0 , 0);
      pg.popMatrix();
      pg.endDraw();
    }
    
    void draw () {
      // test with mouse pressed
      if(mousePressed){
        // slowly fade/clear the background by drawing a slightly opaque rectangle
        rect(0,0,width,height);
      }
      // don't clear the background, render the PGraphics layer directly
      image(pg, mouseX - pg.width / 2, mouseY - pg.height / 2);
    }
    

    如果您按住鼠标,您可以看到淡入淡出效果。 (将透明度更改为 10 以使褪色更快)

    更新要创建涂抹笔刷,您仍然可以对像素进行采样,然后在某种程度上操纵读取的颜色。根据您想要在视觉上实现的效果,有多种方法可以实现涂抹效果。

    这是一个非常粗略的概念证明:

    PFont font;
    PGraphics pg;
    
    int pressX;
    int pressY;
    
    void setup (){
      //font = createFont("Pano Bold Kopie.otf", 600);
      font = createFont("Verdana",600);
    
      size(800, 800, P2D);
      // clear main background once
      background(0);
      // prep fading background
      noStroke();
      // black fill with 10/255 transparnecy
      fill(0,10);
    
      pg = createGraphics(800, 800, JAVA2D);
      pg.beginDraw();
      // leave the PGraphics instance transparent
      //pg.background(0);
      pg.fill(255);
      pg.noStroke();
      pg.textFont(font);
      pg.textSize(400);
      pg.pushMatrix();
      pg.translate(width/2, height/2-140);
      pg.textAlign(CENTER, CENTER);
      pg.text("a", 0 , 0);
      pg.popMatrix();
      pg.endDraw();
    }
    
    void draw () {
      image(pg,0,0);
    }
    
    void mousePressed(){
      pressX = mouseX;
      pressY = mouseY;
    }
    
    void mouseDragged(){
      // sample the colour where mouse was pressed
      color sample = pg.get(pressX,pressY);
      // calculate the distance from where the "smudge" started to where it is
      float distance = dist(pressX,pressY,mouseX,mouseY);
      // map this distance to transparency so the further the distance the less smudge (e.g. short distance, high alpha, large distnace, small alpha)
      float alpha = map(distance,0,30,255,0);
      // map distance to "brush size"
      float size = map(distance,0,30,30,0);
      // extract r,g,b values
      float r = red(sample);
      float g = green(sample);
      float b = blue(sample);
      // set new r,g,b,a values
      pg.beginDraw();
      pg.fill(r,g,b,alpha);
      pg.ellipse(mouseX,mouseY,size,size);
      pg.endDraw();
    }
    

    正如 cmets 所提到的,一种想法是在印刷机上对颜色进行采样,然后使用示例颜色并将其淡化为您从源区域拖出的颜色。这显示了简单地读取单个像素。您可能想尝试采样/读取更多像素(例如矩形或椭圆)。

    另外,上面的代码没有优化。 一些事情可以加快一点,比如读取像素、提取颜色、计算距离等。

    例如:

    void mouseDragged(){
      // sample the colour where mouse was pressed
      color sample = pg.pixels[pressX + (pressY * pg.width)];
      // calculate the distance from where the "smudge" started to where it is (can use manual distance squared if this is too slow)
      float distance = dist(pressX,pressY,mouseX,mouseY);
      // map this distance to transparency so the further the distance the less smudge (e.g. short distance, high alpha, large distnace, small alpha)
      float alpha = map(distance,0,30,255,0);
      // map distance to "brush size"
      float size = map(distance,0,30,30,0);
      // extract r,g,b values
      int r = (sample >> 16) & 0xFF; // Like red(), but faster
      int g = (sample >> 8) & 0xFF;
      int b =  sample & 0xFF;
      // set new r,g,b,a values
      pg.beginDraw();
      pg.fill(r,g,b,alpha);
      pg.ellipse(mouseX,mouseY,size,size);
      pg.endDraw();
    }
    

    我们的想法是从清晰、易读的代码开始,只有在需要时才考虑优化。

    【讨论】:

    • 感谢您的回复!我意识到我错过了问题大纲中的一个重要方面。我正在尝试创建一个涂抹刷,因此是一种将图像的一部分区域复制到其他部分的刷子。这也是我开始手动复制像素的原因。所以不透明度在任何情况下都是一个有用的提示,但如果我想弄脏字母,我还需要阻止字母本身一次又一次地渲染。给您带来的不便深表歉意!
    • 我已经用一种基本的方法更新了我上面的答案,比如涂抹刷。希望这能让您了解如何更进一步。
    • 谢谢,这非常有帮助!我认为在创建我心目中的画笔之前,还有很长的路要走优化和学习,但你的建议是一个很好的开始!
    • 很高兴为您提供帮助,听起来很有趣!虽然这可能有点矫枉过正,但我​​也会看看 Gimp 或其他开源程序是如何实现涂抹的。有点模糊也可能有帮助。有很多乐趣可以做:) 享受!
    猜你喜欢
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多