JMU
An Introduction to 2-D Graphics
in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Rendering
Modeling Color

The (Linear) Color Cube

images/rgb-cube.gif
Modeling Color (cont.)
Coordinate Systems
Clipping
Composition
Composition (cont.)
Obtaining a Rendering Engine in Java
Obtaining a Rendering Engine in Java (cont.)

An Example

javaexamples/graphics/BoringComponent.java
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
    }

}

        
Sampling Static Visual Content
Sampling Static Visual Content (cont.)
Examples of Sampled Static Visual Content
Encapsulating Sampled Static Visual Content
Using Sampled Static Visual Content
Rendering Sampled Static Visual Content
javaexamples/graphics/ImageComponent.java
import 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
    }
}

        
A Simple Application
javaexamples/graphics/ImageComponentDriver.java
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!");
        }
    }
}
        
Described Static Visual Content
Simple Geometric Shape Objects
An Example of Described Static Visual Content
javaexamples/graphics/RandomRectangleComponent.java
import 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);       
    }
}
        
Encapsulating Points
javaexamples/graphics/SimpleShapeComponent.java (Fragment: point)
       // Construct a point
       point = new Point2D.Double(20.0, 30.0);
        
Encapsulating Line Segments
Encapsulating Line Segments (cont.)
Encapsulating Line Segments (cont.)
javaexamples/graphics/SimpleShapeComponent.java (Fragment: line)
       // Construct a line
       line = new Line2D.Double(0.0, 0.0, 50.0, 75.0);
        
Encapsulating Quadratic Bezier Curves
javaexamples/graphics/SimpleShapeComponent.java (Fragment: quadcurve)
       // 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
        
Encapsulating Cubic Curves
javaexamples/graphics/SimpleShapeComponent.java (Fragment: cubiccurve)
       // 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
        
Encapsulating Rectangles
javaexamples/graphics/SimpleShapeComponent.java (Fragment: rectangle)
       // Construct a rectangle
       rectangle = new Rectangle2D.Double(
                                     10.0, 20.0, // Upper Left
                                     30.0,       // Width
                                     60.0);      // Height
        
Encapsulating Ellipses and Arcs
javaexamples/graphics/SimpleShapeComponent.java (Fragment: arc)
       // 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        
        
Glyphs
Glyphs (cont.)

f and i as two glyphs and as a ligature

images/ligature.gif
Glyphs (cont.)
Glyphs (cont.)

The RSB is positive for A and negative for f

images/glyph-advance.gif
Glyphs (cont.)

Kerning - The Use of Different Bearings for Differnt Glyphs

images/kerning.gif
Glyphs (cont.)
Glyphs (cont.)

Line Metrics

images/fontmetrics.gif
Glyphs (cont.)
Glyphs (cont.)

Glyphs as Shapes

javaexamples/graphics/GlyphComponent.java
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;
    }
}
        
Glyphs (cont.)

Font Metrics

javaexamples/graphics/GlyphMetricsComponent.java
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);
    }

}
        
Glyphs (cont.)

Bounding Rectangle

javaexamples/graphics/CenteredGlyphComponent.java
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);
    }



}
        
Glyphs (cont.)
Complicated Geometric Shapes
Complicated Geometric Shapes (cont.)

An Example

javaexamples/graphics/BuzzyComponent.java (Fragment: body1)
       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();
        
Rendering Described Content

Stroke Joins

images/stroke_joins.gif
Rendering Described Content (cont.)

Stroke Caps

images/stroke_caps.gif
Rendering Described Content (cont.)
Rendering Described Content (cont.)

An Example

javaexamples/graphics/BuzzyComponent.java
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);
    }
}