![]() |
![]() 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
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
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
You will need these: simon.gif, tj2gp.gif, blade.gif.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
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
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
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
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
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
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
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
Whew... This was a long chapter!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
A201/A597/I210/A348/A548/T540/NC009