Graphics and the Java 2D API

Computer graphic is a complex subject, several courses can be devoted to it.  Show O’Reilly Graphic Java book.  Java supplies many high-level methods and classes to hide the complexity (Frame.add(Label)) but also allows one to get creative by using the many graphic primitives available in the Java 2D API.  Figure 11.1 (p. 515) shows the classes for Java 1.1 and Java 2.  Review: repaint() ‑‑> update() ‑‑> paint().  Note that a request to repaint schedules the update but may not do it immediately, merely soon  (Show javadocs for java.awt.component.repaint).

Drawing (a.k.a. Rendering) Overview

The steps in the Java drawing process are (from Graphics2D javadocs):

1.     Determine what to draw.  The system knows how to draw four types of things:

a.      filled Shapes

b.     Shape outlines (including lines and arcs)

c.     text

d.     images

Shapes are bounded areas that get filled with the current Paint.  Shape outlines are stroked using the current Stroke (a combination of the pen shape and size, and the type of line to use such as solid or dashed) and current Paint.  Text is converted to Shapes (known as glyphs) letter by letter and then drawn as described above.  Images are the easiest to draw: a Shape the size of the image is created and this Shape is painted from the pixels of the image.

In all four cases, the Shape is transformed from user coordinates to the final output device coordinates by using the current Transform attribute of the Graphics2D object.  (In reality there is a double transformation, the user supplied one and another one to translate (move) the image to the right place on the screen.)

2.     Constrain the drawing operation to the current Clip (Clipping Region).  The Clip is specified by a Shape in user space and is controlled by the program using the various clip manipulation methods of Graphics and Graphics2D.  This user clip is transformed into device space by the current Transform and combined with the device clip, which is defined by the visibility of windows and device extents (i.e., screen or paper size).  The combination of the user clip and device clip defines the composite clip, which determines the final clipping region.  The user clip is not modified by the rendering system to reflect the resulting composite clip.

3.     Determine what Paint to use when drawing.  This step is usually trivial but it is possible to use color corrected output for projectors, color printers, etc., gradients, images, or (wallpaper).  This required the colors used be modified slightly.  (See classes in java.awt.color.)

4.     Apply the Paint to the destination drawing surface using the current Composite attribute (drawing mode) in the Graphics2D context.

The Graphics object

This encapsulates a graphic context: The drawing surface (an on-screen drawing area within some Java component, or an off-screen image used for better graphics or printing), the allowed drawing methods (such as drawRect), and various attributes used by the drawing methods.  Some examples of attributes are the color to draw with, the font to display text with (Java 1.1 defines 5 standard fonts: Serif, SansSerif, Monospaced, Dialog, DialogInput), the drawing origin to interpret all (user) coordinates from, and a clipping region outside of which no drawing is allowed.  Additional features include what cursor to use (there are 14 predefined cursors), what system colors and fonts are specified by the user (e.g., the color for a menubar;  see java.awt.SystemColors), image manipulation, animations, and printing.  You can specify a drawing mode (paint, XOR and others through compositing).  (Describe XOR and how it is used for popups, etc.)

New features for Java 2 include drawing lines of different thicknesses, controlling the ends of lines (and how lines should join), and even dashed style lines.  There are expanded font capabilities, attributed text (draw strings with some words bold or italics), special fills (such as gradients, patterns, and even images; these can be used to draw text with, instead of a mere color), hypertext (this is part of swing, not the 2D API), fancy curves and transforms, and transparency control.  You can use antialiasing (reduces jaggies), custom color models, and control the compositing of images (instead of merely setting XOR mode).

All this may seem quite complicated, but keep in mind the following:

·        There are default values for all attributes so you can safely ignore any settings and features you wish.  The defaults are:

o       Paint: The inherited color of the container of the component.  Top level containers use some default color if none is specified.

o       Font: Same as for Paint.

o       Stroke: A square pen with a line width of 1, no dashing, miter segment joins and square end caps.  (Show java.awt.BasicStroke.)

o       Transform: The inherited Transform.  Top level containers use a default of none.

o       Composite (Pen mode): The AlphaComposite.SRC_OVER rule.

o       Clipping Region:  The boundaries of the component.

·        All Graphics’ features are used the same way:
         
g.setXXX(new value);  // set an attribute.
    g.drawYYY(...);  // use the new setting.

These features require some specialized knowledge of computer graphics to use well.  (Demo %JAVA_HOME%\demo\jfc\Java2Demo.html or java ‑jar java2Demo.jar)

These new abilities use several new classes (used as attributes of a Graphics2D object):

·        Shape interface (with some concrete classes in java.awt.geom package), defines additional graphic primitive operations and attributes.

