Drawing into a BufferedImage


    Jun 10 2006 | 4:00 pm
    There weren't many mxj examples of how to draw into a BufferedImage,
    so I decided to supply one. Hope it's useful--I'm sure what MaxMSP
    most needs are business charts, so it can compete with Excel.
    -- Paul
    import com.cycling74.max.*;
    import com.cycling74.jitter.*;
    import java.awt.*;
    import javax.swing.*;
    import java.awt.geom.*;
    import java.awt.image.BufferedImage;
    // created by Ignotus, June 9, 2006.
    /**
    * Creates a pie graph in a BufferedImage and then pops it into a
    * JittterMatrix for output.
    * List of numbers in left inlet sets pieValues
    attribute, draws graph
    * and triggers output at left outlet.
    * List of numbers in right inlet sets pieValues attribute.
    * Bang in left inlet triggers output if pieValues attribute
    * contains numeric values.
    * Bang in right inlet outputs pieValues at info outlet.
    */
    public class buf2jmTest extends MaxObject
    {
    private static final String[] INLET_ASSIST = new String[]{
    "Bang or list of numbers", "List of numbers"
    };
    private static final String[] OUTLET_ASSIST = new String[]{
    "Jitter Matrix"
    };
    String useMessage = "Enter a list of positive numbers in inlet 1 or
    2, or set the pieValues attribute.";
    public float[] pieValues = new float[0];
    public buf2jmTest(Atom[] args) {
    declareInlets(new int[]{DataTypes.ALL, DataTypes.ALL});
    declareOutlets(new int[]{DataTypes.ALL});
    setInletAssist(INLET_ASSIST);
    setOutletAssist(OUTLET_ASSIST);
    declareAttribute("pieValues", "pieValues", "setPieValues");
    }
    public void bang() {
    int inletNum = getInlet();
    if ( 0 == inletNum ) {
    if (0 == pieValues.length) {
    post("You must supply a list of positive numbers first, or set
    the pieValues attribute.");
    } else {
    outputPieChart();
    }
    } else {
    Atom[] outputList = Atom.newAtom(pieValues());
    int infoOutletNum = getInfoIdx();
    outlet( infoOutletNum, "list", outputList );
    }
    }
    public void list(Atom[] list) {
    int inletNum = getInlet();
    boolean goodInput = true;
    // check for the right sort of list
    if ( list.length < 1 ) {
    post(useMessage);
    return;
    }
    Atom atom;
    float[] valueList = new float[list.length];
    for ( int i = 0; i < list.length; i++ ) {
    atom = list[i];
    if ( !atom.isInt() && !atom.isFloat() ) {
    goodInput = false;
    break;
    }
    valueList[i] = atom.getFloat();
    if ( valueList[i] < 0 ) {
    goodInput = false;
    break;
    }
    }
    if ( goodInput ) {
    setPieValues(valueList);
    if ( 0 == inletNum ) {
    outputPieChart();
    }
    } else {
    post(useMessage);
    }
    }
    private float[] pieValues() {
    return pieValues;
    }
    private void setPieValues(float[] newPieValues) {
    pieValues = newPieValues;
    }
    public static BufferedImage makeBufferedImage(Image image) {
    BufferedImage bufferedImage = new BufferedImage(
    image.getWidth(null), image.getHeight(null),
    BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = bufferedImage.createGraphics();
    g2.drawImage(image, null, null);
    return bufferedImage;
    }
    public static float sum( float[] values ) {
    float sum = 0.0f;
    for ( int i = 0; i < values.length; i++ ) {
    sum += values[i];
    }
    return sum;
    }
    public BufferedImage bakePieGraph() {
    int imageWidth = 212;
    int imageHeight = 200;
    Color bgColor = new Color(255, 248, 228); // fff8e4
    Color colors [] = {Color.blue,
    Color.green,
    Color.red,
    Color.cyan,
    Color.magenta,
    Color.gray,
    Color.yellow,
    Color.pink,
    Color.orange,
    Color.white,
    new Color(89, 55, 233)};
    Color penColor = Color.black;
    Frame frame = new Frame ();
    // addNotify () is required to create an image (this will call
    Toolkit.getDefaultToolkit ())
    // addNotify "creates the frame's peer, its actual resource in
    whatever native window toolkit you're working on"
    // cf. p. 188 Java 2D Graphics, Jonathon Knudsen, O'Reilly Pub.
    frame.addNotify ();
    Image pieImage = frame.createImage(imageWidth, imageHeight);
    // use a Graphics2D to get better imaging
    Graphics2D gc = (Graphics2D)pieImage.getGraphics ();
    gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
    // fill and outline the background
    Shape bgRect = new Rectangle2D.Double(0, 0, imageWidth, imageHeight);
    gc.setPaint(bgColor);
    gc.fill(bgRect);
    gc.setPaint(new Color(128, 128, 128));
    gc.setStroke(new BasicStroke(2)); // 2 pixel wide stroke
    gc.draw(bgRect);
    // Draw whole pie in background color with a gray drop shadow
    Stroke stroke_1px = new BasicStroke(1); // 1 pixel wide stroke
    gc.setStroke(stroke_1px);
    int inset = 16;
    Shape pie = new Ellipse2D.Double(10, 8, imageHeight - inset,
    imageHeight - inset);
    Shape pieShadow = new Ellipse2D.Double(16, 11, imageHeight -
    inset, imageHeight - inset);
    gc.setPaint(new Color(192, 192, 192));
    gc.fill(pieShadow);
    gc.draw(pieShadow);
    gc.setPaint(bgColor);
    gc.fill(pie);
    gc.setPaint(penColor);
    gc.draw(pie);
    // Compute the sum of all values
    float sum = sum( pieValues() );
    // define an arc shape for slices
    Rectangle2D pieBounds = pie.getBounds2D();
    Arc2D pieArc = new Arc2D.Double(pieBounds, 0, 15, Arc2D.PIE);
    // step pie values and draw pie slices
    // note that graphics state is set to 1 pixel stroke
    for (int accum = 0, i = 0; i < pieValues.length; i++) {
    float answerValue = pieValues[i];
    float startAngle = accum * 360.0f / sum;
    float angle = answerValue * 360.0f / sum;
    // Fill the pie slice for current range
    pieArc.setArc(pieBounds, startAngle, angle, Arc2D.PIE);
    gc.setPaint (colors [i % colors.length]);
    gc.fill(pieArc);
    gc.setPaint(penColor);
    gc.draw(pieArc);
    accum += answerValue;
    }
    return makeBufferedImage(pieImage);
    }
    public void outputPieChart() {
    // the drawing code is probably very low impact, but it's still a
    // good idea to wrap it in a Thread to free up processor time.
    (new Thread() {
    public void run() {
    BufferedImage bim = bakePieGraph();
    JitterMatrix jim = new JitterMatrix(bim);
    outlet(0, "jit_matrix", jim.getName());
    }
    }).start();
    }
    }
    --
    ----- |(*,+,#,=)(#,=,*,+)(=,#,+,*)(+,*,=,#)| -----

    • Feb 03 2007 | 2:31 am
      Wow!
      First time i've copied and pasted anything more complicated than Hello World and it's worked first time! :-) (after a bit of live wrapping)
      Thanks! You're right - there aren't many examples of this!
      jonny
    • Jul 07 2010 | 5:57 pm
      Thanks for posting the code for making pie charts. I know it was many years ago and things might have changed. I tried to use it on the latest version of MAX/MSP/Jitter 5.14 running on a mac and got the following errors after I copied and pasted the code in a new patcher:
      Could not load class 'buf2jmTest'
      Could not load class 'buf2jmTest'
      import: no such object
      import: no such object
      import: no such object
      import: no such object
      import: no such object
      import: no such object
      //: no such object
      June: no such object
      ...etc.
      Would you have any suggestions on how I should proceed to make this work?
      Val
    • Jul 07 2010 | 8:05 pm
      You will need to compile the class. Are you using OSX or windows?
      in the case you are on 10.6.4 here's a compiled version.
      Hope it helps!
    • Jul 07 2010 | 9:34 pm
      Thank you, I am using OSX 10.6.4 and it worked fine. Much appreciated.
      Val