Main Page

Previous Next

Filling Shapes

Once you know how to create and draw a shape, filling it is easy. You just call the fill() method for the Graphics2D object and pass a reference of type Shape to it. This works for any shape but for sensible results the boundary should be closed.

Let's try it out by modifying the applet example that displayed stars.

Try It Out – Filling Stars

To fill the stars we just need to call the fill() method for each star in the paint() method of the StarPane object. Modify the paint() method as follows:

    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D)g;
      Star star = new Star(0,0);                 // Create a star
      float delta = 60;                          // Increment between stars
      float starty = 0;                          // Starting y position

      // Draw 3 rows of 4 stars
      for(int yCount = 0 ; yCount<3; yCount++) {
        starty += delta;                        // Increment row position
        float startx = 0;                       // Start x position in a row

        // Draw a row of 4 stars
        for(int xCount = 0 ; xCount<4; xCount++) {
          g2D.setPaint(Color.blue);            // Drawing color blue
          g2D.draw(star.atLocation(startx += delta, starty));
          g2D.setPaint(Color.green);           // Color for fill is green
          g2D.fill(star.getShape());           // Fill the star
        }
      }
    }

We also need an import statement for the Color class name:

import java.awt.Color;

Now the applet window will look something like that shown here – but in color of course.

Click To expand

How It Works

We set the color for drawing and filling the stars separately, simply to show that we can get both. The stars are displayed in green with a blue boundary. You can fill a shape without drawing it – just call the fill() method. You could amend the example to do this by modifying the inner loop to:

        for(int xCount = 0 ; xCount<4; xCount++) {
          g2D.setPaint(Color.GREEN);                    // Color for fill is green
          g2D.fill(star.atLocation(startx += delta, starty)); // Fill the star
        }

Now all we will get is the green fill for each shape – no outline.

Gradient Fill

You are not limited to filling a shape with a uniform color. You can create a GradientPaint object that represents a graduation in shade from one color to another and pass that to the setPaint() method for the graphics context. There are four GradientPaint class constructors:

Constructor

Description

GradientPaint
(Point2D p1, Color c1, Point2D p2, Color c2)

Defines a gradient from point p1 with the color c1 to the point p2 with the color c2. The color varies linearly from color c1 at point p1 to color c2 at point p2.

By default the gradient is acyclic, which means the color variation only applies between the two points. Beyond either end of the line the color is the same as the nearest end point.

GradientPaint
(float x1, float y1, Color c1, float x2, float y2, Color c2)

The same as the previous constructor but with the points specified by their coordinates.

GradientPaint
(Point2D p1, Color c1, Point2D p2, Color c2, boolean cyclic)

With the last argument specified as false, this is identical to the first constructor. If you specify cyclic as true, the color gradation repeats cyclically off either end of the line – that is you get repetitions of the color gradient in both directions.

GradientPaint
(float x1, float y1, Color c1, float x2, float y2, Color c2, boolean cyclic)

This is the same as the previous constructor except for the explicit point coordinates.

Points off the line defining the color gradient will have the same color as the normal (that is, right-angle) projection of the point onto the line.

This stuff is easier to demonstrate than to describe, so here's the output from the example we're about to code:

Click To expand

Try It Out – Color Gradients

We create an example similar to our star applet that will draw rectangles with GradientPaint fills. Here's the complete code:

import javax.swing.JComponent;
import javax.swing.JApplet;

import java.awt.*;
import java.awt.geom.*;

public class GradientApplet extends JApplet {
  // Initialize the applet
  public void init() {
    getContentPane().add(pane);         // BorderLayout.CENTER is default position
  }

  // Class defining a pane on which to draw
  class GradientPane extends JComponent {
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D)g;

