Errors of type "this.g" is null when accessing pure java sketch that extends PApplet as a BufferedImage, output only on 100x100 pixels, other PGraphics objects like size() cannot be used

I am trying to get the image output of processing sketches as a bufferedimage using pure java.
The goal is to use the code output as a complex “texture” for shapes and paths outside processing. So far I have quite promising results, and luckily I do have the output and it is fast.
One issue I am facing by this approach is that the PGraphics object if called is always null because I am not able to use any of the features that depend on it inside the class that extends PApplet.

The strange thing is I get a PERFECT output from processing to pure java only at 100x100 pixels resolution witch is the default size. No other size is possible. There are methods that cannot be used.
I cannot use size() or any other method that needs access to PGraphics. The error message is:

example for colorMode:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
	at processing.core.PApplet.colorMode(PApplet.java:15064)

same applies to size, pushMatrix etc.

The sketches involved has been ported perfectly via extends PApplet and they run as fine and fast as processing IDE.

There is a core class “PixelPaintForProcessing” that implements Paint {} interface

    public Raster getRaster(int xOffset, int yOffset, int w, int h) {

          WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);
//        BufferedImage image = tile(rotateImage(getImage(), rotationAngle)); // optional operators 
     
// simple original image
 BufferedImage image = getImage();

  WritableRaster rasterWr = image.getRaster();  

               return rasterWr;
       } 
    } 
}
//...

and I get the BufferedImage like this:


  private BufferedImage getImage() {       

ProcessingPAppletSketch processingObject = new ProcessingPAppletSketch(); // a simple class that extends PApplet, with slight midifications on numbers, colors etc. over original processing code, same result same speed

// same errors
//processingObject.settings();
//processingObject.size(200, 100);
//processingObject.createGraphics(200, 200, JAVA2D);

processingObject.setup();
processingObject.draw();


BufferedImage image = (BufferedImage) processingObject.img.getNative();

//System.out.println(" PROCESSING.ORG image dimensions X , Y = "+ image.getWidth()+" , "+image.getHeight() );


        return image;
    }

QUESTIONS:

  1. Is there a better way to do this?

  2. Why does the default output image loads OK (only at 100x100 pixels) and every new size does not, leading to because “this.g” is null Exceptions …

  3. If the reason behind this is just the organization and management of methods settings/setup/draw, would then be valuable to have at least an option to bypass this restriction in order to gain access to the PGraphics Object? Can we then just load somehow the PGraphics object and use it, as far as I can see this looks possible otherwise I could not get the 100x100 output. What options do we have?
    For interoperability with pure java it would be great if we could fix (or hack) this odd behavior, or I am just missing a simple link in the chain.

Thanks… any kind of help…will be much appreciated…

All you need are a couple of simple functions to convert PImage to and from BufferedImage.

This sketch first creates a PImage from a graphic file and then converts it to a BufferedImage. To prove the conversion worked it then converts the BufferedImage to a new PImage object. Both the original and recovered PImages are then shown like this

car2

import java.awt.image.BufferedImage;

PImage src, dst;
BufferedImage bimage;

public void setup() {
  size(600, 200);
  src = loadImage("car.gif");
  fill(0);
  textSize(18);
  textAlign(CENTER, CENTER);
  // Convert to BufferedImage
  bimage = pi2bi(src);
  // Recover PImage from Buffered image
  dst = bi2pi(bimage);
}

public void draw() {
  background(200, 200, 255);
  text("Source Image (PImage)", 0, 20, 300, 30);
  image(src, 30, 30);
  text("Recreated from BufferedImage", 300, 20, 300, 30);
  image(dst, 330, 30);
}

// Convert a PImage to a java.awtBufferedImage
public BufferedImage pi2bi(PImage pi) {
  BufferedImage bi = new BufferedImage(pi.width, pi.height, BufferedImage.TYPE_INT_ARGB);
  pi.loadPixels();
  bi.setRGB(0, 0, pi.width, pi.height, pi.pixels, 0, pi.width);
  return bi;
}

// Convert a BufferedImage to a PImage
public PImage bi2pi(BufferedImage bi) {
  int biw = bi.getWidth(), bih = bi.getHeight();
  PImage pi = new PImage(biw, bih, ARGB);
  pi.loadPixels();
  bi.getRGB(0, 0, biw, bih, pi.pixels, 0, biw);
  pi.updatePixels();
  return pi;
}

Thanks for the reply, I have checked this out and it seems that this is not the exact case. But the answer, and code is still useful and close to the point. It seems that the PGraphics object cannot be instantiated the way I try to call it using the image output of the sketch. I will come back with a runnable example.

Here is an experimental runnable example,
There are 2 java files with 2 executable main methods one for a LOCAL implementation of PApplet one for the OUT “output” to the implementation of paint interface .
The PixelPaintForProcessingStandAlone.java file contains the processing sketch code in a separate class, and has a very simple “if” boolean way to switch between a LOCAL implementation of the PApplet window and the OUT implementation that tries to pass the PImage results to a class that implements paint.
ThePanel class acts as a medium jpanel that use paintComponent in order to fill the 2D space with processing PImage… pixels.
all together 3 files → 5 classes.

At PixelPaintForProcessingStandAlone.java
Switch modes true/false at

public boolean enableParametersForLocalProcessing = false; // true=>LOCAL , false=OUT
public boolean enableParametersForOutProcessing = true;  // true=>OUT , false=LOCAL

in order to select LOCAL or OUT mode.
comment or uncomment code after this, in order to see the errors in “public void setup”
//Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException: Cannot invoke “processing.core.PGraphics.colorMode(int, float, float, float, float)” because “this.g” is null

TheTest.java tries to output the code in OUT mode.
PixelPaintForProcessingStandAlone runs the code in LOCAL mode.

There might be useless experimental code in there but the example is fully functional in netbeans by just adding the files. I m not an experienced coder, neither in java or processing, hopefully the code will work ok as a minimal starting point in order to reveal the goal and the errors.

processing sketch code borrowed from:
https://discourse.processing.org/t/psychedelic-noise-in-jrubyart

JAVA FILE 1

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package com.jan.processing.v4.paint;

/* 
* Based on code from:
* A quick example of how to implement java.awt.Paint
 */
// http://asserttrue.blogspot.com/2010/01/how-to-iimplement-custom-paint-in-50.html

//import com.jan.noise.NoiseGenerator;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;


import java.awt.Color;
import java.awt.image.BufferedImage;

import processing.core.PApplet;
import static processing.core.PConstants.JAVA2D;
import processing.core.PGraphics;
import processing.core.PImage;

//PART I: NATIVE JAVA - Call from TheTest java file
public class PixelPaintForProcessingStandAlone implements Paint {

   private AffineTransform transformPixel;

   static final AffineTransform defaultXForm = AffineTransform.getScaleInstance(3.5, 3.5);

   private int imageWidth = 200; //init non final non static
   private int imageHeight = 200;

   // constructor 1
   public PixelPaintForProcessingStandAlone() {
      transformPixel = defaultXForm;

   }
   // constructor 2
   public PixelPaintForProcessingStandAlone(int image_w, int image_h) {
//
      this.imageWidth = image_w;
      this.imageHeight = image_h;

      transformPixel = defaultXForm;
   }

   public PaintContext createContext(ColorModel cm,
           Rectangle deviceBounds,
           Rectangle2D userBounds,
           AffineTransform xform,
           RenderingHints hints) {
      return new Context(cm, xform);
   }

   public int getTransparency() {
      return java.awt.Transparency.TRANSLUCENT;
   }

   class Context implements PaintContext {

      public Context(ColorModel cm_, AffineTransform xform_) {
      }

      public void dispose() {
      }

      public ColorModel getColorModel() {
         return ColorModel.getRGBdefault(); // Or any other color model
      }

      private BufferedImage getImage() {

         SeamlessNoiseOpenPro432548Bi processingObject = new SeamlessNoiseOpenPro432548Bi();

         processingObject.setup();
         processingObject.draw();

//         BufferedImage image = (BufferedImage) processingObject.img.getNative();
         
         
        BufferedImage bufferedimage = processingObject.createBufferedImage();
        
        
        PImage pimage = processingObject.bi2pi(bufferedimage);
        PImage pimage2 = processingObject.img;
        
        
        BufferedImage bufferedimageThroughPimage = processingObject.pi2bi(pimage);


         return bufferedimageThroughPimage;
      }

// FINALY GET RASTER 
      public Raster getRaster(int xOffset, int yOffset, int w, int h) {

//         WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);
         BufferedImage image = getImage();
         WritableRaster rasterWr = image.getRaster();

         return rasterWr;
      } // getRaster()
   } // Context
} //implements Paint class

// PART II - PROCESSING IN PURE JAVA extends PApplet, has a main method to show the applet window


class SeamlessNoiseOpenPro432548Bi extends PApplet {

   PImage img /*= createImage(264, 264, ARGB);*/  ;  // image used to draw the pattern
   PGraphics pg;
   PGraphics pg2;
// BOOLEAN group for LOCAL Applet
//   public boolean enableParametersForLocalProcessing = true;
//   public boolean enableParametersForOutProcessing = false;
// if used then OUT -->
// EXAMPLE ERROR --> Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
// LOCAL -->OK

// BOOLEAN group for EXTERNAL OUTPUT 
public boolean enableParametersForLocalProcessing = false; // true=>LOCAL , false=OUT
public boolean enableParametersForOutProcessing = true;  // true=>OUT , false=LOCAL
// if used then LOCAL window --> EMPTY
// OUT --> OK
//pattern --> if (enableParametersForLocalProcessing) {}
   BufferedImage bi;
   int[] c, c2;  // aray of colors   
   
   
      
   // Convert a PImage to a java.awtBufferedImage
public BufferedImage pi2bi(PImage pi) {
  BufferedImage bi = new BufferedImage(pi.width, pi.height, BufferedImage.TYPE_INT_ARGB);
  pi.loadPixels();
  bi.setRGB(0, 0, pi.width, pi.height, pi.pixels, 0, pi.width);
  return bi;
}

// Convert a BufferedImage to a PImage
public PImage bi2pi(BufferedImage bi) {
  int biw = bi.getWidth(), bih = bi.getHeight();
  PImage pi = new PImage(biw, bih, ARGB);
  pi.loadPixels();
  bi.getRGB(0, 0, biw, bih, pi.pixels, 0, biw);
  pi.updatePixels();
  return pi;
}



   /*-------------*/
   public BufferedImage createBufferedImage() {

      bi = (BufferedImage) img.getNative(); // casting Object to BufferedImage

      return bi;
   }
   


   public void settings() {
      
         size(1200, 1200, JAVA2D);
         smooth(4);
      

      if (enableParametersForLocalProcessing) {

//      PGraphics g2 = new PGraphics();
         size(600, 600, JAVA2D);
         smooth(4);

      }

   }//settings()


   /*-------------*/
   public void setup() {

//Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null       

//         this.colorMode(RGB, 255, 255, 255, 255);
           img = createImage(1 * width, 1 * height / 1, RGB);  // RGB

      if (enableParametersForLocalProcessing) {
//Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null       
//         this.colorMode(RGB, 255, 255, 255, 255);
           img = createImage(1 * width, 1 * height / 1, RGB);  // RGB

      }

//colorMode(ARGB);
      // the dimensions of the image are twice the dimentions of
      // the canvas to add antialiasing when the image is reduced
      if (enableParametersForOutProcessing) {

         //// ARGB ONLY !!!!!!!!!
//         img = createImage(1 * width / 1, 1 * height / 1, ARGB);   // Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Raster IntegerInterleavedRaster: width = 100 height = 6 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0 is incompatible with ColorModel DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
      }

      int lod = (int) map(mouseY, 0, height, 0, random(1, 5f));
      float falloff = map(mouseX, 0, width, 0, random(0, 1f));
      noiseDetail(1, 0.3f);
//  noiseDetail(lod, falloff);
// noise detail is changed to make the pattern smooth
//  noiseDetail(lod, falloff);
      //Noise Detail

      c = new int[255];  // the array has 255 colors, one color for each possible grayscale color
      c2 = new int[255];  // the array has 255 colors, one color for each possible grayscale color

      // fill the array with random colors
      for (int i = 0; i < c.length; i += 1) {
         c[i] = color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
      }
      for (int i = 0; i < c2.length; i += 1) {
         c2[i] = color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
      }

      noLoop();
   }


   /*-------------*/
   float nx, ny, nz;
   float theta = 0;
   float phi = 0;
   float R = random(5, 10), r = random(1, 2);

   public void draw() {

      // create pattern
      img.loadPixels();
//  OpenSimplexNoise osn = new OpenSimplexNoise();
//      NoiseGenerator ngen = new NoiseGenerator(); // custom generator?

      for (int x = 0; x < img.width; x += 1) {
         for (int y = 0; y < img.height; y += 1) {
            // map x and y to angles between 0 and TWO_PI
            theta = map(x, 0, img.width, 0, TWO_PI);
            phi = map(y, 0, img.height, 0, TWO_PI);

            // calculate the parameters of noise with the equation of a torus
            // this is used to make the pattern seamless
//      nx = (R+r*cos(theta))*cos(phi);
//      ny = (R+r*cos(phi))*sin(theta);
            nx = (R + r * cos(phi)) * cos(theta);
            ny = (R + r * cos(phi)) * sin(theta);

            nz = r * sin(phi);

            // normalize noise parameters so that the de pattern has homogeneous dimensions
            nx = norm(nx, 0, R + r);
            ny = norm(ny, 0, R + r);
            nz = norm(nz, 0, r);

//            // apply noise twice and use the equivalent color on the pallete
//            img.pixels[x + y * img.width] = c[floor((float) (16 * noise(floor((float) (200 * ngen.smoothNoise(nx, ny, nz))), 0, 0)))];
//            img.pixels[x + y * img.width] = c2[floor((float) (4 * noise(floor((float) (25 * ngen.smoothNoise(nx, ny, nz))), 0, 0)))];
//
//            
                      // apply noise twice and use the equivalent color on the pallete
            img.pixels[x + y * img.width] = c[floor((float) (16 * noise(floor((float) (200 * noise(nx, ny, nz))), 0, 0)))];
            img.pixels[x + y * img.width] = c2[floor((float) (4 * noise(floor((float) (25 * noise(nx, ny, nz))), 0, 0)))];
            
//      img.pixels[x + y*img.width] = c[floor((float) (25*noise(floor((float) (25*ngen.smoothNoise(nx, ny, nz))), 0, 0)))];
//        image(pg, 50, 50); 
         }
      }
      img.updatePixels(); //? not need

      // display pattern
      if (enableParametersForLocalProcessing) {  
         
         image(img.get(), 0, 0, width, height);  // the image is reduce to the size of the canvas to make it smooth 
      }
      
            if (enableParametersForOutProcessing) {
         
//         image(img.get(), 0, 0, width, height);  // the image is reduce to the size of the canvas to make it smooth 
      }
            

// https://processing.github.io/processing-javadocs/core/index.html?processing/awt/PGraphicsJava2D.html
//Subclass for PGraphics that implements the graphics API using Java2D.
//To get access to the Java 2D "Graphics2D" object for the default renderer, use:
// Graphics2D g2 = (Graphics2D) g.getNative();
//This will let you do Graphics2D calls directly, but is not supported in any way shape or form. Which just means "have fun, but don't complain if it breaks."
      if (enableParametersForLocalProcessing) {
         Graphics2D g2 = (Graphics2D) g.getNative();
         g2.create();
         g2.setColor(new Color(128, 128, 128, 128));

         g2.fillOval(0, 0, 100, 100);
         g2.fillOval(0, 250, 100, 100);

         g2.fillOval(250, 250, 100, 100);
         g2.fillOval(250, 0, 100, 100);

         g2.fillOval(500, 500, 100, 100);
         g2.fillOval(500, 0, 100, 100);

         g2.fillOval(500, 500, 100, 100);
         g2.fillOval(0, 500, 100, 100);

         g2.fillOval(250, 500, 100, 100);
         g2.fillOval(500, 250, 100, 100);

         g2.fillOval(0, 0, 600, 600);
      }

      if (enableParametersForLocalProcessing) {

         PGraphics pg = createGraphics(300, 300, JAVA2D);//  global  
         pg.beginDraw();
         pg.background(100, 100, 100, 128);
         pg.stroke(255);
         pg.line(pg.width * 0.5f, pg.height * 0.5f, mouseX, mouseY);
         pg.endDraw();
         image(pg, 150, 150);
      }

   }


   public void mousePressed() {
//noiseSeed(random(0xFFFFFF)); 
// img.set(0, 0, get());
// img.resize(1000, 1000);

      noiseSeed((long) random(0.1f, 1000f));  // edited to work on openProcessing beta
      println(frameRate);
      redraw();
   }


   /*-------------*/
   public void keyPressed() {
//  save("image.png");
   }

   public static void main(String[] args) {
      String[] processingArgs = {"MySketch"};
      SeamlessNoiseOpenPro432548Bi mySketch = new SeamlessNoiseOpenPro432548Bi();
      PApplet.runSketch(processingArgs, mySketch);
   }
}

JAVA FILE 2

package com.jan.processing.v4.paint;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
// MyPanel extends JPanel, which will eventually be placed in a JFrame
public class ThePanel extends JPanel { 
    // custom painting is performed by the paintComponent method
    @Override
    public void paintComponent(Graphics g){
        // clear the previous painting
        super.paintComponent(g);
        // cast Graphics to Graphics2D
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.gray); // sets Graphics2D color
        // draw the rectangle
        
        Rectangle2D rect = new Rectangle2D.Double (0,0,200,200);
//        g2.draw(rect); // drawRect(x-position, y-position, width, height)
//        g2.setColor(Color.blue);
       
        
        
        PixelPaintForProcessingStandAlone pixelPaintForProcessing = new PixelPaintForProcessingStandAlone(200,200);

//               graphics.setPaint(pixelpaint);

 g2.setPaint(pixelPaintForProcessing);
 

//                    graphics.setPaint(color);
               g2.setPaintMode();

                    g2.fill(rect);
        
    } 
}

JAVA FILE 3

package com.jan.processing.v4.paint;
import javax.swing.*;
import java.awt.*;    

public class TheTest { //the Class by which we display our rectangle    
    JFrame f;    
    ThePanel p;    
    public TheTest(){    
        f = new JFrame();    
        // get the content area of Panel.    
        Container c = f.getContentPane();    
        // set the LayoutManager
        c.setLayout(new BorderLayout());        
        p = new ThePanel();    
        // add MyPanel object into container    
        c.add(p); 
        // set the size of the JFrame
        f.setSize(400,400);    
        // make the JFrame visible
        f.setVisible(true);    
        // sets close behavior; EXIT_ON_CLOSE invokes System.exit(0) on closing the JFrame
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
    }       
    
    public static void main(String args[ ]){    
        TheTest t = new TheTest();     
    } 
}

I’m not certain that I understand what you are trying to achieve, but was curious to see if I could combine all three of your files into one and get it to run in the Processing editor. Please see if this output matches what you are achieving in NetBeans.

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */


/*
 * Based on code from:
 * A quick example of how to implement java.awt.Paint
 */
// http://asserttrue.blogspot.com/2010/01/how-to-iimplement-custom-paint-in-50.html

//import com.jan.noise.NoiseGenerator;
import javax.swing.*;
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.Color;
import java.awt.image.BufferedImage;
import processing.core.PApplet;
import static processing.core.PConstants.JAVA2D;
import processing.core.PGraphics;
import processing.core.PImage;

javax.swing.JFrame frame;
java.awt.Canvas canvas;

final int _wndW = 600;
final int _wndH = 600;

//PART I: NATIVE JAVA - Call from TheTest java file
class PixelPaintForProcessingStandAlone implements Paint {

  private AffineTransform transformPixel;

  final AffineTransform defaultXForm = AffineTransform.getScaleInstance(3.5, 3.5);

  private int imageWidth = 200; //init non final non static
  private int imageHeight = 200;

  // constructor 1
  public PixelPaintForProcessingStandAlone() {
    transformPixel = defaultXForm;
  }
  // constructor 2
  public PixelPaintForProcessingStandAlone(int image_w, int image_h) {
    //
    this.imageWidth = image_w;
    this.imageHeight = image_h;

    transformPixel = defaultXForm;
  }

  public PaintContext createContext(ColorModel cm,
    Rectangle deviceBounds,
    Rectangle2D userBounds,
    AffineTransform xform,
    RenderingHints hints) {
    return new Context(cm, xform);
  }

  public int getTransparency() {
    return java.awt.Transparency.TRANSLUCENT;
  }

  class Context implements PaintContext {

    public Context(ColorModel cm_, AffineTransform xform_) {
    }

    public void dispose() {
    }

    public ColorModel getColorModel() {
      return ColorModel.getRGBdefault(); // Or any other color model
    }

    private BufferedImage getImage() {

      SeamlessNoiseOpenPro432548Bi processingObject = new SeamlessNoiseOpenPro432548Bi();

      processingObject.setup();
      processingObject.draw();

      // BufferedImage image = (BufferedImage) processingObject.img.getNative();

      BufferedImage bufferedimage = processingObject.createBufferedImage();

      PImage pimage = processingObject.bi2pi(bufferedimage);
      PImage pimage2 = processingObject.img;

      BufferedImage bufferedimageThroughPimage = processingObject.pi2bi(pimage);

      return bufferedimageThroughPimage;
    }

    // FINALY GET RASTER
    public Raster getRaster(int xOffset, int yOffset, int w, int h) {

      //         WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);
      BufferedImage image = getImage();
      WritableRaster rasterWr = image.getRaster();

      return rasterWr;
    }
  } 
} 

// PART II - PROCESSING IN PURE JAVA extends PApplet, has a main method to show the applet window


class SeamlessNoiseOpenPro432548Bi extends PApplet {

  PImage img /*= createImage(264, 264, ARGB);*/  ;  // image used to draw the pattern
  PGraphics pg;
  PGraphics pg2;
  // BOOLEAN group for LOCAL Applet
  //   public boolean enableParametersForLocalProcessing = true;
  //   public boolean enableParametersForOutProcessing = false;
  // if used then OUT -->
  // EXAMPLE ERROR --> Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
  // LOCAL -->OK

  // BOOLEAN group for EXTERNAL OUTPUT
  public boolean enableParametersForLocalProcessing = false; // true=>LOCAL , false=OUT
  public boolean enableParametersForOutProcessing = true;  // true=>OUT , false=LOCAL
  // if used then LOCAL window --> EMPTY
  // OUT --> OK
  //pattern --> if (enableParametersForLocalProcessing) {}
  BufferedImage bi;
  int[] c, c2;  // aray of colors



  // Convert a PImage to a java.awtBufferedImage
  public BufferedImage pi2bi(PImage pi) {
    BufferedImage bi = new BufferedImage(pi.width, pi.height, BufferedImage.TYPE_INT_ARGB);
    pi.loadPixels();
    bi.setRGB(0, 0, pi.width, pi.height, pi.pixels, 0, pi.width);
    return bi;
  }

  // Convert a BufferedImage to a PImage
  public PImage bi2pi(BufferedImage bi) {
    int biw = bi.getWidth(), bih = bi.getHeight();
    PImage pi = new PImage(biw, bih, ARGB);
    pi.loadPixels();
    bi.getRGB(0, 0, biw, bih, pi.pixels, 0, biw);
    pi.updatePixels();
    return pi;
  }

  /*-------------*/
  public BufferedImage createBufferedImage() {

    bi = (BufferedImage) img.getNative(); // casting Object to BufferedImage

    return bi;
  }

  public void settings() {

    size(1200, 1200, JAVA2D);
    smooth(4);


    if (enableParametersForLocalProcessing) {

      //      PGraphics g2 = new PGraphics();
      size(600, 600, JAVA2D);
      smooth(4);
    }
  }

  /*-------------*/
  public void setup() {

    //Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null

    // this.colorMode(RGB, 255, 255, 255, 255);
    img = createImage(1 * width, 1 * height / 1, RGB);  // RGB

    if (enableParametersForLocalProcessing) {
      //Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
      //         this.colorMode(RGB, 255, 255, 255, 255);
      img = createImage(1 * width, 1 * height / 1, RGB);  // RGB
    }

    //colorMode(ARGB);
    // the dimensions of the image are twice the dimentions of
    // the canvas to add antialiasing when the image is reduced
    if (enableParametersForOutProcessing) {

      //// ARGB ONLY !!!!!!!!!
      //         img = createImage(1 * width / 1, 1 * height / 1, ARGB);   // Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Raster IntegerInterleavedRaster: width = 100 height = 6 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0 is incompatible with ColorModel DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
    }

    int lod = (int) map(mouseY, 0, height, 0, random(1, 5f));
    float falloff = map(mouseX, 0, width, 0, random(0, 1f));
    noiseDetail(1, 0.3f);
    //  noiseDetail(lod, falloff);
    // noise detail is changed to make the pattern smooth
    //  noiseDetail(lod, falloff);
    //Noise Detail

    c = new int[255];  // the array has 255 colors, one color for each possible grayscale color
    c2 = new int[255];  // the array has 255 colors, one color for each possible grayscale color

    // fill the array with random colors
    for (int i = 0; i < c.length; i += 1) {
      c[i] = color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
    }
    for (int i = 0; i < c2.length; i += 1) {
      c2[i] = color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
    }

    noLoop();
  }

  /*-------------*/
  float nx, ny, nz;
  float theta = 0;
  float phi = 0;
  float R = random(5, 10), r = random(1, 2);

  public void draw() {

    // create pattern
    img.loadPixels();
    //  OpenSimplexNoise osn = new OpenSimplexNoise();
    //      NoiseGenerator ngen = new NoiseGenerator(); // custom generator?

    for (int x = 0; x < img.width; x += 1) {
      for (int y = 0; y < img.height; y += 1) {
        // map x and y to angles between 0 and TWO_PI
        theta = map(x, 0, img.width, 0, TWO_PI);
        phi = map(y, 0, img.height, 0, TWO_PI);

        // calculate the parameters of noise with the equation of a torus
        // this is used to make the pattern seamless
        //      nx = (R+r*cos(theta))*cos(phi);
        //      ny = (R+r*cos(phi))*sin(theta);
        nx = (R + r * cos(phi)) * cos(theta);
        ny = (R + r * cos(phi)) * sin(theta);

        nz = r * sin(phi);

        // normalize noise parameters so that the de pattern has homogeneous dimensions
        nx = norm(nx, 0, R + r);
        ny = norm(ny, 0, R + r);
        nz = norm(nz, 0, r);

        // apply noise twice and use the equivalent color on the pallete
        // img.pixels[x + y * img.width] = c[floor((float) (16 * noise(floor((float) (200 * ngen.smoothNoise(nx, ny, nz))), 0, 0)))];
        // img.pixels[x + y * img.width] = c2[floor((float) (4 * noise(floor((float) (25 * ngen.smoothNoise(nx, ny, nz))), 0, 0)))];     
        // apply noise twice and use the equivalent color on the pallete
        img.pixels[x + y * img.width] = c[floor((float) (16 * noise(floor((float) (200 * noise(nx, ny, nz))), 0, 0)))];
        img.pixels[x + y * img.width] = c2[floor((float) (4 * noise(floor((float) (25 * noise(nx, ny, nz))), 0, 0)))];

        //      img.pixels[x + y*img.width] = c[floor((float) (25*noise(floor((float) (25*ngen.smoothNoise(nx, ny, nz))), 0, 0)))];
        //        image(pg, 50, 50);
      }
    }
    img.updatePixels(); //? not need

    // display pattern
    if (enableParametersForLocalProcessing) {

      image(img.get(), 0, 0, width, height);  // the image is reduce to the size of the canvas to make it smooth
    }

    if (enableParametersForOutProcessing) {

      //         image(img.get(), 0, 0, width, height);  // the image is reduce to the size of the canvas to make it smooth
    }


    // https://processing.github.io/processing-javadocs/core/index.html?processing/awt/PGraphicsJava2D.html
    //Subclass for PGraphics that implements the graphics API using Java2D.
    //To get access to the Java 2D "Graphics2D" object for the default renderer, use:
    // Graphics2D g2 = (Graphics2D) g.getNative();
    //This will let you do Graphics2D calls directly, but is not supported in any way shape or form. Which just means "have fun, but don't complain if it breaks."
    if (enableParametersForLocalProcessing) {
      Graphics2D g2 = (Graphics2D) g.getNative();
      g2.create();
      g2.setColor(new Color(128, 128, 128, 128));

      g2.fillOval(0, 0, 100, 100);
      g2.fillOval(0, 250, 100, 100);

      g2.fillOval(250, 250, 100, 100);
      g2.fillOval(250, 0, 100, 100);

      g2.fillOval(500, 500, 100, 100);
      g2.fillOval(500, 0, 100, 100);

      g2.fillOval(500, 500, 100, 100);
      g2.fillOval(0, 500, 100, 100);

      g2.fillOval(250, 500, 100, 100);
      g2.fillOval(500, 250, 100, 100);

      g2.fillOval(0, 0, 600, 600);
    }

    if (enableParametersForLocalProcessing) {

      PGraphics pg = createGraphics(300, 300, JAVA2D);//  global
      pg.beginDraw();
      pg.background(100, 100, 100, 128);
      pg.stroke(255);
      pg.line(pg.width * 0.5f, pg.height * 0.5f, mouseX, mouseY);
      pg.endDraw();
      image(pg, 150, 150);
    }
  }

  public void mousePressed() {
    //noiseSeed(random(0xFFFFFF));
    // img.set(0, 0, get());
    // img.resize(1000, 1000);

    noiseSeed((long) random(0.1f, 1000f));  // edited to work on openProcessing beta
    println(frameRate);
    redraw();
  }

  /*-------------*/
  public void keyPressed() {
    //  save("image.png");
  }

  /*
   public static void main(String[] args) {
   String[] processingArgs = {"MySketch"};
   SeamlessNoiseOpenPro432548Bi mySketch = new SeamlessNoiseOpenPro432548Bi();
   PApplet.runSketch(processingArgs, mySketch);
   }
   */
}

// ================== //


// MyPanel extends JPanel, which will eventually be placed in a JFrame
class ThePanel extends JPanel {
  // custom painting is performed by the paintComponent method
  @Override
    public void paintComponent(Graphics g) {
    // clear the previous painting
    super.paintComponent(g);
    // cast Graphics to Graphics2D
    Graphics2D g2 = (Graphics2D) g;
    g2.setColor(Color.gray); // sets Graphics2D color
    // draw the rectangle

    Rectangle2D rect = new Rectangle2D.Double (0, 0, 200, 200);
    //        g2.draw(rect); // drawRect(x-position, y-position, width, height)
    //        g2.setColor(Color.blue);


    PixelPaintForProcessingStandAlone pixelPaintForProcessing = new PixelPaintForProcessingStandAlone(200, 200);

    //               graphics.setPaint(pixelpaint);

    g2.setPaint(pixelPaintForProcessing);

    //   graphics.setPaint(color);
    g2.setPaintMode();

    g2.fill(rect);
  }
}

// =========================== //


class TheTest { //the Class by which we display our rectangle
  JFrame f;
  ThePanel p;
  public TheTest() {
    f = new JFrame("TheTest Window");
    Container c = f.getContentPane();
    c.setLayout(new BorderLayout());
    p = new ThePanel();
    c.add(p);
    f.setSize(400, 400);
    f.setVisible(true);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

}

void setup() {
  frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
  canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
  frame.setBounds(500, 300, _wndW, _wndH);
  frame.remove(canvas);

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      TheTest t = new TheTest();
    }
  }
  );
}

Test window output:

1 Like

Yes the Test window output is very similar.

It is nice to see that the code can run inside processing IDE, this can useful, I could not imagine that such code would run this way… interesting!

image

The point here is to avoid processing mode and move to pure java in order to use those pixel blocks inside an other java Graphics2D project, so I am looking for a way to get “all pixels” from the PImage of any PApplet sketch out of processing, and construct an other java object that extends the java Paint interface as you can see. Trying to modify parameters for this “output” for example adding the size parameter for the “pixel block” will produce this.g error. So I cannot modify the size of those processing pixel blocks and cannot pass any other parameter to my code that can modify anything other than the default parameters that eventually show the image we have now.

I see that same errors comes out in processing4 b8 IDE… see that I get the same error when trying to open a PGraphics object there.

It seems that I cannot find a way to pass size parameter to the processing pixel block that constructs the image we have here. What to do to manipulate the PGraphics parameteers like size rotate etc?
So now the question is how do we define a working PGraphics object that can reshape the sketch pixel block that is being passed through to the paint component.

All code that is there inside if statement enableParametersForLocalProcessing
that creates a PGraphics object if moved to draw() will produce the error

So even if we use processing IDE with the new “all in one file processing version”, then we get the same error:
at line 188 uncomment →
// this.colorMode(RGB, 255, 255, 255, 255);
we will get this error:

java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
	at processing.core.PApplet.colorMode(PApplet.java:15064)

So the question is how do we open a new Graphics component that is not null?
Or in final analysis, can we manipulate those tiled pixel blocks produced from the sketch, e.g. get them all tiled at a different resolution in our filled jpanel?
How then do we create new PGraphics objects inside the drawing tiles (each tiled block)?
e.g. how to add simple code to the sketch that include new PGraphics objects like this?

         PGraphics pg = createGraphics(300, 300, JAVA2D);//  global  
         pg.beginDraw();
         pg.background(100, 100, 100, 128);
         pg.stroke(255);
         pg.endDraw();
         image(pg, 150, 150);

The following works on my system.

  public void setup() {
    println("SeamlessNoiseOpenPro432548Bi");

    g = new PGraphics2D();
    println("g = ", this.g);

    //Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
    this.colorMode(RGB, 255, 255, 255, 255);
    img = createImage(1 * width, 1 * height / 1, RGB);  // RGB

    if (enableParametersForLocalProcessing) {
      //Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "processing.core.PGraphics.colorMode(int, float, float, float, float)" because "this.g" is null
      this.colorMode(RGB, 255, 255, 255, 255);
      img = createImage(1 * width, 1 * height / 1, RGB);  // RGB
    }

1 Like

!!
this is the magic object, thanks!!
it seems to work as g = new PGraphics; also, I guess for the P2D renderer

I will try to find out how to resize the pixel block in the jpanel, looks like it is in the Paint interface side.
I will select this as a solution and maybe come back in the next obstacle!

After spending hours unsuccessfully trying to change the cell size of a raster, I decided to try a different approach. The following demo is written only for Processing and does not utilize java components. It will create a resizable window with an image grid of variable size using unique images created with a noise generator and saved to the sketch folder. Extreme settings may exceed the pixel buffer and require an increase in size in the source code. It is also possible to overrun the Processing apps memory allocation and require a change of size in Preferences (untested). I have created grids up to 54 rows x 54 cols with a 40,000 buffer size which allows for reasonably fast rendering according to grid size. The noise generator was taken from your example.

/*
 Pixel buffer is second dimension of rgb[][][] array
 Num pixels = image length x width ; If pixel buffer is exceeded, an exception is thrown. 
 For single image, an increase of pixel buffer up to 2,000,000 will accommodate screen size of 1900x1000
 Pixel buffer of 40,000 will normally accommodate up to 54 rows x 54 cols (2916 images)
 */

final int _cols = 16;
final int _rows = 16;
final int _hg = 1; //horizontal gutter (space between rows)
final int _vg = 1; //vertical gutter (space between cols)

PImage[] image = new PImage[_cols*_rows+1];

int[][] ca = new int[_cols*_rows][255];
int[][] ca2 = new int[_cols*_rows][255];

float nx, ny, nz;
float theta = 0;
float phi = 0;
float R = random(5, 10);
float S = random(1, 2);

float r, g, b;
// rgb[image id][pixel buffer][3] => r,g,b pixel colors for image
int[][][] rgb = new int[_rows*_cols][40000][3]; // pixels for image size < 200x200
int id = 0;
int pixelCount = 0;

void loadColorArrays() {
  for (int j = 0; j < _cols*_rows; j++) {
    for (int i = 0; i < 255; i++) {
      ca[j][i]= color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
      ca2[j][i]= color(random(0, 255), random(0, 255), random(0, 255), random(0, 255));
    }
  }
}

void createImageArray() {
  for (int j = 0; j < image.length; j++) {
    image[j] = createImage( width/_rows, height/_cols, RGB);
  }
}

void createImagesFromPixels() {
  for (int k = 0; k < _cols*_rows; k++) {
    for (int x = 0; x < image[k].width; x++) {
      for (int y = 0; y < image[k].height; y++) {
        int loc = x + y * image[k].width;
        image[k].pixels[loc] = color(rgb[k][loc][0], rgb[k][loc][1], rgb[k][loc][2]);
      }
    }
  }
}

void createNoiseGenerators() {
  for (int a = 0; a < _cols*_rows; a++) {
    noiseGenerator(a);
  }
}

void noiseGenerator(int a) {

  image[a].loadPixels();

  for (int x = 0; x < image[a].width; x++) {
    for (int y = 0; y < image[a].height; y++) {
      // map x and y to angles between 0 and TWO_PI
      theta = map(x, 0, image[a].width, 0, TWO_PI);
      phi = map(y, 0, image[a].height, 0, TWO_PI);

      nx = (R + S * cos(phi)) * cos(theta);
      ny = (R + S * cos(phi)) * sin(theta);
      nz = S * sin(phi);

      // normalize noise parameters so that pattern has homogeneous dimensions
      nx = norm(nx, 0, R + r);
      ny = norm(ny, 0, R + r);
      nz = norm(nz, 0, r);

      // apply noise twice and use the equivalent color on the palette
      int loc = x + y * image[a].width;
      image[a].pixels[loc] = ca[a][floor((float) (16 * noise(floor((float) (200 * noise(nx, ny, nz))), 0, 0)))];
      image[a].pixels[loc] = ca2[a][floor((float) (16 * noise(floor((float) (200 * noise(nx, ny, nz))), 0, 0)))];

      float r = red(image[a].pixels[loc]);
      float g = green(image[a].pixels[loc]);
      float b = blue(image[a].pixels[loc]);
      rgb[a][loc][0] = (int)r;
      rgb[a][loc][1] = (int)g;
      rgb[a][loc][2] = (int)b;

      pixelCount++;
    }
  }
  image[a].updatePixels();
  image[a].save("image["+str(a)+"].png");
  println("image :", a);
}

void createImageGrid(int left, int top, int w, int h) {
  for (int k = 0; k < _rows; k++) {
    for (int j = 0; j < _cols; j++) {
      int x = left + j*(w+_vg );
      int y = top + k*(h+_hg);
      image(image[id], x, y, width/_rows, height/_cols);
      id++;
    }
  }
}

void setup() {
  size(600, 600);
  background(0);
  surface.setLocation(300, 100);
  surface.setTitle("Pixel Madness");
  surface.setResizable(true);
  noLoop();

  loadColorArrays();
  createImageArray();
  createNoiseGenerators();
  createImagesFromPixels();
}

void draw() {
  createImageGrid(0, 0, width/_rows, height/_cols);
}

1 Like

Not exactly what I was expecting for as a solution to the main issue that belongs to the sphere of native java components, but surprisingly, this “mod” is … very very interesting in order to see alternative paths. I will try to find out if this approach can actually work inside my messy code.

Additionally I would like to note that the title is a bit misleading now.
A better general title would be:

“Ways to render a processing sketch inside native java 2D paint components”

I have managed to resolve the issue in pure java by using java.awt.TexturePaint. TexturePaint is a simple but robust implementation of Paint interface so it is a good starting point. We get the bufferedimage with the getNative() method (*pi2bi method works also as mentioned before in this discussion) .

“this.g = new PGraphics();” (not the PGraphics2D() for me) as mentioned before is indeed a crucial object. In fact “this” and “this.g” objects are both needed to be used at the same time initializing even the same basic parameters like width and height.
So I will mark the reply as a solution, as it revealed a path to look deeper. Thanks everyone for all those very helpful suggestions.