|
Java Take-Off Step Five: Rendering Text, Shapes, Images (Part One) |
In graphics, a transformation manipulates geometry and places it within a scene.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
public class AffineTest extends Applet implements ItemListener
{
// the rectange to draw
private Rectangle2D rect;
// two checkboxes to allow us to specify the logical order in which
// to apply the transformations
private Checkbox rotateFirst;
private Checkbox translateFirst;
public void init()
{
// create a CheckboxGroup containing the two Checkboxes
setLayout(new BorderLayout());
CheckboxGroup cbg = new CheckboxGroup();
Panel p = new Panel();
rotateFirst = new Checkbox("rotate, translate", cbg, true);
rotateFirst.addItemListener(this);
p.add(rotateFirst);
translateFirst = new Checkbox("translate, rotate", cbg, false);
translateFirst.addItemListener(this);
p.add(translateFirst);
add(p, BorderLayout.SOUTH);
// model our rectangle about the origin
rect = new Rectangle2D.Float(-0.5f, -0.5f, 1.0f, 1.0f);
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// save an identity transform to clear the Graphics2D context
final AffineTransform identity = new AffineTransform();
// true if we wish to logically rotate first
boolean rotate = rotateFirst.getState();
// create a random number generator to produce random colors
Random r = new Random();
final double oneRadian = Math.toRadians(1.0);
for(double radians = 0.0; radians < 2.0*Math.PI; radians += oneRadian)
{
// clear this Graphics2D's transform
g2d.setTransform(identity);
// remember, operations are performed in reverse order
// than we logically prefer them!
if(rotate)
{
g2d.translate(100, 100);
g2d.rotate(radians);
}
else
{
g2d.rotate(radians);
g2d.translate(100, 100);
}
g2d.scale(10, 10);
g2d.setColor(new Color(r.nextInt()));
g2d.fill(rect);
}
}
public void itemStateChanged(ItemEvent e)
{
// a new Checkbox was selected, better repaint!
repaint();
}
} // AffineTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
public class MouseShapeTest extends Applet implements MouseMotionListener
{
// the Shape to draw
private Shape shape;
// flags whether or not the mouse cursor is over the Shape
private boolean mouseOver;
// the current draw and fill color
private Color currentColor;
public void init()
{
// the control points for the Shape
int[] x = { 25, 55, 60, 75, 110, 130 };
int[] y = { 65, 100, 133, 20, 115, 55 };
// create a new Polygon to represent our Shape. Remember, a
// Polygon *is* a Shape since it implements the Shape interface
shape = new Polygon(x, y, x.length);
mouseOver = false;
addMouseMotionListener(this);
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(currentColor);
// fill the Shape if the mouse cursor is over it
if(mouseOver)
{
g2d.fill(shape);
}
// otherwise, just draw the outline of the Shape
else
{
g2d.draw(shape);
}
}
public void mouseDragged(MouseEvent e) { /* do nothing */ }
public void mouseMoved(MouseEvent e)
{
// save the previous value
boolean prevValue = mouseOver;
// update the mouseOver flag using the Shape.contains method
mouseOver = shape.contains(e.getPoint()) ? true : false;
// repaint only if there is reason to
if(prevValue != mouseOver)
{
// why not change the current color while we're at it
currentColor = new Color(new Random().nextInt());
repaint();
}
}
} // MouseShapeTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
public class InstanceTest extends Applet
{
// the Shape to draw
private Shape shape;
public void init()
{
// create a unit square modeled about the origin
shape = new Rectangle2D.Float(-0.5f, -0.5f, 1.0f, 1.0f);
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// save an identity transform to clear the Graphics2D context
final AffineTransform identity = new AffineTransform();
Random r = new Random();
int width = getSize().width;
int height = getSize().height;
// create 500 instances of the shape's geometry
for(int i = 0; i < 500; i++)
{
// clear this Graphics2D's transform
g2d.setTransform(identity);
// randomly set up the transform
g2d.translate(r.nextInt()%width, r.nextInt()%height);
g2d.rotate(Math.toRadians(360*r.nextDouble()));
g2d.scale(20*r.nextDouble(), 10*r.nextDouble());
// draw the shape
g2d.setColor(new Color(r.nextInt()));
g2d.fill(shape);
}
}
} // InstanceTest
EXERCISES
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
public class ImageTest extends Applet
{
// the Images to render
private Image[] images;
// filename description of our images
private final String[] filenames
= { "simon.gif", "tj2gp.gif", "blade.gif" };
public void init()
{
// get the base URL
java.net.URL appletBaseURL = getCodeBase();
// allocate memory for the images and load 'em in
int n = filenames.length;
images = new Image[n];
for(int i = 0; i < n; i++)
{
images[i] = getImage(appletBaseURL, filenames[i]);
}
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// save an identity transform
final AffineTransform identity = new AffineTransform();
// used to transform our images
AffineTransform at = new AffineTransform();
Random r = new Random();
int width = getSize().width;
int height = getSize().height;
int numImages = filenames.length;
// render 100 images, each with a random transformation
for(int i = 0; i < 100; i++)
{
// clear the transformation
at.setTransform(identity);
// randomly set up the translation and rotation
at.translate(r.nextInt()%width, r.nextInt()%height);
at.rotate(Math.toRadians(360*r.nextDouble()));
// draw the image
g2d.drawImage(images[i%numImages], at, this);
}
}
} // ImageTest
You will need these:
simon.gif,
tj2gp.gif,
blade.gif.
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
public class StrokeTest extends Applet
{
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// set the pen width to 3.0 pixels
float penWidth = 3.0f;
// set a plain end caps decoration and a join miter line join
int endCaps = BasicStroke.CAP_BUTT;
int lineJoins = BasicStroke.JOIN_MITER;
// limit the miter join trim to 10.0 pixels
float trim = 10.0f;
// set the dash pattern
float[] dashPattern = { 5.0f, 9.0f, 3.0f };
// begin the pattern right away (with no pixel offset)
float dashOffset = 0.0f;
BasicStroke stroke = new BasicStroke(penWidth, endCaps, lineJoins,
trim, dashPattern, dashOffset);
g2d.setStroke(stroke);
g2d.draw(new Line2D.Float(10.0f, 10.0f, 140.0f, 10.0f));
g2d.draw(new Rectangle2D.Float(20.0f, 60.0f, 100.0f, 50.0f));
}
} // StrokeTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
public class GradientTest extends Applet
{
// the Polygon to draw
private Polygon poly;
// the two points that will define the paint's endpoints
private Point2D p1;
private Point2D p2;
public void init()
{
// the radius of two circles
final float[] radii = { 10.0f, 20.0f };
// the starting point and increment level for plotting points
double radians = 0.0;
final double increment = Math.toRadians(15.0);
poly = new Polygon();
// the shape will be determined by alternating between points on
// the perimeters of two circles
// since we are incrementing by 15 degrees, we can fit 24
// points in our shape (360/15 = 24)
for(int i = 0; i < 24; i++)
{
poly.addPoint((int)(radii[i%2]*Math.cos(radians)),
(int)(radii[i%2]*Math.sin(radians)));
radians += increment;
}
// set the endpoints of our paint. these values will be scaled
// by the Graphics2D object
p1 = new Point2D.Float(0.0f, +20.0f);
p2 = new Point2D.Float(0.0f, -20.0f);
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
AffineTransform at = new AffineTransform();
at.translate(100,100);
at.scale(5, 5);
// draw the shape
g2d.setTransform(at);
g2d.setPaint(new GradientPaint(p1, Color.orange, p2, Color.green));
g2d.fill(poly);
}
} // GradientTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class CycleTest extends Applet implements ItemListener
{
// the Rectangle to draw
private Rectangle2D rect;
// the line containing the points that will define the paint's endpoints
private Line2D line;
// checkboxes for selecting gradient cycle type
private Checkbox cyclic;
private Checkbox acyclic;
public void init()
{
// create a unit square
rect = new Rectangle2D.Float(-0.5f, -0.5f, 1.0f, 1.0f);
// set the endpoints of our paint.
line = new Line2D.Float(-0.25f, 0.0f, 0.25f, 0.0f);
setBackground(Color.orange);
// create checkboxes for selecting gradient cycle type
CheckboxGroup cbg = new CheckboxGroup();
setLayout(new BorderLayout());
Panel p = new Panel();
p.setBackground(Color.green);
cyclic = new Checkbox("cyclic", cbg, true);
cyclic.addItemListener(this);
p.add(cyclic);
acyclic = new Checkbox("acyclic", cbg, false);
acyclic.addItemListener(this);
p.add(acyclic);
add(p, BorderLayout.SOUTH);
}
public void paint(Graphics g)
{
// the scaled width of the rectangle
final double scaleWidth = 100.0f;
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// transform the shape
g2d.translate(100,100);
g2d.scale(scaleWidth, 50);
// draw the shape
g2d.setPaint(new GradientPaint(
line.getP1(), Color.black,
line.getP2(), Color.white,
cyclic.getState()));
g2d.fill(rect);
// draw lines perpendicular to paint endpoints
g2d.setPaint(Color.red);
g2d.setTransform(new AffineTransform());
g2d.translate(100-0.25*scaleWidth, 100);
g2d.rotate(Math.PI/2);
g2d.scale(scaleWidth/2, 1);
g2d.draw(line);
g2d.setTransform(new AffineTransform());
g2d.translate(100+0.25*scaleWidth, 100);
g2d.rotate(Math.PI/2);
g2d.scale(scaleWidth/2, 1);
g2d.draw(line);
}
public void itemStateChanged(ItemEvent e)
{
// update the change!
repaint();
}
} // CycleTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
public class TextureTest extends Applet implements ActionListener
{
// field for accepting a filename
private TextField input;
public void init()
{
// create a layout and add the textfield and "Ok" button
setLayout(new BorderLayout());
Panel p = new Panel();
input = new TextField("", 20);
p.add(input);
Button ok = new Button("Ok");
ok.addActionListener(this);
p.add(ok);
add(p, BorderLayout.SOUTH);
}
public void paint(Graphics g)
{
// cast the sent Graphics context to get a usable Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// just draw an outline of the shape and return if the text
// field just contains white-space
if("".equals(input.getText().trim()))
{
g2d.translate(112, 15);
g2d.rotate(Math.PI/4);
g2d.draw(new Rectangle2D.Double(0, 0, 104, 104));
return;
}
// load an image
// we'll look at the MediaTracker class when we discuss animation
MediaTracker mt = new MediaTracker(this);
Image image = getImage(getCodeBase(), input.getText());
mt.addImage(image, 0);
try
{ mt.waitForAll();
}
catch(InterruptedException e) { /* do nothing */ }
// the filename was probably invalid if the width or height of
// the image created is <= 0
if(image.getWidth(this) <= 0 || image.getHeight(this) <= 0)
{
// print error message and return
input.setText(input.getText() + " : invalid filename.");
return;
}
// create a new BufferedImage with the image's width and height
BufferedImage bi = new BufferedImage
(image.getWidth(this),
image.getHeight(this),
BufferedImage.TYPE_INT_RGB);
// get the Graphics2D context of the BufferedImage
// and render the original image onto it
((Graphics2D)bi.getGraphics()).
drawImage(image, new AffineTransform(), this);
// create the anchoring rectangle for the paint's
// image equal in size to the image's size
Rectangle2D bounds = new Rectangle2D.Float
(0, 0, bi.getWidth(), bi.getHeight());
// set the paint
g2d.setPaint(new TexturePaint(bi, bounds));
// transform and render!
g2d.translate(112, 15);
g2d.rotate(Math.PI/4);
g2d.fill(new Rectangle2D.Double(0, 0, 104, 104));
}
public void actionPerformed(ActionEvent e)
{
// the "Ok" was pressed; update the changes
repaint();
}
} // TextureTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// A quick n' dirty approach to encapsulating the properties (position,
// size, etc) of a square so that it can be updated regularly
class AlphaBox
{
// a random number generator for the class
private static Random random = null;
// all rendering will be based from a single unit square
private static Rectangle2D square = null;
// a class copy of the identity affine tranform
private static AffineTransform identity = null;
// properties of our box
private AlphaComposite alpha;
private double xPos; // x, y position
private double yPos;
private double xVel; // x, y speed
private double yVel;
private double size; // width and height
private Color color; // color of this instance
private Dimension windowSize;
public AlphaBox(Dimension d)
{
windowSize = d;
// define any null objects
if(random == null)
{
random = new Random();
}
if(square == null)
{
square = new Rectangle2D.Float(-0.5f, -0.5f, 1.0f, 1.0f);
}
if(identity == null)
{
identity = new AffineTransform();
}
// all composites will be SRC_OVER and very transparent
// play around with these values (try randomizing them)
// to get some cool effects
alpha = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.25f);
// randomize the properties of the box
xPos = windowSize.width*random.nextDouble();
yPos = windowSize.height*random.nextDouble();
xVel = 1+2*random.nextDouble();
if(random.nextDouble() > 0.5) xVel = -xVel;
yVel = 1+2*random.nextDouble();
if(random.nextDouble() > 0.5) yVel = -yVel;
size = 25+100*random.nextDouble();
color = new Color(random.nextInt()).brighter();
}
// paints the box to the sent Graphics2D context according
// to its current properties
public void paint(Graphics2D g2d)
{
// bounce the box around the window
xPos += xVel;
if(xPos > windowSize.width)
{
xPos = windowSize.width;
xVel = -xVel;
}
if(xPos < 0)
{
xPos = 0;
xVel = -xVel;
}
yPos += yVel;
if(yPos > windowSize.height)
{
yPos = windowSize.height;
yVel = -yVel;
}
if(yPos < 0)
{
yPos = 0;
yVel = -yVel;
}
// render the box
g2d.setTransform(identity);
g2d.translate(xPos, yPos);
g2d.scale(size, size);
g2d.setComposite(alpha);
g2d.setPaint(color);
g2d.fill(square);
}
} // AlphaBox
public class CompositeTest extends Applet implements Runnable
{
// a thread for animation -- we'll talk about this later
private volatile Thread animation;
// an array of AlphaBox objects
private AlphaBox[] boxes;
public void init()
{
animation = new Thread(this);
// create the boxes
final int n = 10;
boxes = new AlphaBox[n];
Dimension size = this.getSize();
for(int i = 0; i < n; i++)
{
boxes[i] = new AlphaBox(size);
}
}
public void start()
{
animation.start();
}
public void stop()
{
animation = null;
}
// override the update method so that it doesn't clear the window
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
// paint each AlphaBox
for(int i = 0; i < boxes.length; i++)
{
boxes[i].paint(g2d);
}
}
public void run()
{
// we'll talk about this stuff later!
Thread t = Thread.currentThread();
while (t == animation)
{
try
{
t.sleep(10);
}
catch (InterruptedException e)
{
}
repaint();
}
}
} // CompositeTest
EXERCISES
import java.io.*;
import java.awt.*;
public class FontListing
{
public static void pause()
{
System.out.println("\nPress Enter to Continue");
try
{
System.in.read();
}
catch(IOException e)
{
}
}
public static void main(String[] args)
{
String[] availableFonts = GraphicsEnvironment.
getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
for(int i = 0; i < availableFonts.length; i++)
{
System.out.println(availableFonts[i]);
if(i > 0 && i%20 == 0)
{
pause();
}
}
}
} // FontListing
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.font.*;
public class FontTest extends Applet
{
// Color constants for rendering different colored text
static final Color[] colors = {
Color.red, Color.blue, Color.orange, Color.darkGray };
// paints some text to the screen
public void paint(Graphics g)
{
// remember to cast to a vaild Graphics2D object
Graphics2D g2d = (Graphics2D)g;
// we don't need an explicit reference to the font, so we'll
// just specify it in one line. applets that use multiple fonts
// would want to save a copy of each font.
g2d.setFont(new Font("Helvetica", Font.BOLD, 1));
// scale the font, then translate it to be centered on the screen
g2d.translate(150, 150);
g2d.scale(20, 20);
// render "Fonts are FUN!" using each color
for(int i = 0; i < colors.length; i++)
{
// set the current color
g2d.setPaint(colors[i]);
// render the String at (0,0); g2d's transform will take care
// of the actual rendering position
g2d.drawString("Fonts are FUN!", 0, 0);
// rotate by 60 degrees
g2d.rotate(Math.PI/3.0);
}
} // paint
} // FontTest
EXERCISES
import java.applet.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
public class FontBoundsTest extends Applet
{
private final String MESSAGE = "Trapped!";
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
// create a Font Object.
Font baseFont = new Font("Helvetica", Font.PLAIN, 50);
// get the FontRenderContext for the Graphics2D context
FontRenderContext frc = g2d.getFontRenderContext();
// get the layout of our message and font, using the above
// FontRenderContext
TextLayout layout = new TextLayout(MESSAGE, baseFont, frc);
// get the bounds of the layout
Rectangle2D textBounds = layout.getBounds();
// draw the message and the bounding rectangle at (45, 50)
g2d.setFont(baseFont);
g2d.setPaint(Color.black);
g2d.drawString(MESSAGE, 45, 50);
g2d.translate(45, 50);
g2d.setPaint(Color.red);
g2d.draw(textBounds);
}
} // FontBoundsTest
Whew... This was a long chapter!
A201/A597/I210/A348/A548/T540/NC009