Section 7.3. Using Graphics2D for Advanced Drawing


[Page 230 (continued)]

7.3. Using Graphics2D for Advanced Drawing

We have used the java.awt.Graphics object to draw simple shapes like lines, rectangles, ovals, arcs, and polygons. The Graphics object is like a painter who picks up a brush with the color you set and paints a shape with it. If you ask the painter to paint another shape over a previous one the new shape will cover the original shape. But, what if we want to use a thicker "brush" when we draw? What if we want shapes to combine rather than occlude? What if we want to treat our shapes as objects?

The java.awt.Graphics class is okay for simple drawing but lacks many advanced features. However, the class java.awt.Graphics2D which is part of the Java 2D API can be used for more advanced drawing. Some of the capabilities of a Graphics2D object are:

  • You can set the width of the brush (pen). You can also set the style of the brush to do different types of dashed lines.

  • You can rotate, translate, scale, or shear what you are drawing.

  • You can fill a shape with more than just a solid color. You can fill a shape with a gradient or a texture.

  • You can change what happens when objects overlap.


  • [Page 231]
  • You can clip objects so that only the part visible inside the clipping area is drawn. This is like using a stencil.

  • You can set rendering hints to make your curves smoother using anti-aliasing if it is available.

7.3.1. Setting the Brush Width

To use these advanced features you will need a Graphics2D object. To get a Graphics2D object you must cast the Graphics object to a Graphics2D object using Graphics2D g2 = (Graphics2D) graphics. All Graphics objects that you get are really Graphics2D objects, so this is allowed.

You can set the brush options using setStroke. You set the color, gradient, or texture to use for painting with the method setPaint(Paint p). Instead of methods that draw shapes the Graphics2D class has you create objects and either draw them to draw the outline or fill them to fill with the current paint information. Here is a simple program that draws a red x on the current picture using a brush (stroke) with the specified width.

Program 57. Draw an X on a Picture