·        Stroke interface (and concrete class BasicStroke), defines how lines are drawn (or stroked) by defining the pen size and shape, and a line type (e.g., solid or dashed).

·        Paint interface (and concrete classes GradientPaint, TexturePaint, and Color), defines how Shapes are to be filled.  (In AWT 1.1 you used setColor, 2D uses setPaint, but allows setColor).

·        Composite interface (and concrete class AlphaComposite), defines how to combine colors that define a single pixel.  Alpha refers to levels of transparency.  In Java AWT 1.1 you only have two pen modes: paint (equivalent to SRC_OVR) and XOR.  In AWT 1.1 you could use setPaintMode and setXORMode; in 2D use setComposite(new Composite).

·        RenderingHints class, can be use to specify extra features when drawing (such as antialiasing).  These are specified as key-value pairs.

·        AffineTransform class (in java.awt.geom), defines how the user coordinate system maps onto the device coordinate system (or space).  Use this to rotate, scale, move, and shear drawings.  Note several transformations can be concatenated into a single composite transform.

·        Extended font support.  (Discussed below.)

To access the new features you need to use a Graphic2D object instead of a Graphics object.  This is easy since the paint method is actually invoked with a Graphics2D object (Graphics2D is extended from Graphics) in Java 2 and later:

          public void paint ( Graphics gr )

    { Graphics2D g = (Graphics2D) gr;

      g.drawString( ... ); ... }

Demo Jade.java.

Graphic Contexts are Copies

paint (and the swing equivalent paintComponent) is automatically passed a Graphics (actually Graphics2D) object, but you can get one anytime by using any component’s getGraphics method.  You would do this to perform some drawing outside of the paint method.  getGraphics returns not a reference to the actual graphic context, but a reference to a copy.  (Demo CopyTest.java ???)

Disposing of Graphic Contexts

The copy passed to paint is disposed of automatically when paint returns.  However, a graphic context obtained via Component.getGraphics() or Graphics.create() must be disposed of by you.  Although Java has automatic garbage collection, some AWT resources are exempt from this cleanup and must be disposed of manually: a Graphics, a Window, and a Dialog.  (QU: Why?  Ans:  Even after a method that created a Graphics object returns, the drawing might (and probably is) still visible on the screen.)

Graphic contexts are supplied by the underlying native windowing system, and are a finite (and usually scarce) resource.  If care isn’t taken you could run out of these.  (Perhaps some other applications are running too.)

The Graphics.dispose() method should be placed in a finally block, so that it is invoked even if some exception is thrown:

void someMethod()

{  Graphics g = getGraphics();

   if ( g != null )

   {  try

      {  g.whatever();

      }

      finally

      {  g.dispose();

      }

}  }  else { Handle_error; }

The Coordinate System

All  coordinates passed to Graphic (actually Graphic2D) object methods are specify in a device independent coordinate system called User Space.  These coordinates are translated automatically (by a transform, part of the Graphics object) into Device Space, the coordinates used by your monitor, printer, cell phone, ...  These coordinates refer to actual pixels.  The actual math required is determined by the type of device you’re using, which is represented internally as a GraphicConfiguraiton object.  Generally speaking, the transform puts the (0,0) point at the upper left, with increasing values down and to the right, with approximately 72 coordinates to the inch.

In Java you can think of coordinate points as lying between pixels.  Drawing is done with a pen that extends down and to the right of the coordinate.  Drawing is performed by Java methods that define a coordinate path.  So

          g.drawRect( 2, 2, 4, 4 );

uses a 1 pixel square pen to draw (trace) the following path:
          (2,2) -->(6,2)-->(6,6)-->(2,6)-->(2,2)
and any pixel covered by the pen is colored with the proper foreground color.

(Qu:  Suppose you have a 50 x 50 graphic, drawn in the upper left-hand corner of some Frame, and you wish to draw a border around it using Graphics.drawRect().  What is the correct method call?  Ans: g.drawRect( 0, 0, 49, 49);  not ...,50,50!)
There are two coordinate systems, user and device.  A transform is a mathematical expression that takes a user space coordinate (ux, uy) and computes the device space coordinate (dx, uy).  You can control this mapping in 4 ways: translation, rotation, scale, and shear.  (Describe these.)  Remember the transform used is an attribute of a Graphics object.

Stroking Lines

2D drawing:  basic rule: you stroke lines and you paint shapes.  1.1 AWT supported polygon but 2D adds support for many shapes.  You can create a shape from scratch by specifying a general path that represents the shape’s outline.

