|
An Introduction to 2-D Graphics
in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
The (Linear) Color Cube
JComponent Class:
paint(java.awt.Graphics)
method is called and is passed a rendering engine
JComponent
paint() methodAn Example
import java.awt.*;
import javax.swing.*;
/**
* A concrete extension of a JComponent that illustrates
* the process of obtaining and using a rendering engine
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BoringComponent extends JComponent
{
/**
* Render this BoringComponent
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
// Cast the rendering engine appropriately
g2 = (Graphics2D)g;
// Put the rendering code here
}
}
read() method in the ImageIO
classdrawImage(Image i, int x, int y, null) method
in the Graphics classimport java.awt.*;
import javax.swing.*;
/**
* A concrete extension of a JComponent that illustrates
* the rendering of sampled static visual content
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class ImageComponent extends JComponent
{
private Image image;
/**
* Explicit Value Constructor
*
* @param image The Image to render
*/
public ImageComponent(Image image)
{
this.image = image;
}
/**
* Render this ImageCanvas
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
// Cast the rendering engine appropriately
g2 = (Graphics2D)g;
// Render the image
g2.drawImage(image, // The Image to render
0, // The horizontal coordinate
0, // The vertical coordinate
null); // An ImageObserver
}
}
import java.awt.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
/**
* An example that illustrates the rendering of sampled static visual
* content
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class ImageComponentDriver implements Runnable
{
private String name;
/**
* The entry point of the application.
*
* @param args The command line arguments (args[0] is the file)
*/
public static void main(String[] args) throws Exception
{
String name;
if (args.length == 0) name = "firstsnow.gif";
else name = args[0];
SwingUtilities.invokeAndWait(new ImageComponentDriver(name));
}
/**
* Construtor.
*
* @param name The name of the file to read
*/
public ImageComponentDriver(String name)
{
super();
this.name = name;
}
/**
* The code to run in the event dispatch thread.
*/
public void run()
{
try
{
// Read the Image
Image image = ImageIO.read(new File(name));
// Construct the JComponent
JComponent component = new ImageComponent(image);
// Construct the JFrame and add the JComponent
JFrame frame = new JFrame();
frame.setSize(600,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(component, BorderLayout.CENTER);
frame.setVisible(true);
}
catch (IOException ioe)
{
System.out.println("Unable to load image!");
}
}
}
Shape Objectsimport java.awt.*;
import java.awt.geom.*;
import java.util.Random;
import javax.swing.*;
/**
* A JComponent that renders a Rectangle with randomly
* generated attributes each time it's paint method is called
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class RandomRectangleComponent extends JComponent
{
private Random generator;
/**
* Default Constructor
*/
public RandomRectangleComponent()
{
super();
generator = new Random(System.currentTimeMillis());
}
/**
* Paint this Component
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
int height, maxHeight, maxWidth, width, x, y;
Rectangle rectangle;
g2 = (Graphics2D)g;
maxHeight = getHeight();
maxWidth = getWidth();
x = generator.nextInt(maxWidth - 1);
y = generator.nextInt(maxHeight - 1);
width = generator.nextInt(maxWidth - x - 1);
height = generator.nextInt(maxHeight - y - 1);
rectangle = new Rectangle(x, y, width, height);
g2.draw(rectangle);
}
}
// Construct a quadratic curve
quadraticCurve = new QuadCurve2D.Double(
120.0, 120.0, // End 1
300.0, 180.0, // Control
130.0, 190.0); // End 2
// Construct a cubic curve
cubicCurve = new CubicCurve2D.Double(
320.0, 320.0, // End 1
300.0, 180.0, // Control 1
330.0, 370.0, // End 2
360.0, 390.0); // Control 2
// Construct an arc
arc = new Arc2D.Double(10.0, 200.0, // Upper Left
150.0, // Width
100.0, // Height
0.0, // Starting Angle
135.0, // Angular Extent
Arc2D.PIE); // Type
f and i as two glyphs and as a ligature
The RSB is positive for A and negative for f
Kerning - The Use of Different Bearings for Differnt Glyphs
Line Metrics
import java.awt.*;
import java.awt.font.*;
import javax.swing.*;
/**
* A JComponent that displays a GlyphVector
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class GlyphComponent extends JComponent
{
protected Font font;
protected String text;
/**
* Explicit Value Constructor
*/
public GlyphComponent(String text)
{
super();
setText(text);
// Construct a (logical) font
font = new Font("Serif", Font.ITALIC, 100);
}
/**
* Paint this component
*
* @param g The Graphics context to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
g2 = (Graphics2D)g;
// Set the font
g2.setFont(font);
// Use antialiasing for text and shapes
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Use high-quality rendering
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
// Use floating point for font metrics
g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// Render the glyphs
if (text != null) paintGlyphs(g2, text);
}
/**
* Render a String
*/
protected void paintGlyphs(Graphics2D g2, String text)
{
FontRenderContext frc;
GlyphVector glyphs;
Shape shape;
frc = g2.getFontRenderContext();
glyphs = font.createGlyphVector(frc, text);
shape = glyphs.getOutline(0.0f, 100.0f);
g2.setColor(Color.BLACK);
g2.draw(shape);
}
/**
* Set the text associated with this component
*/
public void setText(String text)
{
this.text = text;
}
}
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* A JComponent that displays a GlyphVector and LineMetrics
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class GlyphMetricsComponent extends GlyphComponent
{
/**
* Explicit Value Constructor
*/
public GlyphMetricsComponent(String text)
{
super(text);
// Construct a (logical) Font
font = new Font("Serif", Font.PLAIN, 100);
}
/**
* Render the glyphs and the metrics
*
* Note: There are better approaches. This method
* is used for illustrative purposes only.
*/
protected void paintGlyphs(Graphics2D g2, String text)
{
FontRenderContext frc;
GlyphVector glyphs;
frc = g2.getFontRenderContext();
glyphs = font.createGlyphVector(frc, text);
LineMetrics lm;
lm = font.getLineMetrics(text, frc);
float ascent, descent, height, leading;
// Get the various metrics
ascent = lm.getAscent();
descent = lm.getDescent();
height = lm.getHeight();
leading = lm.getLeading();
Dimension d;
float baseline, pWidth, y;
Rectangle2D bounds;
Shape shape;
d = getSize();
g2.setColor(Color.BLACK);
baseline = (float)(d.height)/2.0f;
pWidth = (float)(d.width);
// Draw the metrics
y = baseline - ascent;
g2.setColor(Color.BLUE);
g2.draw(new Line2D.Float(0.0f, y, pWidth, y));
y = baseline;
g2.setColor(Color.WHITE);
g2.draw(new Line2D.Float(0.0f, y, pWidth, y));
g2.setColor(Color.RED);
y = baseline + descent;
g2.draw(new Line2D.Float(0.0f, y, pWidth, y));
if (leading > 0)
{
y = baseline + descent + leading;
g2.setColor(Color.GREEN);
g2.draw(new Line2D.Float(0.0f, y, pWidth, y));
}
// Get the outline at (0,baseline)
shape = glyphs.getOutline(0, baseline);
g2.setColor(Color.BLACK);
g2.draw(shape);
g2.fill(shape);
}
}
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* A JPanel that displays a GlyphVector centered
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class CenteredGlyphComponent extends GlyphComponent
{
/**
* Explicit Value Constructor
*/
public CenteredGlyphComponent(String text)
{
super(text);
}
/**
* Render the glyphs centered
*
* Note: There are better approaches. This method
* is used for illustrative purposes only.
*/
protected void paintGlyphs(Graphics2D g2, String text)
{
FontRenderContext frc;
GlyphVector glyphs;
Rectangle2D bounds;
Shape shape;
frc = g2.getFontRenderContext();
glyphs = font.createGlyphVector(frc, text);
g2.setColor(Color.black);
// Get the outline at (0,0)
shape = glyphs.getOutline();
// Get the bounding box
bounds = glyphs.getVisualBounds();
Dimension d;
float x, y;
d = getSize();
// Center the text
x = (float)(d.width/2-bounds.getWidth()/2);
y = (float)(d.height/2+bounds.getHeight()/2);
// Get the outline when centered
shape = glyphs.getOutline(x,y);
g2.draw(shape);
}
}
bodyShape = new Path2D.Float();
bodyShape.moveTo( 20, 50);
bodyShape.lineTo( 20, 70);
bodyShape.lineTo( 20, 90);
bodyShape.lineTo( 10, 90);
bodyShape.lineTo( 10,100);
bodyShape.lineTo( 80,100);
bodyShape.lineTo( 80, 90);
bodyShape.lineTo( 40, 90);
bodyShape.lineTo( 40, 70);
bodyShape.lineTo( 40, 50);
bodyShape.closePath();
Reflection
AffineTransform aroundX, aroundY; aroundX = new AffineTransform( 1.0, 0.0, 0.0,-1.0, 0.0, 0.0); aroundY = new AffineTransform(-1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
package visual.statik.described;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* A panel that displays a GlyphVector
* in a spiral
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class SpiralGlyphCanvas extends GlyphCanvas
{
/**
* Constructor
*/
public SpiralGlyphCanvas()
{
super();
}
/**
* Render the glyphs
*/
protected void paintGlyphs(Graphics2D g2, String text)
{
AffineTransform at, trans;
AlphaComposite alpha;
Dimension d;
float angle, x, y;
FontRenderContext frc;
GlyphVector glyphs;
int i;
Shape shape, transformedShape;
d = getSize();
frc = g2.getFontRenderContext();
glyphs = font.createGlyphVector(frc, text);
shape = glyphs.getOutline(0.0f, 100.0f);
g2.setColor(Color.BLACK);
for (i=0; i < 6; i++)
{
angle = (float)(Math.PI/6.0 * i);
x = (float)(d.width/2.0);
y = (float)(d.height/2.0);
at = AffineTransform.getRotateInstance(angle,x,y);
trans = AffineTransform.getTranslateInstance(x,y);
at.concatenate(trans);
transformedShape = at.createTransformedShape(shape);
g2.fill(transformedShape);
}
}
}
Stroke Joins
Stroke Caps
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* An example of a described static visual content
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BuzzyComponent extends JComponent
{
private Arc2D.Float helmetShape, visorShape;
private BasicStroke stroke;
private Color black, gold, gray, purple;
private Path2D.Float bodyShape;
private QuadCurve2D.Float hairShape;
/**
* Default Constructor
*/
public BuzzyComponent()
{
super();
black = Color.BLACK;
gold = new Color(0xc2,0xa1,0x4d);
gray = new Color(0xaa,0xaa,0xaa);
purple = new Color(0x45,0x00,0x84);
stroke = new BasicStroke();
bodyShape = new Path2D.Float();
bodyShape.moveTo( 20, 50);
bodyShape.lineTo( 20, 70);
bodyShape.lineTo( 20, 90);
bodyShape.lineTo( 10, 90);
bodyShape.lineTo( 10,100);
bodyShape.lineTo( 80,100);
bodyShape.lineTo( 80, 90);
bodyShape.lineTo( 40, 90);
bodyShape.lineTo( 40, 70);
bodyShape.lineTo( 40, 50);
bodyShape.closePath();
hairShape = new QuadCurve2D.Float(10,2,40,10,30,25);
helmetShape = new Arc2D.Float(2,20,70,40,2,360,Arc2D.OPEN);
visorShape = new Arc2D.Float(40,25,35,30,315,90,Arc2D.PIE);
}
/**
* Paint this Component
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
g2 = (Graphics2D)g;
g2.setStroke(stroke);
g2.setColor(purple);
g2.fill(bodyShape);
g2.setColor(black);
g2.draw(bodyShape);
g2.setColor(purple);
g2.draw(hairShape);
g2.setColor(gold);
g2.fill(helmetShape);
g2.setColor(black);
g2.draw(helmetShape);
g2.setColor(gray);
g2.fill(visorShape);
g2.setColor(black);
g2.draw(visorShape);
}
}