Figure 12-8 showed a variety of shape-filling techniques; it included a large letter A filled with a complex pattern defined by the GenericPaint class. Example 12-18 shows the implementation of this class. You may want to take another look at Example 12-10 to see how the GenericPaint class is used, before you dive into the code listed here. The GenericPaint class itself is pretty simple: it defines both the abstract color computation methods that subclasses implement and a createContext( ) method that returns a PaintContext. The implementation of PaintContext does all the hard work. This is pretty low-level stuff, so don't be dismayed if you don't understand everything. The code should at least give you a basic idea of how painting works in Java 2D. Example 12-18. GenericPaint.javapackage je3.graphics; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; /** * This is an abstract Paint implementation that computes the color of each * point to be painted by passing the coordinates of the point to the calling * abstract methods computeRed( ), computeGreen( ), computeBlue( ) and * computeAlpha( ). Subclasses must implement these three methods to perform * whatever type of painting is desired. Note that while this class provides * great flexibility, it is not very efficient. **/ public abstract class GenericPaint implements Paint { /** This is the main Paint method; all it does is return a PaintContext */ public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { return new GenericPaintContext(xform); } /** This paint class allows translucent painting */ public int getTransparency( ) { return TRANSLUCENT; } /** * These three methods return the red, green, blue, and alpha values of * the pixel that appears at the specified user-space coordinates. The return * value of each method should be between 0 and 255. **/ public abstract int computeRed(double x, double y); public abstract int computeGreen(double x, double y); public abstract int computeBlue(double x, double y); public abstract int computeAlpha(double x, double y); /** * The PaintContext class does all the work of painting **/ class GenericPaintContext implements PaintContext { ColorModel model; // The color model Point2D origin, unitVectorX, unitVectorY; // For device-to-user xform public GenericPaintContext(AffineTransform userToDevice) { // Our color model packs RGB values into a single int model = new DirectColorModel(32, 0x00ff0000,0x0000ff00, 0x000000ff, 0xff000000); // The specified transform converts user to device pixels // We need to figure out the reverse transformation, so we // can compute the user space coordinates of each device pixel try { AffineTransform deviceToUser = userToDevice.createInverse( ); origin = deviceToUser.transform(new Point(0,0), null); unitVectorX = deviceToUser.deltaTransform(new Point(1,0),null); unitVectorY = deviceToUser.deltaTransform(new Point(0,1),null); } catch (NoninvertibleTransformException e) { // If we can't invert the transform, just use device space origin = new Point(0,0); unitVectorX = new Point(1,0); unitVectorY = new Point(0, 1); } } /** Return the color model used by this Paint implementation */ public ColorModel getColorModel( ) { return model; } /** * This is the main method of PaintContext. It must return a Raster * that contains fill data for the specified rectangle. It creates a * raster of the specified size, and loops through the device pixels. * For each one, it converts the coordinates to user space, then calls * the computeRed( ), computeGreen( ) and computeBlue( ) methods to * obtain the appropriate color for the device pixel. **/ public Raster getRaster(int x, int y, int w, int h) { WritableRaster raster = model.createCompatibleWritableRaster(w,h); int[ ] colorComponents = new int[4]; for(int j = 0; j < h; j++) { // Loop through rows of raster int deviceY = y + j; for(int i = 0; i < w; i++) { // Loop through columns int deviceX = x + i; // Convert device coordinate to user-space coordinate double userX = origin.getX( ) + deviceX * unitVectorX.getX( ) + deviceY * unitVectorY.getX( ); double userY = origin.getY( ) + deviceX * unitVectorX.getY( ) + deviceY * unitVectorY.getY( ); // Compute the color components of the pixel colorComponents[0] = computeRed(userX, userY); colorComponents[1] = computeGreen(userX, userY); colorComponents[2] = computeBlue(userX, userY); colorComponents[3] = computeAlpha(userX, userY); // Set the color of the pixel raster.setPixel(i, j, colorComponents); } } return raster; } /** Called when the PaintContext is no longer needed. */ public void dispose( ) { } } } |