With 2D you can create shapes from scratch or from text and graphics (common: have big text, extract shapes from letters into an array, then paint each letter as a shape with different textures or color, or transformed in some way).  With 2D graphics you can specify line widths, styles (dashes), and how lines join and end.  Use g.setPaint to set a new Paint.  Use g.setStroke to set a new Stroke.  Then start drawing and painting!

Defining and Transforming Shapes

A Shape is an outline of points connected with lines and curves.  Note the Shape is defined in terms of the current user coordinates.

The system comes with many predefined Shapes in java.awt.geom, and allows the creation of Shapes from scratch, or from the outline of some text or a graphic image.  So rather than use g.drawRect or g.fillRect, you can use g.draw(s) and g.fill(s), where s is some Shape such as Rectangle2D.

Once a Shape has been created, you can apply a transformation to it to distort or modify the Shape in some way:  Move it, Scale it, rotate it about some point, or shear it. This is done by creating an AffineTransformation and using:

AffineTransform at = new AffineTransform();
at.translate( 30, 0 );  // move shape 30 to left.
Shape newShape = at.createTransformedShape( old );

(Create some sort of dog bone demo using GeneralPath.)

Colors and Paints

Java uses the foreground color (g.setColor) to draw text, lines, and basic shapes (including polygons) that are part of AWT 1.1.  You can set a background color too.  For Java 2D graphics, you can get more creative by setting a Paint to draw with.  The classes that support the Paint interface are Color, GraientPaint, and TexturePaint (i.e., wallpaper).

Color.Constants, darker() and brighter(), RGB methods, HSB methods, JColorChooser.showDialog( frame_to_anchor_to, dialog_win_title, Color_variable); returns null if user cancels dialog.  ( Show pictures from text section on JColorChooser.)  You can use a component’s setBackground and setColor methods.  (Qu: Using setBackground inside of the paint method doesn’t work well.  Why?  So use repaint().)  System colors set by the user are found in SystemColor.

Fonts

Pixels and points (1/72 in.).  (Show how to determine screen resolution using java.awt.Toolkit.getDefaultToolkit() methods getScreenResolution and getScreenSize.)

 

          Show baseline, ascent, descent, leading:

 

Font types: monospaced vs. proportional, serif vs. sans serif.

Standard 5 logical font names:  Monospaced, Serif, SansSerif, Dialog, DialogInput (and Symbol?).  In addition, 12 fonts (8 additional ones) are provided with the JRE 1.2 and newer: Lucida Bright, Lucida Sans, and Lucida Sans Typewriter.

    Font f = new Font("name", attributes, size_in_points)

(p. ???) Font styles are roman (Font.PLAIN), Bold (Font.BOLD), Italic (usually maps to physical Oblique font) (Font.ITALIC), and BoldItalic (use “Font.BOLD+Font.ITALIC” or use “|” insteaf of “+”.)

Fonts don’t always appear correct when rendered.  One reason may be that the font rendering code thinks that each pixel is equal to one point (about 1/72nd of an inch).  This isn’t always so on modern displays.  A trick that may help is to normalize the graphics contexts so that 1 coordinate point is exactly 1/72 inch, and thus fonts will display at correct sizes:

  g.transform(getConfiguration().getNormalizingTransform());

List available fonts:  Use Toolkit.getFontList() which returns an array of logical font names, or GraphicsEnvironment.getAllFonts() which returns an array of physical fonts in this environment.  (These include the host platform fonts and the physical fonts of the JDK software.)

For special graphic effects you can convert a String in a given Font into a Vector of Shapes (Font.createGlyphVector), then transform each Shape, fill each one (or all), or even use the Shape outline of the string as a clipping region (a “porthole” effect).

Demo: add shadow text:  Draw in black first, then shift up and left (2+points) and change the paint, then draw same string again.  (Demo ShowFonts.java.)

FontMetrics describe a particular font and have many useful methods:

fm = g.getFontMetrics( optional_font_name );

fm.getHeight(), fm.getAscent(), ..., fm.getStringWidth(“some string”)

Splash Screens

A splash screen is a computer term for an image that appears while a program or operating system is loading.  It provides the user with a visual indicator that the program is initializing, and often displays logos, lisence terms, and eye candy.

The problem with using splash screens is that you must wait too long for the JRE and AWT systems to load before you can display a window with a graphic.  A new command line switch has been added to Java 6 to display a graphic (GIF, including animated GIF, JPEG, or PNG) immediely, centered on the screen, until the first window is shown by the application:

java -splash:Hello.png HelloWorld

A java.awt.SplashScreen class allows special effects and control of splash screens once the application has loaded.  You can also put the graphic in an application’s JAR file, and add the following to the JAR’s manifest:

SplashScreen-Image: MyImage.png