/**  * Method to add two crossed lines to a picture.  * One line will go from the top left corner to the  * bottom right corner.   The other will go from the  * bottom left corner to the top right corner.  */ public void drawWideX(Color color, float width) {   // get the Graphics2D object   Graphics graphics = this.getGraphics();   Graphics2D g2 = (Graphics2D) graphics;   // set the color and brush width   g2.setPaint(color);   g2.setStroke(new BasicStroke(width));   // get the max x and y values   int maxX = getWidth() - 1;   int maxY = getHeight() - 1;   // draw the lines   g2.draw(new Line2D.Double(0,0,maxX,maxY));   g2.draw(new Line2D.Double(0,maxY,maxX,0)); }


You can use this program to add a wide red 'X' to a picture, as shown in Figure 7.12.


[Page 232]
> String fileName = FileChooser.getMediaPath("grayMotorcycle.jpg"); > Picture p = new Picture(fileName); > p.drawWideX(java.awt.Color.red,5); > p.show();


Figure 7.12. Drawing a red X on a picture.


Making it Work Tip: Creating and Drawing Shapes with Graphics2D

Notice that we created a Line2D.Double object and then asked the Graphics2D object named g2 to draw this object. This is different from how we drew shapes using the Graphics class. With the Graphics2D class you create geometric objects and either draw or fill them.

The Line2D.Double probably looks strange to you. This is actually the name of a class in the java.awt.geom package. Even though java.awt.geom and java.awt both start the same they are different packages. If you imported all classes in the package java.awt using the wildcard '*' you still wouldn't have imported the classes in the java.awt.geom package. You need at least two import statements if you are using classes from both of these packages.


7.3.2. Copying Pictures by Drawing Images

Both the Graphics and Graphics2D classes have methods for drawing images. You can use these methods to copy a picture to the current picture object. You don't have to copy the colors pixel by pixel. The method to do this is drawImage.

There are many methods with the name drawImage. Recall that method overloading allows methods with the same name as long as the parameters are different. Look at the API for the Graphics class to see all the drawImage methods. Which method would you use to copy all the pixels in one image (picture) to another at a particular location?


[Page 233]

Notice that the drawImage methods take an Image object not a Picture object. The Picture class is one we created, but it contains a method that will get you an Image object: getImage().

So, to copy a picture to the current picture object you need to get the Graphics object to use to draw on the current picture. You can do this using the method getGraphics(). Next draw the Image that you get from the passed Picture (using the method getImage()) at the passed x and y location using the method drawImage(Image img, int x, int y, ImageObserver observer). What is an ImageObserver? It is an object that wants to be notified as the image is changed. It can be null to say that no object wants to be notified.

Program 58. Copy a Picture to this Picture

/**  * Method to copy the passed picture into the current  * picture at the given x and y position in the  * current picture  * @param source the picture to copy  * @param x the x of the upper left corner to copy to  * @param y the y of the upper left corner to copy to  */ public void copy(Picture source, int x, int y) {   // get the graphics object   Graphics g = this.getGraphics();   // copy the image   g.drawImage(source.getImage(),x,y,null); }


You can use this program to copy a turtle to the beach (Figure 7.13) using:

> Picture p1 = new Picture(FileChooser.getMediaPath("beach.jpg")); > p1.explore(); > Picture p2 = new Picture(FileChooser.getMediaPath("turtle.jpg")); > p1.copy(p2,194,304); > p1.show();


Figure 7.13. Drawing a turtle on a beach.
(This item is displayed on page 234 in the print version)


Now change the copy method to use a Graphics2D object. Will it still compile? Try it and see. Does the Graphics2D class have the drawImage method?

Program 59. Copy a Picture to this Picture Using Graphics2D
(This item is displayed on pages 233 - 234 in the print version)

/**  * Method to copy the passed picture into the current  * picture at the given x and y position in the current  * picture  * @param source the picture to copy  * @param x the x of the upper left corner to copy to  * @param y the y of the upper left corner to copy to  */ 
[Page 234]
public void copy2D(Picture source, int x, int y) { // get the graphics object Graphics g = this.getGraphics(); Graphics g2 = (Graphics2D) g; // copy the image g2.drawImage(source.getImage(),x,y, null); }


Why does this work? The class Graphics2D inherits from the class Graphics. What does that mean? Have you heard of children inheriting features or abilities from their parents like eye color, hair color, or musical ability? A child class inherits from a parent class. What can it inherit? Classes don't have eye color, but they do define data (fields) and behavior (methods). A child class inherits data (fields) and behavior (methods).

Unlike people, classes inherit all of the fields and methods of the parent class and they can only have one parent. An object of a child class can invoke public methods that are defined in a parent class just as if they were defined in the child class. An object of a child class can also access any public fields in a parent class as if they were defined in the child class.

What about private fields and methods? While these are inherited, they can not be directly accessed by a child object, nor should they be. The standard way to work with private fields is to provide public methods that can modify or access the private fields, but keep the data in a valid state. Private methods are only meant to be used inside the class they are defined in, so you wouldn't want a child directly using them.


[Page 235]

How can we tell that the class Graphics2D inherits from the class Graphics? If you look at the API for Graphics2D (Figure 7.14) you will see that the API starts with the package name. On the next line is the class name. Following that is the ancestor tree of the class. It shows all the ancestors of the class starting with java.lang.Object. The class java.awt.Graphics2D class inherits from the class java.awt.Graphics which inherits from the class java.lang.Object. You can also tell who the parent class is by finding the class name given after the extends keyword in the class declaration.

Figure 7.14. Documentation for Graphics2D.


Making it Work Tip: Object is the Top Ancestor

All classes inherit from the class java.lang.Object, and it is the only class that doesn't inherit from another class. Even if a class doesn't specify in the class definition the class it inherits from using the extends keyword it will inherit from Object.


7.3.3. General Scaling

We have shown how to scale an image up by copying the same pixel more than once. We have shown how to scale an image down by skipping every other pixel. The methods that we wrote would double the size of the original image or reduce it by half. But, what if we want to scale up in x and down in y? What if we want to scale to a specific size? The Graphics2D class provides ways to handle scaling using an AffineTransform object in package java.awt.geom to handle the transformation. Here is a program that will scale a picture by the passed x and y factors.


[Page 236]

Program 60. General Scale Method

/**  * Method to create a new picture by scaling the current  * picture by the given x and y factors  * @param xFactor the amount to scale in x  * @param yFactor the amount to scale in y  * @return the resulting picture  */ public Picture scale(double xFactor, double yFactor) {   // set up the scale transform   AffineTransform scaleTransform = new AffineTransform();   scaleTransform.scale(xFactor,yFactor);   // create a new picture object that is the right size   Picture result = new Picture((int) (getWidth() * xFactor),                                (int) (getHeight() * yFactor));   // get the graphics 2d object to draw on the result   Graphics graphics = result.getGraphics();   Graphics2D g2 = (Graphics2D) graphics;   // draw the current image onto the result image scaled   g2.drawImage(this.getImage(),scaleTransform,null );   return result; }


You can use the scale method to create a new picture from the original picture by scaling up or down by any amount in x and/or y (Figure 7.15).

> Picture p = new Picture(     FileChooser.getMediaPath("mattDoor.jpg")); > Picture p1 = p.scale(2.0,0.5); > p1.show();


Figure 7.15. Original picture and picture scaled up two times in x and down by half in y.



[Page 237]

Making it Work Tip: Build from Working Parts

Earlier we wrote a method to scale a picture by copying colors from a source picture to a target picture pixel by pixel. Here we are using classes defined as part of the Java language to do that work for us. There are many classes in Java available for you to use. When you start solving a problem, you don't have to create everything you need from scratch. Instead, you can assemble your solution from classes that already exist. This is one of the goals of object-oriented programming: make things reusable by encapsulating related data and methods in classes.


7.3.4. Shearing

One of the effects that is easy to do with an java.awt.geom.AffineTransform object is to shear the image. Shearing by 1.0 in x moves each row over by the y index amount. So the first row, with a y index of 0, starts at (0, 0). The second row, with a y index of 1, starts at (1, 1). The third row, with a y index of 2, starts at (2, 2). The last row's x will start at (height 1, height 1). To figure out how big our new picture will need to be no matter what the amounts we are shearing by are, use a method in the Picture class, the gettranslationEnclosingRectangle(AffineTransform trans) method, which takes an AffineTransform object and returns a rectangle that will enclose the picture that results from applying that AffineTransform object to the current Picture object.

Program 61. General Shear Method
(This item is displayed on pages 237 - 238 in the print version)

/**  * Method to create a new picture by shearing the current  * picture by the given x and y factors  * @param xFactor multiplier to use to shift in x  * direction based on y index  * @param yFactor multiplier to use to shift in y  * direction based on x index  * @return the resulting picture  */ public Picture shear(double xFactor, double yFactor) {   // set up the shear transform   AffineTransform shearTransform = new AffineTransform();   shearTransform.shear(xFactor,yFactor);   Rectangle2D rect =     getTranslationEnclosingRectangle(shearTransform);   /* create a new picture object big enough to hold the    * result    */   Picture result = new Picture(                    (int) (Math.ceil(rect.getWidth())),                    (int) (Math.ceil(rect.getHeight()))); 
[Page 238]
// get the graphics 2d object from the result Graphics graphics = result.getGraphics(); Graphics2D g2 = (Graphics2D) graphics; /* save the current transformation and set-up to * center the new image */ AffineTransform savedTrans = g2.getTransform(); AffineTransform centerTrans = new AffineTransform(); centerTrans.translate(0 - rect.getX(), 0 - rect.getY()); g2.setTransform(centerTrans); // draw the current image onto the result image sheared g2.drawImage(this.getImage(),shearTransform,null); // reset g2 transformation to the saved one g2.setTransform(savedTrans); return result; }


You can use the shear method to create a new picture from the original picture by shearing by any amount in x and/or y (Figure 7.16).

> Picture p = new Picture(    FileChooser.getMediaPath("mattDoor.jpg")); > Picture p1 = p.shear(1.0,0.0); > p1.show();


Figure 7.16. Picture sheared by 1.0 in x.


7.3.5. Drawing with a GradientPaint

Instead of just filling with a solid color, you can fill with a blend of two colors (a gradient). You have to specify the two colors and the rectangular area where they transition from one color to another in the coordinates of the shapes you are drawing.


[Page 239]

What if you want to add a sun that changes color from yellow at the top to red at the bottom to a picture? You can create a java.awt.GradientPaint object and set that to be the paint using the method setPaint(Paint p). A GradientPaint object is a kind of Paint object because the class GradientPaint implements the Paint interface.

7.3.6. Interfaces

What is an interface? An interface in Java is a special kind of class that only has abstract methods (methods with just a method declaration and no body) and perhaps some constants defined in it.

You can compare two strings and find out if one is less than, equal to, or greater than another because the String class implements the java.lang.Comparable interface. This means that the String class provides the code for the compareTo(Object o) method which returns an integer less than 0 if one string is less than the other, the integer 0 if the strings contain the same characters, and an integer greater than zero if one string is greater than the other. It compares the characters in the strings alphabetically. Take a look at the java.lang.Comparable interface for an example of an interface. Also see java.lang.String for an example of a class implementing an interface.

Even though a class can only inherit from one parent class a class can implement many interfaces. An object of a class that implements an interface is said to be of the interface type.

Computer Science Idea: Constants

Constants are variables that are declared with the keyword final which means that the value of the variable can't change. Constants are usually also declared with the keyword static so that they are allocated in the object that defines the class and not in each object of the class. You don't need each object of a class to have a copy of a constant. Constants are usually public so that other classes can use them.

The convention in Java is to name constants with all capital letters and use '_' between words (like AlphaComposite.SRC_OVER). If a constant is defined with the keyword static it can be referred to using ClassName.CONSTANT_NAME.


So even though the class GradientPaint inherits from java.lang.Object it implements the interface java.awt.Paint and so objects of GradientPaint can be passed to methods that expect objects of the type Paint. The classes Color and TexturePaint also implement the Paint interface which is why they can also be passed to the method setPaint(Paint p).

Computer Science Idea: Interface

An interface is a boundary between two things. You use a user interface to communicate with the computer. You may be familiar with the USB interface. It allows several different types of devices to communicate with a computer. You can hook up a mouse, a camera, or a disk drive, and the computer will know how to communicate with the device because each uses the same interface.



[Page 240]

Program 62. Using a Gradient Paint

/**  * Method to add a gradient painted sun to the current picture  * @param x the x location for the upper left corner of the  * rectangle enclosing the sun  * @param y the y location for the upper left corner of the  * rectangle enclosing the sun  * @param width the width of the enclosing rectangle  * @param height the height of the enclosing rectangle  */ public void drawSun(int x, int y, int width, int height) {   // get the graphics2D object for this picture   Graphics g = this.getGraphics();   Graphics2D g2 = (Graphics2D) g;   // create the gradient for painting from yellow to red with   // yellow at the top of the sun and red at the bottom   float xMid = (float) (width / 0.5 + x);   GradientPaint gPaint = new GradientPaint(xMid, y,                                            Color.yellow,                                            xMid, y + height,                                            Color.red);   // set the gradient and draw the ellipse   g2.setPaint(gPaint);   g2.fill(new Ellipse2D.Double(x,y,width,height)); }


You can use this program to add a sun to the beach (Figure 7.17).


[Page 241]
> Picture p = new Picture(FileChooser.getMediaPath("beach.jpg")); > p.drawSun(201,80,40,40); > p.show();


Figure 7.17. A beach with a sun that is filled with a gradient from yellow to red.
(This item is displayed on page 240 in the print version)


7.3.7. Blending Pictures Using AlphaComposite

In the blendPictures method on page 156 we blended two pictures by multiplying the pixel color values by 0.5 and adding them. It is also possible to use an AlphaComposite object from package java.awt to do blending or transparency effects. The default is to replace the color at overlapping pixels with the new color. To modify this default you can get an AlphaComposite object that specifies how you want to handle overlapping pixels in the source and destination using the class method AlphaComposite.getInstance(int rule, float alpha) that returns an AlphaComposite object to handle the overlap. For the rule you can use one of the predefined constants such as SRC_OVER which draws the new pixels on top of the old pixels. The alpha value can be between 0.0 and 1.0 where 0.0 is fully transparent (invisible) and 1.0 is fully opaque. If we use an alpha value of 0.5 we will achieve the same effect as the method blendPictures without having to loop through all the pixels and calculate the resulting color.

To overlap two pictures, we will draw the part of the first picture before a given overlap point using one of the drawImage methods inherited from Graphics that specifies the rectangular area to draw to and the rectangular area to draw from. We will draw the first part using an AlphaComposite that uses an alpha value of 1.0 (opaque) so that it replaces any previous pixel color. Next we will draw the parts of the two pictures that overlap using an alpha value of 0.5 so that it uses half the color from the image being drawn and half the color from the current image. Finally we will draw the part of the second picture that is after the overlapping area using an alpha value of 1.0 (opaque).

Program 63. Overlap Pictures Using AlphaComposite
(This item is displayed on pages 241 - 242 in the print version)

/**  * Method to overlap one picture with another  * horizontally on top of the current picture.   First  * the part of the first picture before the overlap  * will be displayed, next to that will be the overlapping  * region up to the end of the first picture, after that  * is the remainder of the second picture  * @param p1 the first picture to display  * @param p2 the second picture to display  * @param startOverlap the x position where the overlap begins  */ public void overlapPictures(Picture p1,                             Picture p2,                             int startOverlap) 
[Page 242]
{ int amountOverlap = p1.getWidth() - startOverlap; // get the Graphics2D object Graphics g = this.getGraphics(); Graphics2D g2 = (Graphics2D) g; // draw p1 up to overlap point g2.setComposite( AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) 1.0f)); g2.drawImage(p1.getImage(), 0,0,startOverlap,p1.getHeight(), 0,0,startOverlap,p1.getHeight(), null); // draw p1 in the overlap area (replace background) g2.drawImage(p1.getImage(), startOverlap,0,p1.getWidth(),p1.getHeight(), startOverlap,0,p1.getWidth(),p1.getHeight(), null); // set the composite to blend the old and new pixels // 50% g2.setComposite( AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g2.drawImage(p2.getImage(), startOverlap,0,p1.getWidth(),p2.getHeight(), 0,0,amountOverlap,p2.getHeight(),null); // draw p2 after the overlap g2.setComposite( AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) 1.0f)); g2.drawImage(p2.getImage(), p1.getWidth(),0,p2.getWidth() + startOverlap, p2.getHeight(), amountOverlap, 0, p2.getWidth(), p2.getHeight(),null); }


To use this program to overlap two pictures (Figure 7.18), do:

> String fileName = FileChooser.getMediaPath("KatieFancy.jpg"); > Picture p1 = new Picture(fileName); > Picture p2 = new Picture(     FileChooser.getMediaPath("JenParty.jpg")); > Picture p3 = new Picture(     FileChooser.getMediaPath("640x480.jpg")); > p3.overlapPictures(p1,p2,150); > p3.show();



[Page 243]

Figure 7.18. Two pictures with a horizontal overlap.


You can experiment with the alpha amount that you use in the method getInstance. If you specify 0.5, then half of the source color values will be combined with half of the target color values. If you specify 1.0, then the source color will replace the target color. If you use 0.25, then only 0.25 of the source color will be combined with 0.75 of the target color.

7.3.8. Clipping

You can create one shape and then use that shape as a stencil to limit what is shown when you draw other shapes or images. Only the area inside of the stencil will be seen. This is called clipping.

Let's create a stencil from an ellipse and then draw the beach using that stencil to clip the image of the beach. The only part of the beach that will be visible is the part inside the ellipse (Figure 7.19).


[Page 244]

Figure 7.19. Clip picture using an ellipse.


Program 64. Clip an Image to an Ellipse

/**  * Method to clip the picture to an ellipse  * @return a new picture with the image clipped  * to an ellipse  */ public Picture clipToEllipse() {   int width = this.getWidth();   int height = this.getHeight();   Picture result = new Picture(width,height);   // get the graphics2D object for this picture   Graphics g = result.getGraphics();   Graphics2D g2 = (Graphics2D) g;   // create an ellipse to use for clipping   Ellipse2D.Double ellipse =       new Ellipse2D.Double(0,0,width,height);   // use the ellipse for clipping   g2.setClip(ellipse);   // draw the image   g2.drawImage(this.getImage(),0,0,width,                height,null);   // return the result   return result; }



[Page 245]

To use this program try the following:

> Picture p = new Picture(FileChooser.getMediaPath("beach.jpg")); > Picture p2 = p.clipToEllipse(); > p2.show();




Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
ISBN: N/A
EAN: N/A
Year: 2007
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net