Can't use image() with SVG library

Processing throws a NullPointerException when I try to output an image to SVG while using image(). Looks like this is a known issue from 2017. There’s an answer there but like the poster, I don’t know how to implement it in the context of Processing.

Here’s a python sketch you can use to see the bug for yourself. If you remove beginRecord() and endRecord() it works fine:

add_library('svg')

def setup():
    size(1000,1000)

def draw():
    beginRecord(SVG, "svgtest.svg")
    
    thisPGraphicsObject = createGraphics(width, height)

    thisPGraphicsObject.beginDraw()
    thisPGraphicsObject.fill(50)
    thisPGraphicsObject.stroke(80)
    thisPGraphicsObject.ellipse(width/2, height/2, 500, 500)
    thisPGraphicsObject.endDraw()
    image(thisPGraphicsObject, 0, 0)
    
    endRecord()

NullPointerException
at org.apache.batik.svggen.ImageHandlerBase64Encoder.encodeImage(Unknown Source)
at org.apache.batik.svggen.ImageHandlerBase64Encoder.handleHREF(Unknown Source)
at org.apache.batik.svggen.ImageHandlerBase64Encoder.handleHREF(Unknown Source)
at org.apache.batik.svggen.DefaultImageHandler.handleImage(Unknown Source)
at org.apache.batik.svggen.SimpleImageHandler.handleImage(Unknown Source)
at org.apache.batik.svggen.SVGGraphics2D.drawImage(Unknown Source)
at org.apache.batik.ext.awt.g2d.AbstractGraphics2D.drawImage(Unknown Source)
at processing.awt.PGraphicsJava2D.imageImpl(PGraphicsJava2D.java:1622)
at processing.core.PGraphics.image(PGraphics.java:3817)
at processing.core.PApplet.image(PApplet.java:12805)
at sun.reflect.GeneratedMethodAccessor57.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.python.core.PyReflectedFunction.call(PyReflectedFunction.java:188)
at org.python.core.PyReflectedFunction.call(PyReflectedFunction.java:206)
at org.python.core.PyObject.call(PyObject.java:534)
at org.python.core.PyObject.call(PyObject.java:540)
at org.python.core.PyMethod.call(PyMethod.java:171)
at org.python.pycode._pyx251.draw$2(star_generator_2.pyde:129)
at org.python.pycode._pyx251.call_function(star_generator_2.pyde)
at org.python.core.PyTableCode.call(PyTableCode.java:171)
at org.python.core.PyBaseCode.call(PyBaseCode.java:125)
at org.python.core.PyFunction.call(PyFunction.java:403)
at org.python.core.PyFunction.call(PyFunction.java:398)
at jycessing.PAppletJythonDriver.draw(PAppletJythonDriver.java:1370)
at processing.core.PApplet.handleDraw(PApplet.java:2475)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:866)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:759)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:452)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)

Okay, this solution is kinda hacky –

SVG is a markup-based language, so perhaps you put in some sort of ‘placeholder’ shape, then open the SVG with any code/text editor and replace the placeholder code with an <image> tag?

For example, run this code, then open the SVG and follow the Console output instructions:

add_library('svg')

size(1000,1000)

beginRecord(SVG, 'svgtest.svg')
fill(50)
stroke(80)
ellipse(width/2, height/2, 500, 500)

fill('#FF0000')
noStroke()
x=10; y=10; w=200; h=200
rect(x, y, w, h)

print('open the svg in a code editor and replace this:')
print('<rect x="%s" y="%s" width="%s" style="fill:red; stroke:none;" height="%s"' % (x,y,w,h) )
print('with is:')
print('<image xlink:href="img.png" x="%s" y="%s" width="%s" height="%s"' % (x,y,w,h) )

endRecord()

Of course, you’ll need to replace the xlink:href="img.png" attribute with the relevant image path/filename.

You could even take this a step further and include some Python code – after the endRecord() – that does the find-replace job automatically. Somebody else may have a better solution, but hopefully, that helps you for now.

Realizing this might be a different issue than I first thought.

https://processing.org/reference/libraries/svg/index.html
The above says " * Many methods, particularly pixel-based methods, don’t make sense for SVG renderers. This includes: loadPixels, updatePixels, get, set, mask, filter, copy, blend, save, and image"

Emphasize on “and image”.

Uploaded here is an example of the kind of thing I’m creating. I’m drawing a tesselation to a PGraphics object, drawing a shape to another PGraphic then using it as a mask to cut out a selection of that tesselation in that shape. I then take my ‘cut out’ tesselation and use image to paste it repeatedly, rotated around a circle to create the image here.

My understanding now is that every time I use image I’m effectively converting shape data into pixel data which SVG can’t understand. So, how do I create the same effect while still being able to make an SVG of the combined image? An SVG is important because I’m exporting this to a CNC machine to use as toolpaths that will cut material.

1 Like

Nice work @Raums!

I’m afraid that pure Processing can’t mask/cut vector data (please someone correct me if I’m wrong). You might have to use a library like Geomerative.

Here a silly example I was playing with:

add_library('geomerative')

def setup():
    size(400, 400)
    RG.init(this)

def draw():
    background(100)
    translate(width / 2, height / 2)

    r0 = rg_rect(mouseX, mouseY, 100, 100, QUARTER_PI)
    r1 = rg_rect(mouseX, -mouseY, 100, 100, 0)
    r2 = rg_rect(-mouseX, -mouseY, 100, 100, QUARTER_PI / 2)
    r3 = rg_rect(-mouseX, mouseY, 100, 100)
    noFill()
    RG.shape(r0)
    RG.shape(r1)
    RG.shape(r2)
    RG.shape(r3)

    result = RG.intersection(r0, r1)
    result = RG.intersection(result, r2)
    result = RG.intersection(result, r3)
    fill(255)
    RG.shape(result)


def rg_rect(x, y, w, h, rot=0):
    r = RShape.createRectangle(0, 0, w, h)
    r.translate(x - w / 2, y - h / 2)
    r.rotate(rot, RPoint(x, y))
    return r

1 Like