Creating a Layer class that extends PGraphics

edited August 2016 in How To...

So I tried creating a LayerSystem class to manage a Layer class that extends PGraphics and would also hold other properties like opacity, visible, etc. The only problem is that PGraphics is different from everything else in Processing in the way that its instances are created through createGraphics().

The reason I want Layer to extend PGraphics is to be able to write stuff like layer.beginDraw() instead of having Layer hold a PGraphics object like layer.pgraphics.beginDraw().

I cant simply create new Layer() because it is createGraphics(int, int) that sets everything up and creating a PGraphics and casting it to Layer doesn't work.

Any light?

Answers

  • hm, gotoloop should shed a light

    did you try extends?

  • edited May 2014

    Composition is often better than inheritance, in general... The added level of indirection is nothing, you can name your field with a short name if that's your problem, like layer.p.beginDraw(). After all, the main PGraphics in PApplet is named g...

    If you really want to extend PGraphics or a variant of it, you can do it and have createGraphics() to use it, because it has a renderer parameter, that is not limited to Processing's types, because it uses reflection.

    For example, my P8gGraphicsSVG library extends PGraphicsJava2D and declares a SVG constant: "org.philhosoft.p8g.svg.P8gGraphicsSVG"
    I can then use this P8gGraphicsSVG.SVG parameter in createGraphics():
    size(400, 400, P8gGraphicsSVG.SVG, "Hearts.svg");
    or in beginRecord():
    beginRecord(P8gGraphicsSVG.SVG, "ImageTransform.svg");

  • edited May 2014

    I see how it would be easier to just compose it.

    If I understand it correctly you declared a SVG constant with a path pointing to your P8gGraphicsSVG class itself. That class extends PGraphicsJava2D and all that enabled you to use it as a renderer.

    Now:

    1 - Can a class extending PGraphics be used as a renderer?

    2 - Is it just a matter of declaring a constant pointing to the location of the file?

    3 - Could you write a very basic example???

  • edited May 2014 Answer ✓

    Hmm, GoToLoop should shed a light...

    Well, only after some research, HEHE! Now to the issue at hand:

    For the surprise of many here, PGraphics is an incomplete class which doesn't have the full implementation to do its job! @-)
    Instead, other subclasses of it are tasked for it. That's why we got the selector function createGraphics().
    Its job is to instantiate the correct PGraphics renderer subclass w/ the right parameters!
    Below's a list of the most common PGraphics renderer implementations:

    println(JAVA2D);
    println(P2D);
    println(P3D);
    println(PDF);
    
    exit();
    

    So, rather than directly extend PGraphics, the path of least resistance is pick up 1 of those renderer subclasses! :ar!
    And also implement the rest of the initialization details. Like setParent(), setPath() & setSize(), etc.

    And finally, a Layer blueprint, which extends PGraphicsJava2D. That should get ya started methinks: (*)

    /**
     * JAVA2D Subclass (v1.13)
     * by GoToLoop (2014/May)
     *
     * forum.processing.org/two/discussion/5238/
     * creating-a-layer-class-that-extends-pgraphics
     */
    
    import processing.core.PApplet;
    import processing.core.PGraphicsJava2D;
    
    public class Layer extends PGraphicsJava2D {
      public Layer(int w, int h) {
        final PApplet p = getEnclosingPApplet();
        initialize(w, h, p, p.dataPath(""));
        ignite();
      }
    
      public Layer(int w, int h, PApplet p) {
        initialize(w, h, p, p.dataPath(""));
        ignite();
      }
    
      public Layer(int w, int h, PApplet p, String s) {
        initialize(w, h, p, s);
        ignite();
      }
    
      public void initialize(int w, int h, PApplet p, String s) {
        setParent(p);
        setPrimary(false);
        setPath(s);
        setSize(w, h);
      }
    
      public void ignite() {
        beginDraw();
        smooth(4);
        fill(-1);
        stroke(0);
        endDraw();
      }
    
      protected PApplet getEnclosingPApplet() {
        try {
          return (PApplet) getClass()
            .getDeclaredField("this$0").get(this);
        }
    
        catch (ReflectiveOperationException cause) {
          throw new RuntimeException(cause);
        }
      }
    
      @ Override public String toString() {
        return "Width: " + width + "\t Height: " + height
          + "\nPath:  " + path;
      }
    }
    
    Layer layer;
    int w, h;
    
    void setup() {
      size(800, 600, JAVA2D);
      frameRate(1);
      smooth(4);
      clear();
    
      println(layer = new Layer(int(width*.75), int(height*.75)));
      w = layer.width; 
      h = layer.height;
    
      layer.strokeWeight(3);
    }
    
    void draw() {
      final color c = (color) random(#000000);
    
      if (!online)  frame.setTitle("Frame: " + frameCount
        + "\t\tColor: #" + hex(c, 6));
    
      layer.background(c);
      layer.ellipse(w>>1, h>>1, random(w), random(h));
    
      layer.endDraw();
    
      set(width-w >> 1, height-h >> 1, layer);
    }
    
  • thanks! ;-)

  • @GoToLoop you have definitely answered my initial question and your blueprint works perfectly.

    Thanks @PhiLho, your answer got me started. And thanks to @Chrisir for crying for help! :D

  • edited May 2014

    Glad you all liked it! Although my version isn't 100% compatible w/ the Processing's API! :|
    It doesn't use createGraphics(), neither can be used as a parameter for size() or beginRecord() like @PhiLho's implementation!
    But everything else should work alright. I believe it's exclusively for off-screen buffer after all! 3:-O

    Nevertheless, I've updated the class to v1.11 now. This time it automatically calls beginDraw(), so we don't have to! \m/

    public void ignite() {
      beginDraw();
      smooth(4);
      fill(-1);
      stroke(0);
      endDraw();
    }
    
  • @gotoloop how would you go about setting layer opacity? Would I have to loop the pixels and multiply the alpha or does PGraphicsJAVA2D (or other) have a setting for that?

  • edited May 2014

    A PGraphics, and thus a Layer, starts off as 100% transparent black already.
    We can use clear(); or background(0, 0); to reset to 100% transparent black again! *-:)

    Also, read about the color datatype:
    http://processing.org/reference/color_datatype.html

  • BtW, I've got the following example that sets the alpha value of a squared area: :-bd

    /**
     * Pixels-Based Alpha Rect (v1.1)
     * by GoToLoop (2014/Apr)
     *
     * forum.processing.org/two/discussion/4696/erase-effect
     *
     * forum.processing.org/two/discussion/4825/
     * how-to-create-mask-for-transparency-between-2-pictures
     */
    
    static final int DIAM = 0100, FPS = 10;
    PImage bgMask;
    
    void setup() {
      size(800, 600, JAVA2D);
      frameRate(FPS);
      smooth(4);
      imageMode(CORNER);
    
      bgMask = createImage(width, height, ALPHA);
    
      // fill up bg w/ #000000, which is 100% opaque black:
      java.util.Arrays.fill(bgMask.pixels, #000000);
    }
    
    void draw() {
      // Replace background() w/ your video feed:
      background((color) random(#000000));
    
      // bgMask is the foreground overlay mask:
      image(bgMask, 0, 0);
    }
    
    void mousePressed() {
      // Alpha 0 is 100% transparent.
      // Alpha 0xFF 100% opaque:
      alphaRect(bgMask, mouseButton == LEFT? 0 : 0xFF, DIAM);
    }
    
    void alphaRect(PImage img, color a, int dim) {
      dim >>= 1; // diameter to radius.
      a <<= 030; // move alpha channel to its right octet.
    
      final color[] p = img.pixels;
      final int w = img.width, h = img.height;
    
      final int minX = max(mouseX - dim, 0);
      final int maxX = min(mouseX + dim, w);
    
      final int minY = max(mouseY - dim, 0);
      final int maxY = min(mouseY + dim, h);
    
      for (int row = minY; row != maxY;)
        for (int col = minX, rw = w*row++; col != maxX;) {
          final int idx = rw + col++;
          p[idx] = p[idx] & 0xFFFFFF | a;
        }
    
      img.updatePixels();
    }
    
Sign In or Register to comment.