Drawing into a BufferedImage
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();
}
}
--
----- |(*,+,#,=)(#,=,*,+)(=,#,+,*)(+,*,=,#)| -----
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
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?
Thank you, I am using OSX 10.6.4 and it worked fine. Much appreciated.
Val