      Point2D.Float p1 = new Point2D.Float(150.f, 75.f);    // Gradient line start
      Point2D.Float p2 = new Point2D.Float(250.f, 75.f);    // Gradient line end
      float width = 300;
      float height = 50;
      GradientPaint g1 = new GradientPaint(p1, Color.white,
                                           p2, Color.DARK_GRAY,
                                           true);           // Cyclic gradient
      Rectangle2D.Float rect1 = new Rectangle2D.Float(
                                   p1.x-100, p1.y-25, width,height);
      g2D.setPaint(g1);                                     // Gradient color fill
      g2D.fill(rect1);                                      // Fill the rectangle
      g2D.setPaint(Color.black);                            // Outline in black
      g2D.draw(rect1);                                      // Fill the rectangle
      g2D.draw(new Line2D.Float(p1, p2));
      g2D.drawString("Cyclic Gradient Paint", p1.x-100, p1.y-50);
      g2D.drawString("p1", p1.x-20, p1.y);
      g2D.drawString("p2", p2.x+10, p2.y);

      p1.setLocation(150, 200);
      p2.setLocation(250, 200);
      GradientPaint g2 = new GradientPaint(p1, Color.white,
                                           p2, Color.DARK_GRAY,
                                           false);          // Acyclic gradient
      rect1.setRect(p1.x-100, p1.y-25, width, height);
      g2D.setPaint(g2);                                     // Gradient color fill
      g2D.fill(rect1);                                      // Fill the rectangle
      g2D.setPaint(Color.black);                            // Outline in black
      g2D.draw(rect1);                                      // Fill the rectangle
      g2D.draw(new Line2D.Float(p1, p2));
      g2D.drawString("Acyclic Gradient Paint", p1.x-100, p1.y-50);
      g2D.drawString("p1", p1.x-20, p1.y);
      g2D.drawString("p2", p2.x+10, p2.y);
    }
  }

  GradientPane pane = new GradientPane();  // Pane containing filled rectangles
}

If you run this applet with the following HTML, you should get the window shown above.

<applet code="GradientApplet.class" width=400 height=280></applet>

Note that to get a uniform color gradation, your monitor needs to be set up for at least 16 bit (65536 colors) colors, preferably 24 bits (16.7 million colors).

How It Works

To import the individual class names that are used in this example needs nine import statements so here we just import all the class names in each of the three packages. As a rule, it is better practice to only import the class names that you use in your code, but we will use the * form to import all the names in a package when this reduces the number of import statements significantly.

The applet displays two rectangles, and they are annotated to indicate which is which. The applet also displays the gradient lines, which lie in the middle of the rectangles. You can see the cyclic and acyclic gradients quite clearly. You can also see how points off the gradient line have the same color as the normal projection onto the line.

The first block of shaded code in the paint() method creates the upper rectangle where the GradientPaint object that is used is g1. This is created as a cyclic gradient between the points p1 and p2 varying from white to dark gray. These shades have been chosen because the book is in black and white, but you can try any color combination you like. To set the color gradient for the fill, we call setPaint() for the Graphics2D object and pass g1 to it. Any shapes drawn and/or filled subsequent to this call will use the gradient color, but here we just fill the rectangle, rect1.

To make the outline and the annotation clearer, we set the current color back to black before calling the draw() method to draw the outline of the rectangle, and the drawString() method to annotate it.

The code for the lower rectangle is essentially the same as that for the first. The only important difference is that we specify the last argument to the constructor as false to get an acyclic gradient. This causes the colors of the ends of the gradient line to be the same as the end points. We could have omitted the Boolean parameter here, and got an acyclic gradient by default.

The applet shows how points off the gradient line have the same color as the normal projection onto the line. This is always the case regardless of the orientation of the gradient line. Try changing the definition of g1 for the upper rectangle to:

GradientPaint g1 = new GradientPaint(p1.x, p1.y – 20, Color.white,
                                     p2.x, p2.y + 20, Color.DARK_GRAY,
                                     true);               // Cyclic gradient

You will also need to draw the gradient line in its new orientation:

g2D.draw(rect1);                                      // Fill the rectangle
      
//g2D.draw(new Line2D.Float(p1, p2));
g2D.draw(new Line2D.Float(p1.x, p1.y - 20, p2.x, p2.y + 20));

The annotation for the end points will also have to be moved:

g2D.drawString("p1",p1.x – 20,p1.y – 20);
g2D.drawString("p2",p2.x + 10,p2.y + 20);

If you run the applet with these changes, you can see how the gradient is tilted, and how the colors of a point off the gradient line matches that of the point that is the orthogonal projection onto it.

Click To expand
Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor