Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Processing on a server to create images behind the scenes

The way I see most people use Processing is for drawing an image directly onto a screen or webpage on the client side.

How would one use Processing to create an image without a visual canvas, then save this image to a file?

Here are the specific steps I'm interested in:

  1. Someone visits a webpage, which causes the Processing program to start running
  2. The Processing program would work behind the scenes to create an image, then save it to a known filename
  3. The webpage would load the known filename (which only exists after the Processing program is run - so, how can the webpage know to load the image when it's finished?)

I'm assuming that the Processing program is running on a server (which is contrary to how Processing usually works), and the file will be stored on the server. I'm also assuming some code in the Processing program to throttle the number of files that are created - for example, it won't create a new image if an existing image was created within 5 minutes.

like image 311
David Koelle Avatar asked Dec 11 '09 18:12

David Koelle


3 Answers

I've done this, using Processing in a Servlet to render images on the fly. A problem I found is that Processing isn't thread-safe, so I had to create multiple Processing instances and share them in a queue.

Here's a servlet which renders Mandelbrot fractals, to be used by Google Maps as an overlay:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import processing.core.PApplet;

public class Tile extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static LinkedBlockingQueue<PApplet> pQueue = new LinkedBlockingQueue<PApplet>();

    private PApplet createPApplet() {
        PApplet p = new PApplet();
        p.init();
        p.size(256, 256);
        p.noLoop();
        p.textFont(p.createFont("Monospace", 8, true));
        p.stroke(0x22FFFFFF);
        p.colorMode(PApplet.HSB, 256, 1, 1);
        return p;
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        PApplet p;

        if (pQueue.size() == 0) {
            p = createPApplet();
        } else {
            try {
                p = pQueue.take();
            } catch (InterruptedException e) {
                p = createPApplet();
            }
        }

        int zoom = Integer.parseInt(request.getParameter("z"));
        int tileX = Integer.parseInt(request.getParameter("x"));
        int tileY = Integer.parseInt(request.getParameter("y"));
        int tiles = 1 << zoom;

        p.loadPixels();

        final int N = 256;
        //final double inverse_N = 2.0 / 256;
        final double inverse_N = 2.0 / tiles / 256;
        int y = -1;

        while ((++y) < N) {
            double Civ = (double) (y + tileY * 256) * inverse_N - 1.0;
            for (int x = 0; x < N; x++) {
                double Crv = (double) (x + tileX * 256) * inverse_N - 1.5;

                double Zrv = Crv;
                double Ziv = Civ;

                double Trv = Crv * Crv;
                double Tiv = Civ * Civ;

                int i = 256;
                do {
                    Ziv = (Zrv * Ziv) + (Zrv * Ziv) + Civ;
                    Zrv = Trv - Tiv + Crv;

                    Trv = Zrv * Zrv;
                    Tiv = Ziv * Ziv;
                } while (((Trv + Tiv) <= 4.0) && (--i > 0));

                if (i == 0) {
                    p.pixels[x + y * N] = 0x00000000;
                } else {
                    p.pixels[x + y * N] = p.color(256 - i,1,1);
                }
            } // end foreach column
        }
        p.updatePixels();

        // render info
        p.fill(0x22000000);
        p.text("X: " + tileX + "\nY: " + tileY + "\nZ: " + zoom, 1, 13);
        p.fill(0x22FFFFFF);
        p.text("X: " + tileX + "\nY: " + tileY + "\nZ: " + zoom, 0, 12);

        p.line(0, 0, 0, 2);
        p.line(0, 0, 2, 0);
        p.line(255, 255, 255, 253);
        p.line(255, 255, 253, 255);

        // done
        p.loadPixels();
        BufferedImage img = new BufferedImage(256, 256,
                BufferedImage.TYPE_INT_ARGB);
        img.setRGB(0, 0, 256, 256, p.pixels, 0, 256);
        p.draw();

        response.setHeader("Content-Type", "image/png");
        ImageIO.write(img, "PNG", response.getOutputStream());

        try {
            pQueue.put(p);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
like image 133
George Bashi Avatar answered Nov 06 '22 10:11

George Bashi


Processing was originally written for Java if I remember correctly. It was then ported to Javascript. You could use Java to create the image.

like image 28
Ben Shelock Avatar answered Nov 06 '22 10:11

Ben Shelock


You could download the java version of Processing here and use that. Processing is not limited to javascript. As Ben mentions, it started as a java program. The homepage also lists implementations in javascript, clojure, ruby, and scala.

How to integrate this into the rest of your web page depends mostly on your web framework.

like image 1
Peter Recore Avatar answered Nov 06 '22 11:11

Peter Recore