# Java2D Graphics Guide



## JGuru (Nov 9, 2014)

*Java2D Graphics Guide*

This guide showcases many of the features of Java2D. You can use Java2D to create some spectacular animations & effects.
Read on to know more about Java2D.

*Java 2DTM API Overview*

The Java 2D™ API enhances the graphics, text, and imaging capabilities of the Abstract Windowing Toolkit (AWT), enabling the development of richer user interfaces and new types of Java™ applications.

Along with these richer graphics, font, and image APIs, the Java 2D API supports enhanced color definition and composition, hit detection on arbitrary geometric shapes and text, and a uniform rendering model for printers and display devices.

The Java 2D API also enables the creation of advanced graphics libraries, such as CAD-CAM libraries and graphics or imaging special effects libraries, as well as the creation of image and graphic file read/write filters.

When used in conjunction with the Java Media Framework and other Java Media APIs, the Java 2D APIs can be used to create and display animations and other multimedia presentations. The Java Animation and Java Media Framework APIs rely on the Java 2D API for rendering support.
Enhanced Graphics, Text, and Imaging

Early versions of the AWT provided a simple rendering package suitable for rendering common HTML pages, but not full-featured enough for complex graphics, text, or imaging. As a simplified rendering package, the early AWT embodied specific cases of more general rendering concepts. The Java 2D™ API provides a more flexible, full-featured rendering package by expanding the AWT to support more general graphics and rendering operations.

For example, through the Graphics class you can draw rectangles, ovals, and polygons. Graphics2D enhances the concept of geometric rendering by providing a mechanism for rendering virtually any geometric shape. Similarly, with the Java 2D API you can draw styled lines of any width and fill geometric shapes with virtually any texture.

Geometric shapes are provided through implementations of the Shape interface, for example Rectangle2D and Ellipse2D. Curves and arcs are also specific implementations of Shape.

Fill and pen styles are provided through implementations of the Paint and Stroke interfaces, for example BasicStroke, GradientPaint, TexturePaint, and Color.

AffineTransform defines linear transformations of 2D coordinates, including scale, translate, rotate, and shear.

Clip regions are defined by the same implementations of the Shape interface that are used to define general clipping regions, for example Rectangle2D and GeneralPath.

Color composition is provided by implementations of the Composite interface, for example AlphaComposite.

A Font is defined by collections of Glyphs, which are in turn defined by individual Shapes.

The Java 2D™ API Packages

The Java 2D API classes are organized into the following packages:

    java.awt

    java.awt.geom

    java.awt.font

    java.awt.color

    java.awt.image

    java.awt.image.renderable

    java.awt.print

Package java.awt contains those Java 2D API classes and interfaces that are general in nature or that enhance legacy classes. (Obviously, not all of the classes in java.awt are Java 2D classes.)

AlphaComposite

BasicStroke

Color

Composite

CompositeContext

Font

GradientPaint

Graphics2D

GraphicsConfiguration

GraphicsDevice

GraphicsEnvironment

Paint

PaintContext

Rectangle

Shape

Stroke

TexturePaint

Transparency

Package java.awt.geom contains classes and interfaces related to the definition of geometric primitives:

AffineTransform

Arc2D

Arc2D.Double

Arc2D.Float

Area

CubicCurve2D

CubicCurve2D.Double

CubicCurve2D.Float

Dimension2D

Ellipse2D

Ellipse2D.Double

Ellipse2D.Float

FlatteningPathIterator

GeneralPath

Line2D

Line2D.Double

Line2D.Float

PathIterator

Point2D

Point2D.Double

Point2D.Float

QuadCurve2D

QuadCurve2D.Double

QuadCurve2D.Float

Rectangle2D

Rectangle2D.Double

Rectangle2D.Float

RectangularShape

RoundRectangle2D

RoundRectangle2D.Double

RoundRectangle2D.Float

Many of the geometric primitives have corresponding .Float and .Double implementations. This was done to enable both floating single- and double-precision implementations. Double-precision implementations provide greater rendering precision at the expense of performance on some platforms.

Package java.awt.font contains classes and interfaces used for text layout and the definition of fonts:

FontRenderContext

GlyphJustificationInfo

GlyphMetrics

GlyphVector

GraphicAttribute

ImageGraphicAttribute

LineBreakMeasurer

LineMetrics

MultipleMaster

OpenType

ShapeGrapicAttribute

TextAttribute

TextHitInfo

TextLayout

TransformAttribute

Package java.awt.color contains classes and interfaces for the definition of color spaces and color profiles:

ColorSpace 

ICC_ColorSpace

ICC_Profile

ICC_ProfileGray

ICC_ProfileRGB

The java.awt.image and java.awt.image.renderable packages contain classes and interfaces for the definition and rendering of images:

AffineTransformOp

BandCombineOp

BandedSampleModel

BufferedImage

BufferedImageFilter

BufferedImageOp

ByteLookupTable

ColorConvertOp

ColorModel

ComponentColorModel

ComponentSampleModel

ConvolveOp

ContextualRenderedImageFactory

DataBuffer

DataBufferByte

DataBufferInt

DataBufferShort

DataBufferUShort

DirectColorModel

IndexColorModel

Kernel

LookupOp

LookupTable

MultiPixelPackedSampleModel

PackedColorModel

ParameterBlock

PixelInterleavedSampleModel

Raster

RasterOp

RenderableImage

RenderableImageOp

RenderableImageProducer

RenderContext

RenderedImageFactory

RenderedImage

RescaleOp

SampleModel

ShortLookupTable

SinglePixelPackedSampleModel

TileObserver

WritableRaster

WritableRenderedImage

Package java.awt.image was present in earlier versions of the AWT. The Java 2D API enhances the following legacy AWT image classes:

    ColorModel

    DirectColorModel

    IndexColorModel

These color model classes remain in the java.awt.image package for backward compatibility. To maintain consistency, the new color model classes are also located in the java.awt.image package.

Package java.awt.print contains classes and interfaces that enable printing of all Java 2D–based text, graphics, and images

*Rendering with Graphics2D*

Graphics2D extends java.awt.Graphics to provide more sophisticated control over the presentation of shapes, text, and images. The Java 2D™ rendering process is controlled through the Graphics2D object and its state attributes.

The Graphics2D state attributes, such as line styles and transformations, are applied to graphic objects when they are rendered. The collection of state attributes associated with a Graphics2D is referred to as the Graphics2D context. To render text, shapes, or images, you set up the Graphics2D context and then call one of the Graphics2D rendering methods, such as draw or fill.
Interfaces and Classes

The following tables list the interfaces and classes used in conjunction with the Graphics2D context, including the classes that represent state attributes. Most of these classes are part of the java.awt package.

Interface     Description

Composite  Defines methods to compose a draw primitive with the underlying graphics area. Implemented by AlphaComposite.

CompositeContext Defines the encapsulated and optimized environment for a composite operation. Used by programmers implementing custom compositing rules.

Paint      Extends: Transparency
           Defines colors for a draw or fill operation. Implemented by Color, GradientPaint and TexturePaint.

PaintContext Defines the encapsulated and optimized environment for a paint operation. Used by programmers implementing custom paint operations.

Stroke  Generates a Shape that encloses the outline of the Shape to be rendered. Implemented by BasicStroke.


*Class         Description*

AffineTransform (java.awt.geom)Represents a 2D affine transform, which performs a linear mapping from 2D coordinates to other 2D coordinates.

AlphaComposite Implements: Composite Implements basic alpha composite rules for shapes, text, and images.

BasicStroke    Implements: Stroke Defines the “pen style” to be applied to the outline of a Shape.

Color          Implements: Paint Defines a solid color fill for a Shape.

GradientPaint  Implements: Paint Defines a linear color gradient fill pattern for a Shape. This fill pattern changes from color C1 at point P1 to color C2 at point P2.

Graphics2D     Extends: Graphics Fundamental class for 2D rendering. Extends the original java.awt.Graphics class.

TexturePaint   Implements: Paint Defines a texture or pattern fill for a Shape. The texture or pattern is generated from a BufferedImage.

Rendering Concepts

To render a graphic object using the Java 2D™ API, you set up the Graphics2D context and pass the graphic object to one of the Graphics2D rendering methods.

You can modify the state attributes that form the Graphics2D context to:

    Vary the stroke width.

    Change how strokes are joined together.

    Set a clipping path to limit the area that is rendered.

    Translate, rotate, scale, or shear objects when they are rendered.

    Define colors and patterns to fill shapes with.

    Specify how multiple graphics objects should be composed.

Graphics2D defines several methods for adding and changing attributes in the graphics context. Most of these methods take an object that represents a particular attribute, such as a Paint or Stroke object.

The Graphics2D context holds references to these attribute objects: they are not cloned. If you alter an attribute object that is part of the Graphics2D context, you need to call the appropriate set method to notify the context. Modifying an attribute object during a rendering operation will cause unpredictable and possibly unstable behavior.
2.2.1 Rendering Process

When a graphic object is rendered, the geometry, image, and attribute information are combined to calculate which pixel values must be changed on the display.

The rendering process for a Shape can be broken down into four steps:

    If the Shape is to be stroked, the Stroke attribute in the Graphics2D context is used to generate a new Shape that encompasses the stroked path.

    The coordinates of the Shape’s path are transformed from user space into device space according to the transform attribute in the Graphics2D context.

    The Shape’s path is clipped using the clip attribute in the Graphics2D context.

    The remaining Shape, if any, is filled using the Paint and Composite attributes in the Graphics2D context.

Rendering text is similar to rendering a Shape, since the text is rendered as individual glyphs and each glyph is a Shape. The only difference is that the Java 2D API must determine what Font to apply to the text and get the appropriate glyphs from the Font before rendering.

Images are handled differently, transformations and clipping operations are performed on the image’s bounding box. The color information is taken from the image itself and its alpha channel is used in conjunction with the current Composite attribute when the image pixels are composited onto the rendering surface.
2.2.2 Controlling Rendering Quality

The Java 2D API lets you indicate whether you want objects to be rendered as quickly as possible, or whether you prefer that the rendering quality be as high as possible. Your preferences are specified as hints through the RenderingHints attribute in the Graphics2D context. Not all platforms support modification of the rendering mode so specifying rendering hints does not guarantee that they will be used.

The RenderingHints class supports the following types of hints:

    Alpha interpolation—can be set to default, quality, or speed.

    Antialiasing—can be set to default, on, or off.

    Color Rendering–can be set to default, quality, or speed.

    Dithering—can be set to default, disable, or enable.

    Fractional Metrics—can be set to default, on, or off.

    Interpolation—can be set to nearest-neighbor, bilinear, or bicubic.

    Rendering—can be set to default, quality, or speed.

    Text antialiasing—can be set to default, on, or off.

To set or change the RenderingHints attribute in the Graphics2D context, you call setRenderingHints. When a hint is set to default, the platform rendering default is used is used.

Transformations

The Graphics2D context contains a transform that is used to transform objects from user space to device space during rendering. To perform additional transformations, such as rotation or scaling, you can add other transforms to the Graphics2D context. These additional transforms become part of the pipeline of transformations applied during rendering.

Graphics2D provides several different ways to modify the transform in the Graphics2D context. The simplest is to call one of the Graphics2D transformation methods: rotate, scale, shear, or translate. You specify the characteristics of the transform that you want to be applied during rendering, and Graphics2D automatically makes the appropriate changes.

You can also explicitly concatenate an AffineTransform with the current Graphics2D transform. An AffineTransform performs a linear transformation such as translation, scaling, rotation, or shearing on a set of graphics primitives. When a transform is concatenated with an existing transform, the last transform specified is the first to be applied. To concatenate a transform with the current transform, you pass an AffineTransform to Graphics2D.transform.

The Graphics2D class also contains a setTransform method, but this method should never be used to concatenate another coordinate transform onto of an existing transform. The setTransform method overwrites the Graphics2D object’s current transform, which is needed for other purposes, such as:

    Applying a scaling transform to adjust for printer resolution.

    Painting a JComponent at non-zero translation from its parent’s origin

    Scaling up a component for easier viewing.

    Any other situation in which the supplier of a Graphics2D object might want to transform the rendering for effect .

The setTransform method is intended for setting the Graphics2D object back to the original transform after rendering the transformed graphics, text or images:


```
AffineTransform aT = g2d.getTransform(); 
g2d.transform(...);g2d.draw(...); 
g2d.setTransform(aT);
```

Graphics2D also provides a version of drawImage that takes an AffineTransform as a parameter. This enables you to apply a transformation to an image object when it is drawn without permanently modifying the transformation pipeline. The image is drawn as if you had concatenated the transform with the current transform in the Graphics2D context.

*Affine Transforms*

The Java 2D API provides one transform class, AffineTransform. AffineTransforms are used to transform text, shapes, and images when they are rendered. You can also apply transforms to Font objects to create new font derivations, as discussed in “Creating Font Derivations” on page 65.

An affine transformation performs a linear transformation on a set of graphics primitives. It always transforms straight lines into straight lines and parallel lines into parallel lines; however, the distance between points and the angles between nonparallel lines might be altered.

*Filling a Shape with a Texture*

The TexturePaint class provides an easy way to fill a shape with a repeating pattern. When you create a TexturePaint, you specify a BufferedImage to use as the pattern. You also pass the constructor a rectangle to define the repetition frequency of the pattern,

To fill a shape with a texture:

    Create a TexturePaint object.

    Call Graphics2D.setPaint.

    Create the Shape.

    Call Graphics2D.fill(shape).

In the following example, a rectangle is filled with a simple texture created from a buffered image.


```
// Create a buffered image texture patch of size 5x5 
BufferedImage bi = new BufferedImage(5, 5,   
                       BufferedImage.TYPE_INT_RGB); 
Graphics2D big = bi.createGraphics(); 
// Render into the BufferedImage graphics to create the texture 
big.setColor(Color.green); 
big.fillRect(0,0,5,5); 
big.setColor(Color.lightGray); 
big.fillOval(0,0,5,5); 
 
// Create a texture paint from the buffered image 
Rectangle r = new Rectangle(0,0,5,5); 
TexturePaint tp = new TexturePaint(bi,r,TexturePaint.NEAREST_NEIGHBOR); 
 
// Add the texture paint to the graphics context. 
g2.setPaint(tp); 
 
// Create and render a rectangle filled with the texture. 
g2.fillRect(0,0,200,200); 
}
```

The Texture class demonstrates creating TexturePaint objects with shapes,
gradients and rgb values.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import javax.swing.*;

;

/**
 * The Texture class demonstrates creating TexturePaint objects with shapes,
 * gradients and rgb values.
 */
public class Texture extends JApplet {

    private static TexturePaint bluedots, greendots, triangles;
    private static TexturePaint blacklines, gradient;

    // creates the TexturePaint objects
    static {
        BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
        Graphics2D gi = bi.createGraphics();
        gi.setBackground(Color.white);
        gi.clearRect(0, 0, 10, 10);

        // creates the triangles TexturePaint
        GeneralPath p1 = new GeneralPath();
        p1.moveTo(0, 0);
        p1.lineTo(5, 10);
        p1.lineTo(10, 0);
        p1.closePath();
        gi.setColor(Color.lightGray);
        gi.fill(p1);
        triangles = new TexturePaint(bi, new Rectangle(0, 0, 10, 10));

        // creates the blacklines TexturePaint
        bi = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB);
        gi = bi.createGraphics();
        gi.setColor(Color.black);
        gi.fillRect(0, 0, 5, 5);
        gi.setColor(Color.gray);
        gi.fillRect(1, 1, 4, 4);
        blacklines = new TexturePaint(bi, new Rectangle(0, 0, 5, 5));

        // creates the gradient TexturePaint
        int w = 30;
        int h = 30;
        bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        gi = bi.createGraphics();
        Color oc = Color.white;
        Color ic = Color.lightGray;
        gi.setPaint(new GradientPaint(0, 0, oc, w * .35f, h * .35f, ic));
        gi.fillRect(0, 0, w / 2, h / 2);
        gi.setPaint(new GradientPaint(w, 0, oc, w * .65f, h * .35f, ic));
        gi.fillRect(w / 2, 0, w / 2, h / 2);
        gi.setPaint(new GradientPaint(0, h, oc, w * .35f, h * .65f, ic));
        gi.fillRect(0, h / 2, w / 2, h / 2);
        gi.setPaint(new GradientPaint(w, h, oc, w * .65f, h * .65f, ic));
        gi.fillRect(w / 2, h / 2, w / 2, h / 2);
        gradient = new TexturePaint(bi, new Rectangle(0, 0, w, h));

        // creates the bluedots TexturePaint
        bi = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB);
        bi.setRGB(0, 0, 0xffffffff);
        bi.setRGB(1, 0, 0xffffffff);
        bi.setRGB(0, 1, 0xffffffff);
        bi.setRGB(1, 1, 0xff0000ff);
        bluedots = new TexturePaint(bi, new Rectangle(0, 0, 2, 2));

        // creates the greendots TexturePaint
        bi = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB);
        bi.setRGB(0, 0, 0xffffffff);
        bi.setRGB(1, 0, 0xffffffff);
        bi.setRGB(0, 1, 0xffffffff);
        bi.setRGB(1, 1, 0xff00ff00);
        greendots = new TexturePaint(bi, new Rectangle(0, 0, 2, 2));
    }

    @Override
    public void init() {
        setBackground(Color.white);
    }

    public void drawDemo(int w, int h, Graphics2D g2) {

        Rectangle r = new Rectangle(10, 10, w - 20, h / 2 - 20);

        g2.setPaint(gradient);
        g2.fill(r);

        g2.setPaint(Color.green);
        g2.setStroke(new BasicStroke(20));
        g2.draw(r);

        g2.setPaint(blacklines);
        g2.setStroke(new BasicStroke(15));
        g2.draw(r);

        Font f = new Font("Times New Roman", Font.BOLD, w / 5);
        TextLayout tl = new TextLayout("Texture", f, g2.getFontRenderContext());
        int sw = (int) tl.getBounds().getWidth();
        int sh = (int) tl.getBounds().getHeight();
        Shape sha = tl.getOutline(AffineTransform.getTranslateInstance(w / 2 - sw / 2, h * .25 + sh / 2));

        g2.setColor(Color.black);
        g2.setStroke(new BasicStroke(3));
        g2.draw(sha);

        g2.setPaint(greendots);
        g2.fill(sha);

        // resets location of rectangle and fills with triangles Texture
        r.setLocation(10, h / 2 + 10);
        g2.setPaint(triangles);
        g2.fill(r);

        g2.setPaint(blacklines);
        g2.setStroke(new BasicStroke(20));
        g2.draw(r);

        g2.setPaint(Color.green);
        g2.setStroke(new BasicStroke(4));
        g2.draw(r);

        f = new Font("serif", Font.BOLD, w / 4);
        tl = new TextLayout("Paint", f, g2.getFontRenderContext());
        sw = (int) tl.getBounds().getWidth();
        sh = (int) tl.getBounds().getHeight();
        sha = tl.getOutline(AffineTransform.getTranslateInstance(w / 2 - sw / 2, h * .75 + sh / 2));

        // strokes outline shape of "Paint" with black
        g2.setColor(Color.black);
        g2.setStroke(new BasicStroke(5));
        g2.draw(sha);

        // fills outline shape of "Paint" with bluedots Texture
        g2.setPaint(bluedots);
        g2.fill(sha);
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Dimension d = getSize();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, d.width, d.height);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        drawDemo(d.width, d.height, g2);
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final Texture demo = new Texture();
                demo.init();
                JFrame f = new JFrame("Texture");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add("Center", demo);
                f.setSize(new Dimension(800, 600));
                f.setVisible(true);
            }
        });
    }
}
```

*s29.postimg.org/ka9n5bjxv/Texture.jpg

The TextureAnim class demonstrates TexturePaint animation with controls for
selecting the transformations to demonstrate.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;

/**
 * The TextureAnim class demonstrates TexturePaint animation with controls for
 * selecting the transformations to demonstrate.
 */
public class TextureAnim extends JApplet {

    Demo demo;

    @Override
    public void init() {
        add(demo = new Demo());
        add("North", new DemoControls(demo));
    }

    @Override
    public void start() {
        demo.start();
    }

    @Override
    public void stop() {
        demo.stop();
    }

    /**
     * The Demo class performs animation and painting.
     */
    static final class Demo extends JPanel implements Runnable {

        // a blue color that is halfway transparent
        public static final Color colorblend = new Color(0f, 0f, 1f, .5f);
        public boolean bouncesize = false;
        public boolean bouncerect = true;
        public boolean rotate = false;
        public boolean shearx = false;
        public boolean sheary = false;
        public boolean showanchor = true;
        private BufferedImage bimgTP;
        private Rectangle tilerect;
        private TexturePaint texture;
        private boolean newtexture;
        private int tilesize;
        private AnimVal w, h, x, y, rot, shx, shy;
        private Thread thread;
        private BufferedImage bimg;

        public Demo() {
            makeImage(32);

            // the size of the BufferedImage containing the texture
            tilesize = bimgTP.getWidth();

            // initializes the size and location of the texture anchor
            w = new AnimVal(0, 200, 3, 10, tilesize);
            h = new AnimVal(0, 200, 3, 10, tilesize);
            x = new AnimVal(0, 200, 3, 10, 0);
            y = new AnimVal(0, 200, 3, 10, 0);

            rot = new AnimVal(-360, 360, 5, 15, 0);
            shx = new AnimVal(-50, 50, 3, 10, 0);
            shy = new AnimVal(-50, 50, 3, 10, 0);

            // the anchor of the TexturePaint
            tilerect = new Rectangle(x.getInt(), y.getInt(),
                    w.getInt(), h.getInt());
            texture = new TexturePaint(bimgTP, tilerect);
        }

        // creates the TexturePaint pattern
        public void makeImage(int size) {
            bimgTP = new BufferedImage(size, size, bimgTP.TYPE_INT_BGR);
            Graphics2D g2d = bimgTP.createGraphics();
            g2d.setColor(Color.white);
            g2d.fillRect(0, 0, size, size);
            for (int j = 0; j < size; j++) {
                float red = j / (float) size;
                for (int i = 0; i < size; i++) {
                    float green = i / (float) size;
                    g2d.setColor(new Color(1.0f - red, 1.0f - green, 0.0f, 1.0f));
                    g2d.drawLine(i, j, i, j);
                }
            }
            newtexture = true;
        }

        // resets the limits of the anchor's coordinates
        public void reset(int width, int height) {
            x.newlimits(-width / 4, width / 4 - w.getInt());
            y.newlimits(-height / 4, height / 4 - h.getInt());
        }

        // adjusts coordinates and size of the texture tile
        public void step(int width, int height) {
            if (tilesize != bimgTP.getWidth()) {
                tilesize = bimgTP.getWidth();
            }

            if (bouncesize) {
                w.anim();
                h.anim();
                x.newlimits(-width / 4, width / 4 - w.getInt());
                y.newlimits(-height / 4, height / 4 - h.getInt());
            } else {
                if (w.getInt() != tilesize) {
                    w.set(tilesize);
                    x.newlimits(-width / 4, width / 4 - w.getInt());
                }
                if (h.getInt() != tilesize) {
                    h.set(tilesize);
                    y.newlimits(-height / 4, height / 4 - h.getInt());
                }
            }

            if (bouncerect) {
                x.anim();
                y.anim();
            }
            if (newtexture
                    || x.getInt() != tilerect.x || y.getInt() != tilerect.y
                    || w.getInt() != tilerect.width || h.getInt() != tilerect.height) {
                newtexture = false;
                int X = x.getInt();
                int Y = y.getInt();
                int W = w.getInt();
                int H = h.getInt();
                tilerect = new Rectangle(X, Y, W, H);
                texture = new TexturePaint(bimgTP, tilerect);
            }
        }

        // performs the transformations and draws the pattern
        public void drawDemo(int width, int height, Graphics2D g2) {

            g2.translate(width / 2, height / 2);
            if (rotate) {
                rot.anim();
                g2.rotate(Math.toRadians(rot.getFlt()));
            } else {
                rot.set(0);
            }
            if (shearx) {
                shx.anim();
                g2.shear(shx.getFlt() / 100, 0.0f);
            } else {
                shx.set(0);
            }
            if (sheary) {
                shy.anim();
                g2.shear(0.0f, shy.getFlt() / 100);
            } else {
                shy.set(0);
            }
            g2.setPaint(texture);
            g2.fillRect(-1000, -1000, 2000, 2000);
            if (showanchor) {
                g2.setColor(Color.black);
                g2.setColor(colorblend);
                g2.fill(tilerect);
            }
        }

        public Graphics2D createGraphics2D(int w, int h) {

            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
                reset(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            thread = null;
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }

        /**
         * The AnimVal class generates new values used to set the coordinates,
         * width and height and transformation factors for the texture.
         */
        static final class AnimVal {

            float curval;
            float lowval;
            float highval;
            float currate;
            float lowrate;
            float highrate;

            public AnimVal(int lowval, int highval,
                    int lowrate, int highrate) {
                this.lowval = lowval;
                this.highval = highval;
                this.lowrate = lowrate;
                this.highrate = highrate;
                this.curval = randval(lowval, highval);
                this.currate = randval(lowrate, highrate);
            }

            public AnimVal(int lowval, int highval,
                    int lowrate, int highrate,
                    int pos) {
                this(lowval, highval, lowrate, highrate);
                set(pos);
            }

            public float randval(float low, float high) {
                return (float) (low + Math.random() * (high - low));
            }

            public float getFlt() {
                return curval;
            }

            public int getInt() {
                return (int) curval;
            }

            public void anim() {
                curval += currate;
                clip();
            }

            public void set(float val) {
                curval = val;
                clip();
            }

            public void clip() {
                if (curval > highval) {
                    curval = highval - (curval - highval);
                    if (curval < lowval) {
                        curval = highval;
                    }
                    currate = -randval(lowrate, highrate);
                } else if (curval < lowval) {
                    curval = lowval + (lowval - curval);
                    if (curval > highval) {
                        curval = lowval;
                    }
                    currate = randval(lowrate, highrate);
                }
            }

            public void newlimits(int lowval, int highval) {
                this.lowval = lowval;
                this.highval = highval;
                clip();
            }
        }  // End AnimVal class

    } // End Demo class

    /**
     * The DemoControls class provides buttons for transforming the texture,
     * showing the blue anchor and dynamically resizing the texture.
     */
    static final class DemoControls extends JPanel implements ActionListener {

        Demo demo;
        JToolBar toolbar;
        JComboBox<String> combo;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            add(toolbar = new JToolBar());
            toolbar.setFloatable(false);
            addTool("BO", "bounce", true);
            addTool("SA", "show anchor", true);
            addTool("RS", "resize", false);
            addTool("RO", "rotate", false);
            addTool("SX", "shear x", false);
            addTool("SY", "shear y", false);
            add(combo = new JComboBox<String>());
            combo.addActionListener(this);
            combo.addItem("8");
            combo.addItem("16");
            combo.addItem("32");
            combo.addItem("64");
            combo.setSelectedIndex(2);
        }

        public void addTool(String str, String toolTip, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(str));
            b.setBackground(state ? Color.green : Color.lightGray);
            b.setSelected(state);
            b.setToolTipText(toolTip);
            b.addActionListener(this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() instanceof JComboBox) {
                int size = Integer.parseInt((String) combo.getSelectedItem());
                demo.makeImage(size);
                return;
            }
            JButton b = (JButton) e.getSource();
            b.setSelected(!b.isSelected());
            b.setBackground(b.isSelected() ? Color.green : Color.lightGray);
            if (b.getText().equals("BO")) {
                demo.bouncerect = b.isSelected();
            } else if (b.getText().equals("SA")) {
                demo.showanchor = b.isSelected();
            } else if (b.getText().equals("RS")) {
                demo.bouncesize = b.isSelected();
            } else if (b.getText().equals("RO")) {
                demo.rotate = b.isSelected();
            } else if (b.getText().equals("SX")) {
                demo.shearx = b.isSelected();
            } else if (b.getText().equals("SY")) {
                demo.sheary = b.isSelected();
            }
        }
    } // End DemoControls class

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final TextureAnim demo = new TextureAnim();
                demo.init();
                JFrame f = new JFrame("TextureAnim");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                f.pack();
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height / 2 + 150));
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

*s8.postimg.org/9u86zd0z5/Texture_Anim.jpg

*Setting the Clipping Path*

To define a clipping path:

    Create a Shape that represents the area you want to render.

    Call Graphics2D.setClip to use the shape as the clipping path for the Graphics2D context.

To shrink the clipping path:

    Create a Shape that intersects the current clipping path.

    Call clip to change the clipping path to the intersection of the current clipping path and the new Shape.

In the following example, a clipping path is created from an ellipse and then modified by calling clip.


```
public void paint(Graphics g) { 
  Graphics2D g2 = (Graphics2D) g; 
 
// The width and height of the canvas 
  int w = getSize().width; 
  int h = getSize().height; 
  // Create an ellipse and use it as the clipping path 
  Ellipse2D e = new Ellipse2D.Float(w/4.0f,h/4.0f, 
                                    w/2.0f,h/2.0f); 
  g2.setClip(e); 
 
  // Fill the canvas. Only the area within the clip is rendered 
  g2.setColor(Color.cyan); 
  g2.fillRect(0,0,w,h); 
 
  // Change the clipping path, setting it to the intersection of 
  // the current clip and a new rectangle. 
  Rectangle r = new Rectangle(w/4+10,h/4+10,w/2-20,h/2-20); 
  g2.clip(r); 
 
  // Fill the canvas. Only the area within the new clip 
  // is rendered 
  g2.setColor(Color.magenta); 
  g2.fillRect(0,0,w,h); 
}
```

Here is a demo that clips an Image

```
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

public class ClipDemo extends JFrame {

    private BufferedImage image;
    private String fileName = "Images/Adel Miller.jpg";
    private int imgWidth, imgHeight;

    public ClipDemo() {
        image = getImage(fileName);
        if (image != null) {
            imgWidth = image.getWidth(null);
            imgHeight = image.getHeight(null);
            setSize(imgWidth, imgHeight);
        }

        setTitle("ClipDemo Demo");
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public BufferedImage getImage(String fileName) {
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
            System.exit(-1);
        }
        return null;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        int width = getWidth();
        int height = getHeight();
        Graphics2D g2d = (Graphics2D) g;
        Arc2D pieArc = new Arc2D.Float(Arc2D.PIE);
        pieArc.setFrame(0, 0, imgWidth, imgHeight);
        pieArc.setAngleStart(0);
        pieArc.setAngleExtent(360);
        // Clip only the arc region
        g2d.setClip(pieArc);
        if (image != null) {
            g2d.drawImage(image, (width - imgWidth) / 2, (height - imgHeight) / 2, this);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ClipDemo();
            }
        });
    }
}
```

*s12.postimg.org/d9ysifvo9/Clip_Demo.jpg

*Setting the Graphics2D Transform*

To transform a Shape, text string, or Image you add a new AffineTransform to the transformation pipeline in the Graphics2D context before rendering. The transformation is applied when the graphic object is rendered.

For example, to draw a rectangle that is rotated 45 degrees:

    Get the current Graphics2D transform before performing any transformations. Always call getTransform on the Graphics2D before adding a transform to the graphics context because the graphics context might already have a transform that is needed for other reasons, such as positioning Swing and lightweight components within a window.

    Get a rotation transform by calling AffineTransform. getRotateInstance.

    Call Graphics2D.transform to add the new transform to the transformation pipeline. Never use the setTransform method to add a new coordinate transform because setTransform will overwrite the current transform in the graphics context.

    Create a Rectangle2D.Float object.

    Call Graphics2D.draw to render the rectangle.

    After you have rendered your transformed rectangle, reset the transform of the Graphics2D back to the original transform that you saved in Step 1 by calling setTransform with the original transform.

In the following example, an instance of AffineTransform is used to rotate a rectangle 45 degrees when it is rendered.


```
AffineTransform aT = g2.getTransform();Rectangle2D rect = new Rectangle2D.Float(1.0,1.0,2.0,3.0); 
AffineTransform rotate45 =   
  AffineTransform.getRotateInstance(Math.PI/4.0,0.0,0.0) 
g2.transform(rotate45); 
g2.draw(rect);g2.setTransform(aT); 

In this example, an AffineTransform is used to rotate a text string around a center point:

// Define the rendering transform 
AffineTransform at = new AffineTransform(); 
// Apply a translation transform to make room for the 
// rotated text. 
at.setToTranslation(400.0, 400.0); 
g2.transform(at); 
// Create a rotation transform to rotate the text 
at.setToRotation(Math.PI / 2.0); 
// Render four copies of the string “Java” at 90 degree angles 
for (int i = 0; i < 4; i++) { 
    g2.drawString(“Java”, 0.0f, 0.0f); 
    g2.transform(at); 
}
```

Here is a demo that demonstrates AffineTranform.


```
// AffineTransform Demo 
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

class AffineTransformDemo extends JPanel {

    private Image image;
    private float mX, mY;
    private int imgWidth, imgHeight;

    AffineTransformDemo() {

        image = getImage("Images/Jelena Jensen.jpg");
        // resize Image faster
        imgWidth = image.getWidth(null) / 4;
        imgHeight = image.getHeight(null) / 4;
        image = image.getScaledInstance(imgWidth, imgHeight, Image.SCALE_SMOOTH);
        Random random = new Random();
        mX = random.nextFloat() * 500;
        mY = random.nextFloat() * 500;
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        setPreferredSize(new Dimension(scrDim.width / 2, scrDim.height / 2 + 100));
    }

    public Image getImage(String fileName) {
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image : " + fileName + "!!");
            System.exit(-1);
        }
        return null;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        // Set RenderingHints properties
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);

        AffineTransform af = new AffineTransform();
        //Must convert the angle to Radians!
        af.rotate(Math.toRadians(-60.0), imgWidth, imgHeight);

        g2d.drawImage(image, 0, 0, this);
        g2d.drawImage(image, AffineTransform.getRotateInstance(Math.toRadians(85), imgWidth, imgHeight), this);
        g2d.drawImage(image, AffineTransform.getRotateInstance(Math.toRadians(35), imgWidth, imgHeight), this);

        g2d.drawImage(image, af, this);
        //Shear the Image
        af.shear(1.25, 3.146);
        g2d.drawImage(image, af, this);
        g2d.setColor(Color.red);
        g2d.setFont(new Font("Dialog", Font.BOLD, 28));
        g2d.drawString("AffineTransform - Scaling, Shearing, Rotation", getSize().width / 5, getSize().height / 2);
    }

    public static void main(String[] arg) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("AffineTransform");
                f.add(new AffineTransformDemo());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s30.postimg.org/nv4qw7dl9/Affine_Transform_Demo.jpg

You can transform an image in the same way—the transform in the Graphics2D context is applied during rendering regardless of the type of graphic object being rendered.

To apply a transform to an image without changing the transform in the Graphics2D context, you can pass an AffineTransform to drawImage:


```
AffineTransform rotate45 =   
  AffineTransform.getRotateInstance(Math.PI/4.0,0.0,0.0) 
g2.drawImage(myImage, rotate45);
```

The TransformAnim class performs animation of shapes, text and images
rotating, scaling, shearing and translating around a surface.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * The TransformAnim class performs animation of shapes, text and images
 * rotating, scaling, shearing and translating around a surface.
 */
public class TransformAnim extends JApplet {

    Demo demo;

    @Override
    public void init() {
        getContentPane().add(demo = new Demo());
        getContentPane().add("East", new DemoControls(demo));
    }

    @Override
    public void start() {
        demo.start();
    }

    @Override
    public void stop() {
        demo.stop();
    }

    /**
     * The Demo class performs the transformations and the painting.
     */
    static class Demo extends JPanel implements Runnable {

        private static TexturePaint texture;

        // creates the TexturePaint pattern
        static {
            BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
            Graphics2D gi = bi.createGraphics();
            gi.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            gi.setColor(Color.red);
            gi.fillOval(0, 0, 9, 9);
            texture = new TexturePaint(bi, new Rectangle(0, 0, 10, 10));
        }

        private static BasicStroke bs = new BasicStroke(6);
        private static Font fonts[] = {
            new Font("Times New Roman", Font.PLAIN, 48),
            new Font("serif", Font.BOLD + Font.ITALIC, 24),
            new Font("Courier", Font.BOLD, 36),
            new Font("Arial", Font.BOLD + Font.ITALIC, 64),
            new Font("Helvetica", Font.PLAIN, 52)};
        private static String strings[] = {
            "Transformation", "Rotate", "Translate",
            "Shear", "Scale"};
        private static String imgs[] = {"duke.gif"};
        private static Paint paints[] = {
            Color.red, Color.blue, texture, Color.green, Color.magenta,
            Color.orange, Color.pink, Color.cyan,
            new Color(0, 255, 0, 128), new Color(0, 0, 255, 128),
            Color.yellow, Color.lightGray, Color.white};
        private Vector vector = new Vector(13);
        private int numShapes, numStrings, numImages;
        private Thread thread;
        private BufferedImage bimg;
        protected boolean doRotate = true;
        protected boolean doTranslate = true;
        protected boolean doScale = true;
        protected boolean doShear;

        public Demo() {
            setBackground(Color.black);

            // initializes numbers of Strings, Images and Shapes
            setStrings(1);
            setImages(2);
            setShapes(10);
        }

        public Image getImage(String name) {
            URL url = TransformAnim.class.getResource(name);
            Image img = getToolkit().getImage(url);
            try {
                MediaTracker tracker = new MediaTracker(this);
                tracker.addImage(img, 0);
                tracker.waitForID(0);
            } catch (InterruptedException e) {
            }
            return img;
        }

        // adds images to a Vector
        public void setImages(int num) {

            if (num < numImages) {
                Vector<Object> v = new Vector<>(vector.size());
                for (int i = 0; i < vector.size(); i++) {
                    if (((ObjectData) vector.get(i)).object instanceof Image) {
                        v.addElement(vector.get(i));
                    }
                }
                vector.removeAll(v);
                v.setSize(num);
                vector.addAll(v);
            } else {
                Dimension d = getSize();
                for (int i = numImages; i < num; i++) {
                    Object obj = getImage(imgs[i % imgs.length]);
                    ObjectData od = new ObjectData(obj, Color.black);
                    od.reset(d.width, d.height);
                    vector.addElement(od);
                }
            }
            numImages = num;
        }

        // adds Strings to a Vector
        public void setStrings(int num) {

            if (num < numStrings) {
                Vector<Object> v = new Vector<>(vector.size());
                for (int i = 0; i < vector.size(); i++) {
                    if (((ObjectData) vector.get(i)).object instanceof TextData) {
                        v.addElement(vector.get(i));
                    }
                }
                vector.removeAll(v);
                v.setSize(num);
                vector.addAll(v);
            } else {
                Dimension d = getSize();
                for (int i = numStrings; i < num; i++) {
                    int j = i % fonts.length;
                    int k = i % strings.length;
                    Object obj = new TextData(strings[k], fonts[j]);
                    ObjectData od = new ObjectData(obj, paints[i % paints.length]);
                    od.reset(d.width, d.height);
                    vector.addElement(od);
                }
            }
            numStrings = num;
        }

        // adds Shapes to a Vector
        public void setShapes(int num) {

            if (num < numShapes) {
                Vector<Object> v = new Vector<>(vector.size());
                for (int i = 0; i < vector.size(); i++) {
                    if (((ObjectData) vector.get(i)).object instanceof Shape) {
                        v.addElement(vector.get(i));
                    }
                }
                vector.removeAll(v);
                v.setSize(num);
                vector.addAll(v);
            } else {
                Dimension d = getSize();
                for (int i = numShapes; i < num; i++) {
                    Object obj = null;
                    switch (i % 7) {
                        case 0:
                            obj = new GeneralPath();
                            break;
                        case 1:
                            obj = new Rectangle2D.Double();
                            break;
                        case 2:
                            obj = new Ellipse2D.Double();
                            break;
                        case 3:
                            obj = new Arc2D.Double();
                            break;
                        case 4:
                            obj = new RoundRectangle2D.Double();
                            break;
                        case 5:
                            obj = new CubicCurve2D.Double();
                            break;
                        case 6:
                            obj = new QuadCurve2D.Double();
                            break;
                    }
                    ObjectData od = new ObjectData(obj, paints[i % paints.length]);
                    od.reset(d.width, d.height);
                    vector.addElement(od);
                }
            }
            numShapes = num;
        }

        // calls ObjectData.reset for each item in vector
        public void reset(int w, int h) {
            for (int i = 0; i < vector.size(); i++) {
                ((ObjectData) vector.get(i)).reset(w, h);
            }
        }

        // calls ObjectData.step for each item in vector
        public void step(int w, int h) {
            for (int i = 0; i < vector.size(); i++) {
                ((ObjectData) vector.get(i)).step(w, h, this);
            }
        }

        public void drawDemo(int w, int h, Graphics2D g2) {

            for (int i = 0; i < vector.size(); i++) {
                ObjectData od = (ObjectData) vector.get(i);
                g2.setTransform(od.at);
                g2.setPaint(od.paint);
                if (od.object instanceof Image) {
                    g2.drawImage((Image) od.object, 0, 0, this);
                } else if (od.object instanceof TextData) {
                    g2.setFont(((TextData) od.object).font);
                    g2.drawString(((TextData) od.object).string, 0, 0);
                } else if (od.object instanceof QuadCurve2D
                        || od.object instanceof CubicCurve2D) {
                    g2.setStroke(bs);
                    g2.draw((Shape) od.object);
                } else if (od.object instanceof Shape) {
                    g2.fill((Shape) od.object);
                }
            }
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
                reset(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            thread = null;
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }

        /**
         * The TextData class creates an Object to store the specified String
         * and Font.
         */
        static class TextData extends Object {

            public String string;
            public Font font;

            public TextData(String str, Font font) {
                string = str;
                this.font = font;
            }
        }

        /**
         * The ObjectData class calculates the transformations and generates
         * random coordinates for all objects and generates random sizes for
         * shapes.
         */
        static class ObjectData extends Object {

            Object object;
            Paint paint;
            static final int UP = 0;
            static final int DOWN = 1;
            double x, y;
            double ix = 5, iy = 3;
            int rotate;
            double scale, shear;
            int scaleDirection, shearDirection;
            AffineTransform at = new AffineTransform();

            // generates random transformation direction and factor
            public ObjectData(Object object, Paint paint) {
                this.object = object;
                this.paint = paint;
                rotate = (int) (Math.random() * 360);
                scale = Math.random() * 1.5;
                scaleDirection = Math.random() > 0.5 ? UP : DOWN;
                shear = Math.random() * 0.5;
                shearDirection = Math.random() > 0.5 ? UP : DOWN;
            }


            /*
             * generates random coordinate values for all objects
             * and generates random size values for shapes
             */
            public void reset(int w, int h) {
                x = Math.random() * w;
                y = Math.random() * h;
                double ww = 20 + Math.random() * ((w == 0 ? 400 : w) / 4);
                double hh = 20 + Math.random() * ((h == 0 ? 300 : h) / 4);
                if (object instanceof Ellipse2D) {
                    ((Ellipse2D) object).setFrame(0, 0, ww, hh);
                } else if (object instanceof Rectangle2D) {
                    ((Rectangle2D) object).setRect(0, 0, ww, ww);
                } else if (object instanceof RoundRectangle2D) {
                    ((RoundRectangle2D) object).setRoundRect(0, 0, hh, hh, 20, 20);
                } else if (object instanceof Arc2D) {
                    ((Arc2D) object).setArc(0, 0, hh, hh, 45, 270, Arc2D.PIE);
                } else if (object instanceof QuadCurve2D) {
                    ((QuadCurve2D) object).setCurve(0, 0, w * .2, h * .4, w * .4, 0);
                } else if (object instanceof CubicCurve2D) {
                    ((CubicCurve2D) object).setCurve(0, 0, 30, -60, 60, 60, 90, 0);
                } else if (object instanceof GeneralPath) {
                    GeneralPath p = new GeneralPath();
                    float size = (float) ww;
                    p.moveTo(-size / 2.0f, -size / 8.0f);
                    p.lineTo(+size / 2.0f, -size / 8.0f);
                    p.lineTo(-size / 4.0f, +size / 2.0f);
                    p.lineTo(+0.0f, -size / 2.0f);
                    p.lineTo(+size / 4.0f, +size / 2.0f);
                    p.closePath();
                    object = p;
                }
            }


            /*
             * calculates new transformation factors for the
             * chosen transformation and the current direction
             */
            public void step(int w, int h, Demo demo) {
                at.setToIdentity();
                if (demo.doRotate) {
                    if ((rotate += 5) == 360) {
                        rotate = 0;
                    }
                    at.rotate(Math.toRadians(rotate), x, y);
                }
                at.translate(x, y);
                if (demo.doTranslate) {
                    x += ix;
                    y += iy;
                    if (x > w) {
                        x = w - 1;
                        ix = Math.random() * -w / 32 - 1;
                    }
                    if (x < 0) {
                        x = 2;
                        ix = Math.random() * w / 32 + 1;
                    }
                    if (y > h) {
                        y = h - 2;
                        iy = Math.random() * -h / 32 - 1;
                    }
                    if (y < 0) {
                        y = 2;
                        iy = Math.random() * h / 32 + 1;
                    }
                }
                if (demo.doScale && scaleDirection == UP) {
                    if ((scale += 0.05) > 1.5) {
                        scaleDirection = DOWN;
                    }
                } else if (demo.doScale && scaleDirection == DOWN) {
                    if ((scale -= .05) < 0.5) {
                        scaleDirection = UP;
                    }
                }
                if (demo.doScale) {
                    at.scale(scale, scale);
                }
                if (demo.doShear && shearDirection == UP) {
                    if ((shear += 0.05) > 0.5) {
                        shearDirection = DOWN;
                    }
                } else if (demo.doShear && shearDirection == DOWN) {
                    if ((shear -= .05) < -0.5) {
                        shearDirection = UP;
                    }
                }
                if (demo.doShear) {
                    at.shear(shear, shear);
                }
            }
        } // End ObjectData class

    } // End Demo class

    /**
     * The DemoControls class provides controls for selecting the amount of
     * Shapes, Strings or Images to display and selecting the transformation to
     * perform.
     */
    static class DemoControls extends JPanel implements ActionListener, ChangeListener {

        Demo demo;
        JSlider shapeSlider, stringSlider, imageSlider;
        Font font = new Font("serif", Font.PLAIN, 10);
        JToolBar toolbar;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            setLayout(new BorderLayout());
            JToolBar bar = new JToolBar(JToolBar.VERTICAL);
            bar.setBackground(Color.gray);
            bar.setFloatable(false);
            shapeSlider = new JSlider(JSlider.HORIZONTAL, 0, 20, demo.numShapes);
            shapeSlider.addChangeListener(this);
            TitledBorder tb = new TitledBorder(new EtchedBorder());
            tb.setTitleFont(font);
            tb.setTitle(String.valueOf(demo.numShapes) + " Shapes");
            shapeSlider.setBorder(tb);
            shapeSlider.setPreferredSize(new Dimension(80, 44));
            bar.add(shapeSlider);
            bar.addSeparator();

            stringSlider = new JSlider(JSlider.HORIZONTAL, 0, 10, demo.numStrings);
            stringSlider.addChangeListener(this);
            tb = new TitledBorder(new EtchedBorder());
            tb.setTitleFont(font);
            tb.setTitle(String.valueOf(demo.numStrings) + " Strings");
            stringSlider.setBorder(tb);
            stringSlider.setPreferredSize(new Dimension(80, 44));
            bar.add(stringSlider);
            bar.addSeparator();

            imageSlider = new JSlider(JSlider.HORIZONTAL, 0, 10, demo.numImages);
            imageSlider.addChangeListener(this);
            tb = new TitledBorder(new EtchedBorder());
            tb.setTitleFont(font);
            tb.setTitle(String.valueOf(demo.numImages) + " Images");
            imageSlider.setBorder(tb);
            imageSlider.setPreferredSize(new Dimension(80, 44));
            bar.add(imageSlider);
            bar.addSeparator();

            toolbar = new JToolBar();
            toolbar.setFloatable(false);
            addButton("T", "translate", demo.doTranslate);
            addButton("R", "rotate", demo.doRotate);
            addButton("SC", "scale", demo.doScale);
            addButton("SH", "shear", demo.doShear);
            bar.add(toolbar);

            add(bar);
        }

        public void addButton(String s, String tt, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(s));
            b.setFont(font);
            b.setSelected(state);
            b.setToolTipText(tt);
            b.setBackground(state ? Color.green : Color.lightGray);
            b.addActionListener(this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JButton b = (JButton) e.getSource();
            b.setSelected(!b.isSelected());
            b.setBackground(b.isSelected() ? Color.green : Color.lightGray);
            if (b.getText().equals("T")) {
                demo.doTranslate = b.isSelected();
            } else if (b.getText().equals("R")) {
                demo.doRotate = b.isSelected();
            } else if (b.getText().equals("SC")) {
                demo.doScale = b.isSelected();
            } else if (b.getText().equals("SH")) {
                demo.doShear = b.isSelected();
            }
        }

        @Override
        public void stateChanged(ChangeEvent e) {
            JSlider slider = (JSlider) e.getSource();
            int value = slider.getValue();
            TitledBorder tb = (TitledBorder) slider.getBorder();
            if (slider.equals(shapeSlider)) {
                tb.setTitle(String.valueOf(value) + " Shapes");
                demo.setShapes(value);
            } else if (slider.equals(stringSlider)) {
                tb.setTitle(String.valueOf(value) + " Strings");
                demo.setStrings(value);
            } else if (slider.equals(imageSlider)) {
                tb.setTitle(String.valueOf(value) + " Images");
                demo.setImages(value);
            }
        }
    } // End DemoControls class

    public static void main(String argv[]) {
        final TransformAnim demo = new TransformAnim();
        demo.init();
        JFrame f = new JFrame("Java 2D - TransformAnim");
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
                demo.start();
            }

            @Override
            public void windowIconified(WindowEvent e) {
                demo.stop();
            }
        });
        f.add("Center", demo);
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        f.setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height / 2 + 150));
        f.setVisible(true);
        demo.start();
    }
}
```

*s12.postimg.org/ogg2g3ord/Transform_Anim.jpg

The WarpImage class warps an image on a CubicCurve2D flattened path.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

/**
 * The WarpImage class warps an image on a CubicCurve2D flattened path.
 */
public class WarpImage extends JApplet implements Runnable {

    private static int iw, ih, iw2, ih2;
    private static Image img;
    private static final int FORWARD = 0;
    private static final int BACK = 1;

    // the points of the curve
    private Point2D pts[];

    // initializes direction of movement forward, or left-to-right
    private int direction = FORWARD;
    private int pNum;
    private int x, y;
    private Thread thread;
    private BufferedImage bimg;
    private static int imgWidth, imgHeight;

    @Override
    public void init() {
        setBackground(Color.white);
        img = getToolkit().getImage(getClass().getResource("Carolyn.jpg"));
        try {
            MediaTracker tracker = new MediaTracker(this);
            tracker.addImage(img, 0);
            tracker.waitForID(0);
        } catch (InterruptedException e) {
        }
        iw = img.getWidth(this);
        ih = img.getHeight(this);
        imgWidth = iw;
        imgHeight = ih;
        iw2 = iw / 2;
        ih2 = ih / 2;
    }

    public void reset(int w, int h) {
        pNum = 0;
        direction = FORWARD;

        // initializes the cubic curve
        CubicCurve2D cc = new CubicCurve2D.Float(
                w * .2f, h * .5f, w * .4f, 0, w * .6f, h, w * .8f, h * .5f);

        // creates an iterator to define the boundary of the flattened curve
        PathIterator pi = cc.getPathIterator(null, 0.1);
        Point2D tmp[] = new Point2D[200];
        int i = 0;

        // while pi is iterating the curve, adds points to tmp array
        while (!pi.isDone()) {
            float[] coords = new float[6];
            switch (pi.currentSegment(coords)) {
                case PathIterator.SEG_MOVETO:
                case PathIterator.SEG_LINETO:
                    tmp[i] = new Point2D.Float(coords[0], coords[1]);
            }
            i++;
            pi.next();
        }
        pts = new Point2D[i];

        // copies points from tmp to pts
        System.arraycopy(tmp, 0, pts, 0, i);
    }

    // gets coordinates from pts and adjusts direction
    public void step(int w, int h) {
        if (pts == null) {
            return;
        }
        x = (int) pts[pNum].getX();
        y = (int) pts[pNum].getY();
        if (direction == FORWARD) {
            if (++pNum == pts.length) {
                direction = BACK;
            }
        }
        if (direction == BACK) {
            if (--pNum == 0) {
                direction = FORWARD;
            }
        }
    }

    /*
     * Scales the image on the fly to fit inside of the destination drawable
     * surface.  Crops the image into quarter pieces, based on the x & y
     * coordinates scales the cropped images.
     */
    public void drawDemo(int w, int h, Graphics2D g2) {
        g2.drawImage(img,
                0, 0, x, y,
                0, 0, iw2, ih2,
                this);
        g2.drawImage(img,
                x, 0, w, y,
                iw2, 0, iw, ih2,
                this);
        g2.drawImage(img,
                0, y, x, h,
                0, ih2, iw2, ih,
                this);
        g2.drawImage(img,
                x, y, w, h,
                iw2, ih2, iw, ih,
                this);
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
        notifyAll();
    }

    @Override
    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final WarpImage demo = new WarpImage();
                demo.init();
                JFrame f = new JFrame("WarpImage");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                f.setSize(new Dimension(imgWidth * 2, imgHeight));
                f.setResizable(false);
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

*s27.postimg.org/n0okzx2b3/Warp_Image.jpg

Transforms can also be applied to a Font to create a modified version of the Font, for more information see “Creating Font Derivations” on page 65.
2.3.6 Specifying a Composition Style

An AlphaComposite encapsulates composition rules that determine how colors should be rendered when one object overlaps another. To specify the composition style for the Graphics2D context, you create an AlphaComposite and pass it into setComposite. The most commonly used is composition style is SRC_OVER.
2.3.6.1 Using the Source Over Compositing Rule

The SRC_OVER compositing rule composites the source pixel over the destination pixel such that the shared pixel takes the color of the source pixel. For example, if you render a blue rectangle and then render a red rectangle that partially overlaps it, the overlapping area will be red. In other words, the object that is rendered last will appear to be on top.

To use the SRC_OVER composition rule:

    Create an AlphaComposite object by calling getInstance and specifying the SRC_OVER rule.

AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER); 

    Call setComposite to add the AlphaComposite object to the Graphics2D context.

    g2.setComposite(ac);

Once the composite object is set, overlapping objects will be rendered using the specified composition rule.
2.3.6.2 Increasing the Transparency of Composited Objects

AlphaComposite allows you to specify an additional constant alpha value that is multiplied with the alpha of the source pixels to increase transparency.

For example, to create an AlphaComposite object that renders the source object 50% transparent, specify an alpha of .5:

AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f); 

In the following example, a source over alpha composite object is created with an alpha of .5 and added to the graphics context, causing subsequent shapes to be rendered 50% transparent.


```
public void paint(Graphics g) { 
  Graphics2D g2 = (Graphics2D) g; 
 
  g2.setColor(Color.red); 
  g2.translate(100,50); 
  // radians=degree * pie / 180 
  g2.rotate((45*java.lang.Math.PI)/180); 
  g2.fillRect(0,0,100,100); 
  g2.setTransform(new AffineTransform());  // set to identity 
  // Create a new alpha composite 
  AlphaComposite ac = 
      AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f); 
  g2.setComposite(ac); 
  g2.setColor(Color.green); 
  g2.fillRect(50,0,100,100); 
  g2.setColor(Color.blue); 
  g2.fillRect(125,75,100,100); 
  g2.setColor(Color.yellow); 
  g2.fillRect(50,125,100,100); 
  g2.setColor(Color.pink); 
  g2.fillRect(-25,75,100,100); 
}
```

The following programs demonstrates reflection of an Image using AlphaComposite.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public final class Reflection extends JComponent {

    private BufferedImage image;
    private int imageWidth, imageHeight;
    private final Dimension scrDim;
    private final Color color = Color.BLACK;

    public Reflection() {
        scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        try {
            image = ImageIO.read(new File("Images/Liz Ashley1.jpg"));
            image = resize(image);
            imageWidth = image.getWidth();
            imageHeight = image.getHeight();
        } catch (IOException e) {
            System.err.println("Error loading Image!!");
            System.exit(-1);
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        // Set RenderingHints properties
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        int width = getWidth();
        int height = getHeight();
        int gap = 10;
        float opacity = 0.4f;
        float fadeHeight = 0.3f;

        g2d.setPaint(new GradientPaint(0, 0, color, 0, height, color));
        g2d.fillRect(0, 0, width, height);
        g2d.translate((width - imageWidth) / 2, height / 2 - imageHeight);
        g2d.drawRenderedImage(image, null);
        g2d.translate(0, 2 * imageHeight + gap);
        g2d.scale(1, -1);

        BufferedImage reflection = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D rg = reflection.createGraphics();
        rg.drawRenderedImage(image, null);
        rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
        rg.setPaint(
                new GradientPaint(0, imageHeight * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f),
                0, imageHeight, new Color(0.0f, 0.0f, 0.0f, opacity)
                )
                );
        rg.fillRect(0, 0, imageWidth, imageHeight);
        rg.dispose();
        g2d.drawRenderedImage(reflection, null);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(imageWidth * 2, imageHeight * 2);
    }

    public BufferedImage resize(BufferedImage image) {
        if (image != null) {
            int imgWidth2 = image.getWidth(null);
            int imgHeight2 = image.getHeight(null);
            double aspect = ((double) imgWidth2) / ((double) imgHeight2);
            int maxWidth = 0, maxHeight = 0;
            // Fix the height , calculate the maxWidth for this
            maxHeight = scrDim.height / 2;
            maxWidth = (int) (((double) maxHeight) * aspect);
            BufferedImage buffImg = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
            Graphics gr = buffImg.getGraphics();
            gr.drawImage(image.getScaledInstance(maxWidth, maxHeight, Image.SCALE_SMOOTH), 0, 0, this);
            gr.dispose();
            return buffImg;
        }
        return null;
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("Reflection");
                f.add(new Reflection());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s2.postimg.org/7129bmvud/Reflection.jpg

Here is another demo for AlphaComposite


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.lang.reflect.Field;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/*
 * AlphaCompositesApplication.java
 *
 * Created on September 18, 2006, 6:46 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

/**
 *
 * @author JGuru
 */
public class AlphaCompositesDemo extends JFrame {

    private CompositePainter painter;
    private JSlider opacity;
    private JComboBox<String> composites;

    /**
     * Creates a new instance of AlphaCompositesApplication
     */
    public AlphaCompositesDemo() {
        super("Alpha Composites");

        add(painter = new CompositePainter(), BorderLayout.CENTER);

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        panel.add(buildCompositeSelector());
        panel.add(buildOpacitySelector());
        add(panel, BorderLayout.SOUTH);

        setSize(400, 300);
        setLocationRelativeTo(null);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private Component buildOpacitySelector() {
        opacity = new JSlider(0, 100, 50);
        opacity.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent changeEvent) {
                changeComposite();
            }
        });
        JPanel panel = new JPanel();
        panel.add(new JLabel("0%"));
        panel.add(opacity);
        panel.add(new JLabel("100%"));
        return panel;
    }

    private Component buildCompositeSelector() {
        composites = new JComboBox<>(new String[]{
            "CLEAR",
            "DST", "DST_ATOP", "DST_IN", "DST_OUT", "DST_OVER",
            "SRC", "SRC_ATOP", "SRC_IN", "SRC_OUT", "SRC_OVER",
            "XOR"
        });
        composites.setSelectedItem("SRC");
        composites.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                changeComposite();
            }
        });
        return composites;
    }

    private void changeComposite() {
        String rule = composites.getSelectedItem().toString();
        try {
            Field ruleField = AlphaComposite.class.getDeclaredField(rule);
            AlphaComposite composite = AlphaComposite.getInstance(ruleField.getInt(null),
                    (float) opacity.getValue() / 100.0f);
            painter.setComposite(composite);
        } catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
        }
    }

    private final class CompositePainter extends JComponent {

        private static final long serialVersionUID = 1L;
        private AlphaComposite composite = AlphaComposite.getInstance(
                AlphaComposite.SRC, 0.5f);

        @Override
        protected void paintComponent(Graphics g) {
            BufferedImage image = new BufferedImage(getWidth(), getHeight(),
                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = image.createGraphics();
            //Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            g2.setColor(Color.BLUE);
            g2.fillRect(4 + (getWidth() / 4), 4, getWidth() / 2, getHeight() - 8);
            g2.setColor(Color.RED);
            g2.setComposite(composite);
            g2.fillOval(40, 40, getWidth() - 80, getHeight() - 80);
            g2.dispose();

            g.drawImage(image, 0, 0, null);
        }

        private void setComposite(AlphaComposite composite) {
            this.composite = composite;
            repaint();
        }
    }

    public static void main(String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new AlphaCompositesDemo();
            }
        });
    }
}
```

*s28.postimg.org/plb842nwp/Alpha_Composites_Demo.jpg

The ColorConvert class demonstrates a ColorConvertOp operation that converts a ColorSpace.TYPE_RGB BufferedImage to a ColorSpace.CS_GRAY BufferedImage.


```
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * The ColorConvert class demonstrates a ColorConvertOp operation that converts
 * a ColorSpace.TYPE_RGB BufferedImage to a ColorSpace.CS_GRAY BufferedImage.
 */
public class ColorConvert extends JPanel {

    private static Image image;
    private static Color colors[] = {Color.red, Color.pink, Color.orange,
        Color.yellow, Color.green, Color.magenta, Color.cyan, Color.blue};

    public ColorConvert() {
        setBackground(Color.white);
        image = getImage("Images/Adel Miller.jpg");
        setPreferredSize(new Dimension(image.getWidth(this) * 2, image.getHeight(this)));
    }

    public BufferedImage getImage(String fileName) {
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
            System.exit(-1);
        }
        return null;
    }

    public void drawDemo(int w, int h, Graphics2D g2) {

        int iw = image.getWidth(this);
        int ih = image.getHeight(this);

        FontRenderContext frc = g2.getFontRenderContext();
        Font font = g2.getFont();
        g2.setColor(Color.black);
        TextLayout tl = new TextLayout("ColorConvertOp RGB->GRAY", font, frc);

        /*
         * centers the string "ColorConvertOp RGB->GRAY" at the top
         */
        tl.draw(g2, (float) (w / 2 - tl.getBounds().getWidth() / 2),
                tl.getAscent() + tl.getLeading());

        BufferedImage srcImg
                = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);
        Graphics2D srcG = srcImg.createGraphics();
        RenderingHints rhs = g2.getRenderingHints();
        srcG.setRenderingHints(rhs);
        srcG.drawImage(image, 0, 0, null);
        String s = "JavaColor";
        Font f = new Font("serif", Font.BOLD, iw / 6);
        tl = new TextLayout(s, f, frc);
        Rectangle2D tlb = tl.getBounds();

        // puts the string "JavaColor" into an array of chars
        char[] chars = s.toCharArray();
        float charWidth = 0.0f;
        int rw = iw / chars.length;
        int rh = ih / chars.length;

        /*
         * for each char in chars[], creates a TextLayout and
         * renders the text with a different color from the colors array.
         * Fills colored rectangles above and below.
         */
        for (int i = 0; i < chars.length; i++) {
            tl = new TextLayout(String.valueOf(chars[i]), f, frc);
            Shape shape = tl.getOutline(null);
            srcG.setColor(colors[i % colors.length]);
            tl.draw(srcG, (float) (iw / 2 - tlb.getWidth() / 2 + charWidth),
                    (float) (ih / 2 + tlb.getHeight() / 2));
            charWidth += (float) shape.getBounds().getWidth();
            srcG.fillRect(i * rw, ih - rh, rw, rh);
            srcG.setColor(colors[colors.length - 1 - i % colors.length]);
            srcG.fillRect(i * rw, 0, rw, rh);
        }

        ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
        ColorConvertOp theOp = new ColorConvertOp(cs, rhs);
        BufferedImage dstImg
                = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);
        theOp.filter(srcImg, dstImg);

        g2.drawImage(srcImg, 10, 20, w / 2 - 20, h - 30, null);
        g2.drawImage(dstImg, w / 2 + 10, 20, w / 2 - 20, h - 30, null);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        Dimension d = getSize();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, d.width, d.height);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        drawDemo(d.width, d.height, g2);
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("ColorConvert");
                f.add(new ColorConvert());
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s21.postimg.org/bhl5f9b6b/Color_Convert.jpg

The Rotator3D class displays animated multi-colored 3D objects that are
translated, rotated and scaled. Color changes are demonstrated by using a
light source and shading.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;

/**
 * The Rotator3D class displays animated multi-colored 3D objects that are
 * translated, rotated and scaled. Color changes are demonstrated by using a
 * light source and shading.
 */
public class Rotator3D extends JApplet implements Runnable {

    private Objects3D objs[] = new Objects3D[3];
    private static final int[][][] polygons
            = {
                // Solid cube
                {{5, 1, 15, 13, 21, 23, 15},
                {5, 2, 21, 13, 19, 27, 21},
                {5, 3, 23, 15, 17, 25, 23},
                {5, 4, 19, 13, 15, 17, 19},
                {5, 5, 27, 21, 23, 25, 27},
                {5, 6, 27, 19, 17, 25, 27}},
                // Polygonal faces cube
                {{5, 1, 21, 13, 19, 27, 21},
                {5, 5, 23, 15, 17, 25, 23},
                {4, 0, 15, 14, 16, 15}, {7, 6, 16, 14, 13, 12, 18, 17, 16}, {4, 0, 12, 19, 18, 12},
                {4, 2, 22, 21, 20, 22}, {7, 0, 24, 23, 22, 20, 27, 26, 24}, {4, 2, 24, 26, 25, 24},
                {4, 3, 15, 13, 23, 15}, {4, 0, 23, 13, 21, 23},
                {5, 0, 27, 26, 18, 19, 27}, {5, 4, 25, 17, 18, 26, 25}},
                // Octahedron
                {{4, 3, 18, 21, 16, 18}, {4, 1, 20, 16, 18, 20},
                {4, 1, 18, 21, 16, 18}, {4, 3, 20, 17, 19, 20},
                {4, 2, 20, 26, 27, 20}, {5, 3, 26, 18, 16, 27, 26},
                {5, 0, 17, 24, 25, 19, 17}, {4, 3, 21, 25, 24, 21},
                {4, 4, 18, 21, 22, 18}, {4, 2, 22, 21, 17, 22},
                {4, 5, 20, 23, 16, 20}, {4, 1, 20, 23, 19, 20},
                {4, 6, 21, 23, 16, 21}, {4, 4, 21, 23, 19, 21},
                {4, 5, 20, 18, 22, 20}, {4, 6, 20, 22, 17, 20}}
            };
    private static final double[][][] points
            = {
                // Points for solid cube & polygonal faces cube
                {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1},
                {0, 0, -1}, {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0},
                {0, 0, 1}, {0, 0, -1}, {1, 1, 0}, {1, 1, 1}, {0, 1, 1},
                {-1, 1, 1}, {-1, 1, 0}, {-1, 1, -1}, {0, 1, -1}, {1, 1, -1},
                {1, -1, 0}, {1, -1, 1}, {0, -1, 1}, {-1, -1, 1}, {-1, -1, 0},
                {-1, -1, -1}, {0, -1, -1}, {1, -1, -1}},
                // Points for octahedron
                {{0, 0, 1}, {0, 0, -1}, {-0.8165, 0.4714, 0.33333},
                {0.8165, -0.4714, -0.33333}, {0.8165, 0.4714, 0.33333},
                {-0.8165, -0.4714, -0.33333}, {0, -0.9428, 0.3333},
                {0, 0.9428, -0.33333}, {0, 0, 1}, {0, 0, -1},
                {-0.8165, 0.4714, 0.33333}, {0.8165, -0.4714, -0.33333},
                {0.8165, 0.4714, 0.33333}, {-0.8165, -0.4714, -0.33333},
                {0, -0.9428, 0.33333}, {0, 0.9428, -0.33333},
                {-1.2247, -0.7071, 1}, {1.2247, 0.7071, -1},
                {0, 1.4142, 1}, {0, -1.4142, -1}, {-1.2247, 0.7071, -1},
                {1.2247, -0.7071, 1}, {0.61237, 1.06066, 0},
                {-0.61237, -1.06066, 0}, {1.2247, 0, 0},
                {0.61237, -1.06066, 0}, {-0.61237, 1.06066, 0}, {-1.2247, 0, 0}}
            };
    private static final int[][][] faces
            = {
                // Solid cube
                {{1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 0}, {1, 5}},
                // Polygonal faces cube
                {{1, 0}, {1, 1}, {3, 2, 3, 4}, {3, 5, 6, 7}, {2, 8, 9}, {2, 10, 11}},
                // Octahedron
                {{1, 2}, {1, 3}, {2, 4, 5}, {2, 6, 7}, {2, 8, 9},
                {2, 10, 11}, {2, 12, 13}, {2, 14, 15}},};
    private Thread thread;
    private BufferedImage bimg;

    @Override
    public void init() {
        setBackground(Color.white);
    }

    // initializes the 3D objects
    public void reset(int w, int h) {
        objs[0] = new Objects3D(polygons[0], points[0], faces[0], w, h);
        objs[1] = new Objects3D(polygons[1], points[0], faces[1], w, h);
        objs[2] = new Objects3D(polygons[2], points[1], faces[2], w, h);
    }

    public void step(int w, int h) {
        for (int i = objs.length; i-- > 0;) {
            if (objs[i] != null) {
                objs[i].step(w, h);
            }
        }
    }

    public void drawDemo(int w, int h, Graphics2D g2) {
        for (int i = objs.length; i-- > 0;) {
            if (objs[i] != null) {
                objs[i].Draw(g2);
            }
        }
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(Color.black);
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
    }

    @Override
    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    /**
     * The Objects3D class creates a Solid Cube, Cube and Octahedron with
     * polygonal faces.
     */
    public class Objects3D {

        private final int UP = 0;
        private final int DOWN = 1;
        private int[][] polygons;
        private int npoly;
        private double[][] points;
        private int npoint;
        private int[][] faces;
        private int nface;
        private int ncolour = 10;
        private Color[][] colours = new Color[ncolour][7];
        private double[] lightvec = {0, 1, 1};
        private double Zeye = 10;
        private double angle;
        private Matrix3D orient, tmp, tmp2, tmp3;
        private int scaleDirection;
        private double scale, scaleAmt;
        private double ix = 3.0, iy = 3.0;
        private double[][] rotPts;
        private int[][] scrPts;
        private int xx[] = new int[20];
        private int yy[] = new int[20];
        private double x, y;
        private int p, j;
        private int colour;
        private double bounce, persp;

        public Objects3D(int[][] polygons,
                double[][] points,
                int[][] faces,
                int w,
                int h) {

            this.polygons = polygons;
            this.points = points;
            this.faces = faces;
            npoly = polygons.length;
            npoint = points.length;
            nface = faces.length;

            x = w * Math.random();
            y = h * Math.random();

            ix = Math.random() > 0.5 ? ix : -ix;
            iy = Math.random() > 0.5 ? iy : -iy;

            rotPts = new double[npoint][3];
            scrPts = new int[npoint][2];

            /*
             * computes colors to produce shading effect
             */
            for (int i = 0; i < ncolour; i++) {
                // white
                colours[i][0] = new Color(255 - (ncolour - 1 - i) * 100 / ncolour,
                        255 - (ncolour - 1 - i) * 100 / ncolour, 255
                        - (ncolour - 1 - i) * 100 / ncolour);
                // red
                colours[i][1] = new Color(255 - (ncolour - 1 - i) * 100 / ncolour, 0, 0);
                // green
                colours[i][2] = new Color(0, 255 - (ncolour - 1 - i) * 100 / ncolour, 0);
                // blue
                colours[i][3] = new Color(0, 0, 255 - (ncolour - 1 - i) * 100 / ncolour);
                // yellow
                colours[i][4] = new Color(255 - (ncolour - 1 - i) * 100 / ncolour,
                        255 - (ncolour - 1 - i) * 100 / ncolour, 0);
                // cyan
                colours[i][5] = new Color(0, 255 - (ncolour - 1 - i) * 100 / ncolour,
                        255 - (ncolour - 1 - i) * 100 / ncolour);
                // magenta
                colours[i][6] = new Color(255 - (ncolour - 1 - i) * 100 / ncolour, 0,
                        255 - (ncolour - 1 - i) * 100 / ncolour);
            }

            double len = Math.sqrt(lightvec[0] * lightvec[0]
                    + lightvec[1] * lightvec[1]
                    + lightvec[2] * lightvec[2]);
            lightvec[0] = lightvec[0] / len;
            lightvec[1] = lightvec[1] / len;
            lightvec[2] = lightvec[2] / len;

            double max = 0;
            for (int i = 0; i < npoint; i++) {
                len = Math.sqrt(points[i][0] * points[i][0]
                        + points[i][1] * points[i][1]
                        + points[i][2] * points[i][2]);
                if (len > max) {
                    max = len;
                }
            }

            for (int i = 0; i < nface; i++) {
                len = Math.sqrt(points[i][0] * points[i][0]
                        + points[i][1] * points[i][1]
                        + points[i][2] * points[i][2]);
                points[i][0] = points[i][0] / len;
                points[i][1] = points[i][1] / len;
                points[i][2] = points[i][2] / len;
            }

            orient = new Matrix3D();
            tmp = new Matrix3D();
            tmp2 = new Matrix3D();
            tmp3 = new Matrix3D();
            tmp.Rotation(2, 0, Math.PI / 50);
            CalcScrPts((double) w / 3, (double) h / 3, 0);

            scale = Math.min(w / 3 / max / 1.2, h / 3 / max / 1.2);
            scaleAmt = scale;
            scale *= Math.random() * 1.5;
            scaleDirection = scaleAmt < scale ? DOWN : UP;
        }

        // gets the color based on the current rotation and lightsource
        private Color getColour(int f, int index) {
            colour = (int) ((rotPts[f][0] * lightvec[0]
                    + rotPts[f][1] * lightvec[1]
                    + rotPts[f][2] * lightvec[2]) * ncolour);
            if (colour < 0) {
                colour = 0;
            }
            if (colour > ncolour - 1) {
                colour = ncolour - 1;
            }
            return colours[colour][polygons[faces[f][index]][1]];
        }

        // performs matrix multiplication to generate rotated points
        private void CalcScrPts(double x, double y, double z) {
            for (p = 0; p < npoint; p++) {
                rotPts[p][2] = points[p][0] * orient.M[2][0]
                        + points[p][1] * orient.M[2][1]
                        + points[p][2] * orient.M[2][2];
                rotPts[p][0] = points[p][0] * orient.M[0][0]
                        + points[p][1] * orient.M[0][1]
                        + points[p][2] * orient.M[0][2];
                rotPts[p][1] = -points[p][0] * orient.M[1][0]
                        - points[p][1] * orient.M[1][1]
                        - points[p][2] * orient.M[1][2];
            }
            for (p = nface; p < npoint; p++) {
                rotPts[p][2] += z;
                persp = (Zeye - rotPts[p][2]) / (scale * Zeye);
                scrPts[p][0] = (int) (rotPts[p][0] / persp + x);
                scrPts[p][1] = (int) (rotPts[p][1] / persp + y);
            }
        }

        // tests if the specified face is showing
        private boolean faceUp(int f) {
            return (rotPts[f][0] * rotPts[nface + f][0]
                    + rotPts[f][1] * rotPts[nface + f][1]
                    + rotPts[f][2] * (rotPts[nface + f][2] - Zeye) < 0);
        }


        /*
         * advances the x and y coordinates, scaling factors and
         * directions and angle of rotation
         */
        public void step(int w, int h) {
            x += ix;
            y += iy;
            if (x > w - scale) {
                x = w - scale - 1;
                ix = -w / 100 - 1;
            }
            if (x - scale < 0) {
                x = 2 + scale;
                ix = w / 100 + Math.random() * 3;
            }
            if (y > h - scale) {
                y = h - scale - 2;
                iy = -h / 100 - 1;
            }
            if (y - scale < 0) {
                y = 2 + scale;
                iy = h / 100 + Math.random() * 3;
            }

            angle += Math.random() * 0.15;
            tmp3.Rotation(1, 2, angle);
            tmp2.Rotation(1, 0, angle * Math.sqrt(2) / 2);
            tmp.Rotation(0, 2, angle * Math.PI / 4);
            orient.M = tmp3.Times(tmp2.Times(tmp.M));
            bounce = Math.abs(Math.cos(0.5 * (angle))) * 2 - 1;

            if (scale > scaleAmt * 1.4) {
                scaleDirection = DOWN;
            }
            if (scale < scaleAmt * 0.4) {
                scaleDirection = UP;
            }
            if (scaleDirection == UP) {
                scale += Math.random();
            }
            if (scaleDirection == DOWN) {
                scale -= Math.random();
            }

            CalcScrPts(x, y, bounce);
        }

        // calls DrawPoly on each of the showing faces of the 3D objects
        public void Draw(Graphics2D g2) {
            for (int f = 0; f < nface; f++) {
                if (faceUp(f)) {
                    for (j = 1; j < faces[f][0] + 1; j++) {
                        DrawPoly(g2, faces[f][j], getColour(f, j));
                    }
                }
            }
        }

        // draws and fills the faces of the specified polygon
        private void DrawPoly(Graphics2D g2, int poly, Color colour) {
            for (int p = 2; p < polygons[poly][0] + 2; p++) {
                xx[p - 2] = scrPts[polygons[poly][p]][0];
                yy[p - 2] = scrPts[polygons[poly][p]][1];
            }
            g2.setColor(colour);
            g2.fillPolygon(xx, yy, polygons[poly][0]);
            g2.setColor(Color.black);
            g2.drawPolygon(xx, yy, polygons[poly][0]);
        }

        /**
         * The Matrix3D class defines a matrix to be used to perform the
         * rotation.
         */
        public class Matrix3D {

            public double[][] M = {{1, 0, 0},
            {0, 1, 0},
            {0, 0, 1}};
            private double[][] tmp = new double[3][3];
            private int row, col, k;

            public void Rotation(int i, int j, double angle) {
                for (row = 0; row < 3; row++) {
                    for (col = 0; col < 3; col++) {
                        if (row != col) {
                            M[row][col] = 0.0;
                        } else {
                            M[row][col] = 1.0;
                        }
                    }
                }
                M[i][i] = Math.cos(angle);
                M[j][j] = Math.cos(angle);
                M[i][j] = Math.sin(angle);
                M[j][i] = -Math.sin(angle);
            }

            public double[][] Times(double[][] N) {
                for (row = 0; row < 3; row++) {
                    for (col = 0; col < 3; col++) {
                        tmp[row][col] = 0.0;
                        for (k = 0; k < 3; k++) {
                            tmp[row][col] += M[row][k] * N[k][col];
                        }
                    }
                }
                return tmp;
            }
        } // End Matrix3D class

    } // End Objects3D class

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final Rotator3D demo = new Rotator3D();
                demo.init();
                Frame f = new Frame("Rotator3D");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add(demo);
                f.pack();
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height / 2 + 150));
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

The SelectTx class demonstrates scaling, shearing or rotating an image and rectangle.

```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * The SelectTx class demonstrates scaling, shearing or rotating an image and
 * rectangle.
 */
public class SelectTx extends JPanel {

    Demo demo;

    public SelectTx() {
        setLayout(new BorderLayout());
        add(demo = new Demo());
        add("North", new DemoControls(demo));
    }

    public void start() {
        demo.start();
    }

    public void stop() {
        demo.stop();
    }

    /**
     * The Demo class performs the painting and the transformations.
     */
    static class Demo extends JPanel implements Runnable {

        private static final int LEFT = 1;
        private static final int XMIDDLE = 2;
        private static final int DOWN = 3;
        private static final int UP = 4;
        private static final int YMIDDLE = 5;
        private static final int XupYup = 6;
        private static final int XdownYdown = 7;
        private static final String[] title = {"Scale", "Shear", "Rotate"};
        private static final long serialVersionUID = 1L;
        private Image img, original;
        private int iw, ih;
        private Thread thread;
        private BufferedImage bimg;

        // the directions of the transformations
        public static final int RIGHT = 0;
        public static final int SCALE = 0;
        public static final int SHEAR = 1;
        public static final int ROTATE = 2;
        public int transformType = SHEAR;
        public double sx, sy;
        public double angdeg;
        public int direction = RIGHT;

        public Demo() {
            setBackground(Color.white);
            original = getImage("Images/Anastasia Christen1.jpg");
            try {
                MediaTracker tracker = new MediaTracker(this);
                tracker.addImage(original, 0);
                tracker.waitForID(0);
            } catch (InterruptedException e) {
            }
            iw = original.getWidth(this);
            ih = original.getHeight(this);
        }

        public BufferedImage getImage(String fileName) {
            try {
                return ImageIO.read(new File(fileName));
            } catch (IOException ioe) {
                System.err.println("Error loading Image!!");
                System.exit(-1);
            }
            return null;
        }

        public void reset(int w, int h) {
            iw = w / 3;
            ih = h / 3;
            img = createImage(iw, ih);
            Graphics big = img.getGraphics();
            big.drawImage(original, 0, 0, iw, ih, Color.orange, null);

            if (transformType == SCALE) {
                direction = RIGHT;
                sx = sy = 1.0;
            } else if (transformType == SHEAR) {
                direction = RIGHT;
                sx = sy = 0;
            } else {
                angdeg = 0;
            }
        }

        public void step(int w, int h) {
            int rw = iw + 10;
            int rh = ih + 10;

            if (transformType == SCALE && direction == RIGHT) {
                sx += .05;
                if (w * .5 - iw * .5 + rw * sx + 10 > w) {
                    direction = DOWN;
                }
            } else if (transformType == SCALE && direction == DOWN) {
                sy += .05;
                if (h * .5 - ih * .5 + rh * sy + 20 > h) {
                    direction = LEFT;
                }
            } else if (transformType == SCALE && direction == LEFT) {
                sx -= .05;
                if (rw * sx - 10 <= -(w * .5 - iw * .5)) {
                    direction = UP;
                }
            } else if (transformType == SCALE && direction == UP) {
                sy -= .05;
                if (rh * sy - 20 <= -(h * .5 - ih * .5)) {
                    direction = RIGHT;
                }
            }

            if (transformType == SHEAR && direction == RIGHT) {
                sx += .05;
                if (rw + 2 * rh * sx + 20 > w) {
                    direction = LEFT;
                    sx -= .1;
                }
            } else if (transformType == SHEAR && direction == LEFT) {
                sx -= .05;
                if (rw - 2 * rh * sx + 20 > w) {
                    direction = XMIDDLE;
                }
            } else if (transformType == SHEAR && direction == XMIDDLE) {
                sx += .05;
                if (sx > 0) {
                    direction = DOWN;
                    sx = 0;
                }
            } else if (transformType == SHEAR && direction == DOWN) {
                sy -= .05;
                if (rh - 2 * rw * sy + 20 > h) {
                    direction = UP;
                    sy += .1;
                }
            } else if (transformType == SHEAR && direction == UP) {
                sy += .05;
                if (rh + 2 * rw * sy + 20 > h) {
                    direction = YMIDDLE;
                }
            } else if (transformType == SHEAR && direction == YMIDDLE) {
                sy -= .05;
                if (sy < 0) {
                    direction = XupYup;
                    sy = 0;
                }
            } else if (transformType == SHEAR && direction == XupYup) {
                sx += .05;
                sy += .05;
                if (rw + 2 * rh * sx + 30 > w || rh + 2 * rw * sy + 30 > h) {
                    direction = XdownYdown;
                }
            } else if (transformType == SHEAR && direction == XdownYdown) {
                sy -= .05;
                sx -= .05;
                if (sy < 0) {
                    direction = RIGHT;
                    sx = sy = 0.0;
                }
            }

            if (transformType == ROTATE) {
                angdeg += 5;
                if (angdeg == 360) {
                    angdeg = 0;
                }
            }
        }

        /*
         * draws the transformed image, the String describing the current
         * transform, and the current transformation factors.
         */
        public void drawDemo(int w, int h, Graphics2D g2) {

            Font font = g2.getFont();
            FontRenderContext frc = g2.getFontRenderContext();
            TextLayout tl = new TextLayout(title[transformType], font, frc);
            g2.setColor(Color.black);
            tl.draw(g2, (float) (w / 2 - tl.getBounds().getWidth() / 2), tl.getAscent() + tl.getDescent());

            if (transformType == ROTATE) {
                String s = Double.toString(angdeg);
                g2.drawString("angdeg=" + s, 2, h - 4);
            } else {
                String s = Double.toString(sx);
                s = (s.length() < 5) ? s : s.substring(0, 5);
                TextLayout tlsx = new TextLayout("sx=" + s, font, frc);
                tlsx.draw(g2, 2, h - 4);

                s = Double.toString(sy);
                s = (s.length() < 5) ? s : s.substring(0, 5);
                g2.drawString("sy=" + s, (int) (tlsx.getBounds().getWidth() + 4), h - 4);
            }

            if (transformType == SCALE) {
                g2.translate(w / 2 - iw / 2, h / 2 - ih / 2);
                g2.scale(sx, sy);
            } else if (transformType == SHEAR) {
                g2.translate(w / 2 - iw / 2, h / 2 - ih / 2);
                g2.shear(sx, sy);
            } else {
                g2.rotate(Math.toRadians(angdeg), w / 2, h / 2);
                g2.translate(w / 2 - iw / 2, h / 2 - ih / 2);
            }

            g2.setColor(Color.orange);
            g2.fillRect(0, 0, iw + 10, ih + 10);
            g2.drawImage(img, 5, 5, this);
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
                reset(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            thread = null;
            notify();
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }
    }  // End Demo class

    /**
     * The DemoControls class provides buttons for selecting either scaling,
     * shearing or rotating of the image.
     */
    static final class DemoControls extends JPanel implements ActionListener {

        Demo demo;
        JToolBar toolbar;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            add(toolbar = new JToolBar());
            toolbar.setFloatable(false);
            addTool("Scale", false);
            addTool("Shear", true);
            addTool("Rotate", false);
        }

        public void addTool(String str, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(str));
            b.setBackground(state ? Color.green : Color.lightGray);
            b.addActionListener(this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            for (int i = 0; i < toolbar.getComponentCount(); i++) {
                JButton b = (JButton) toolbar.getComponentAtIndex(i);
                b.setBackground(Color.lightGray);
            }
            JButton b = (JButton) e.getSource();
            b.setBackground(Color.green);
            if (b.getText().equals("Scale")) {
                demo.transformType = demo.SCALE;
                demo.direction = demo.RIGHT;
                demo.sx = demo.sy = 1;
            } else if (b.getText().equals("Shear")) {
                demo.transformType = demo.SHEAR;
                demo.direction = demo.RIGHT;
                demo.sx = demo.sy = 0;
            } else if (b.getText().equals("Rotate")) {
                demo.transformType = demo.ROTATE;
                demo.angdeg = 0;
            }
        }
    } // End DemoControls class

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final SelectTx demo = new SelectTx();
                JFrame f = new JFrame("SelectTx");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(scrDim.width / 2, scrDim.height / 2 + 75);
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

*s18.postimg.org/5cl6x85g5/Select_Tx.jpg

*Drawing a Shape*

The outline of any Shape can be rendered with the Graphics2D.draw method. The draw methods from previous versions of the JDK software are also supported: drawLine, drawRect, drawRoundRect, drawOval, drawArc, drawPolyline, drawPolygon, draw3DRect.

When a Shape is drawn, its path is stroked with the Stroke object in the Graphics2D context. (See “Stroke Attributes” on page 19 for more information.) By setting an appropriate BasicStroke object in the Graphics2D context, you can draw lines of any width or pattern. The BasicStroke object also defines the line’s endcap and join attributes.

To render shape’s outline:

    Create a BasicStroke object

    Call Graphics2D.setStroke

    Create the Shape.

    Call Graphics2D.draw(shape).

In the following example, a GeneralPath object is used to define a star and a BasicStroke object is added to the Graphics2D context to define the star’s line with and join attributes.


```
public void paint(Graphics g) { 
  Graphics2D g2 = (Graphics2D) g; 
 
  // create and set the stroke 
  g2.setStroke(new BasicStroke(4.0f)); 
 
  // Create a star using a general path object 
  GeneralPath p = new GeneralPath(GeneralPath.NON_ZERO); 
  p.moveTo(- 100.0f, - 25.0f); 
  p.lineTo(+ 100.0f, - 25.0f); 
  p.lineTo(- 50.0f, + 100.0f); 
  p.lineTo(+ 0.0f, - 100.0f); 
  p.lineTo(+ 50.0f, + 100.0f); 
  p.closePath(); 
 
  // translate origin towards center of canvas 
  g2.translate(100.0f, 100.0f); 
  
  // render the star's path 
  g2.draw(p); 
}
```

The following demo showcases Lines & Paths animation illustrating BasicStroke attributes.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

/**
 * Lines & Paths animation illustrating BasicStroke attributes.
 */
public class LineAnim extends JApplet implements Runnable {

    private static int caps[] = {BasicStroke.CAP_BUTT,
        BasicStroke.CAP_SQUARE, BasicStroke.CAP_ROUND};
    private static int joins[] = {BasicStroke.JOIN_MITER,
        BasicStroke.JOIN_BEVEL, BasicStroke.JOIN_ROUND};
    private static Color colors[] = {Color.red, Color.green, Color.blue};
    private static BasicStroke bs1 = new BasicStroke(1.0f);
    private static final int CLOCKWISE = 0;
    private static final int COUNTERCW = 1;
    private static final long serialVersionUID = 1L;

    private Line2D lines[] = new Line2D[3];
    private int rAmt[] = new int[lines.length];
    private int direction[] = new int[lines.length];
    private int speed[] = new int[lines.length];
    private BasicStroke strokes[] = new BasicStroke[lines.length];
    private GeneralPath path;
    private Point2D[] pts;
    private float size;
    private Ellipse2D ellipse = new Ellipse2D.Double();
    private Thread thread;
    private BufferedImage bimg;

    @Override
    public void init() {
        setBackground(Color.white);
    }

    public void reset(int w, int h) {
        size = (w > h) ? h / 6f : w / 6f;
        for (int i = 0; i < lines.length; i++) {
            lines[i] = new Line2D.Float(0, 0, size, 0);
            strokes[i] = new BasicStroke(size / 3, caps[i], joins[i]);
            rAmt[i] = i * 360 / lines.length;
            direction[i] = i % 2;
            speed[i] = i + 1;
        }

        path = new GeneralPath();
        path.moveTo(size, -size / 2);
        path.lineTo(size + size / 2, 0);
        path.lineTo(size, +size / 2);

        ellipse.setFrame(w / 2 - size * 2 - 4.5f, h / 2 - size * 2 - 4.5f, size * 4, size * 4);
        PathIterator pi = ellipse.getPathIterator(null, 0.9);
        Point2D[] points = new Point2D[100];
        int num_pts = 0;
        while (!pi.isDone()) {
            float[] pt = new float[6];
            switch (pi.currentSegment(pt)) {
                case FlatteningPathIterator.SEG_MOVETO:
                case FlatteningPathIterator.SEG_LINETO:
                    points[num_pts] = new Point2D.Float(pt[0], pt[1]);
                    num_pts++;
            }
            pi.next();
        }
        pts = new Point2D[num_pts];
        System.arraycopy(points, 0, pts, 0, num_pts);
    }

    public void step(int w, int h) {
        for (int i = 0; i < lines.length; i++) {
            if (direction[i] == CLOCKWISE) {
                rAmt[i] += speed[i];
                if (rAmt[i] == 360) {
                    rAmt[i] = 0;
                }
            } else {
                rAmt[i] -= speed[i];
                if (rAmt[i] == 0) {
                    rAmt[i] = 360;
                }
            }
        }
    }

    public void drawDemo(int w, int h, Graphics2D g2) {

        ellipse.setFrame(w / 2 - size, h / 2 - size, size * 2, size * 2);
        g2.setColor(Color.black);
        g2.draw(ellipse);

        for (int i = 0; i < lines.length; i++) {
            AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2);
            at.rotate(Math.toRadians(rAmt[i]));
            g2.setStroke(strokes[i]);
            g2.setColor(colors[i]);
            g2.draw(at.createTransformedShape(lines[i]));
            g2.draw(at.createTransformedShape(path));

            int j = (int) ((double) rAmt[i] / 360 * pts.length);
            j = (j == pts.length) ? pts.length - 1 : j;
            ellipse.setFrame(pts[j].getX(), pts[j].getY(), 9, 9);
            g2.fill(ellipse);
        }

        g2.setStroke(bs1);
        g2.setColor(Color.black);
        for (int i = 0; i < pts.length; i++) {
            ellipse.setFrame(pts[i].getX(), pts[i].getY(), 9, 9);
            g2.draw(ellipse);
        }
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
    }

    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final LineAnim demo = new LineAnim();
                demo.init();
                JFrame f = new JFrame("LineAnim");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                f.pack();
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height - 75));
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

*s23.postimg.org/ml63jgtnr/Line_Anim.jpg

*Filling a Shape*

The Graphics2D.fill method can be used to fill any Shape. When a Shape is filled, the area within its path is rendered with the Graphics2D context’s current Paint attribute—a Color, TexturePaint, or GradientPaint.

The fill methods from previous versions of the JDK software are also supported: fillRect, fill3DRect, fillRoundRect, fillOval, fillArc, fillPolygon, clearRect.

To fill a Shape:

    Set the fill color or pattern on the graphics context using
    Graphics2D.setColor or Graphics2D.setPaint.

    Create the Shape.

    Call Graphics2D.fill to render the Shape.

In the following example, setColor is called to define a green fill for a Rectangle2D.


```
public void paint(Graphics g) { 
  Graphics2D g2 = (Graphics2D) g; 
 
  g2.setPaint(Color.green); 
  Rectangle2D r2 = new Rectangle2D.Float(25,25,150,150); 
 
   g2.fill(r2); 
}
```

Here is a demo that draws various shapes, & fills.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.geom.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/*
 * Java2D demo
 */
public class ShapesDemo2D extends JPanel {

    final int maxCharHeight = 15;
    final int minFontSize = 6;

    final Color bg = Color.white;
    final Color fg = Color.black;
    final Color red = Color.red;
    final Color white = Color.white;

    final BasicStroke stroke = new BasicStroke(2.0f);
    final BasicStroke wideStroke = new BasicStroke(8.0f);

    final float dash1[] = {10.0f};
    final BasicStroke dashed = new BasicStroke(1.0f,
            BasicStroke.CAP_BUTT,
            BasicStroke.JOIN_MITER,
            10.0f, dash1, 0.0f);
    private final static long serialVersionUID = 1L;
    Dimension totalSize;
    FontMetrics fontMetrics;

    FontMetrics pickFont(Graphics2D g2,
            String longString,
            int xSpace) {
        boolean fontFits = false;
        Font font = g2.getFont();
        FontMetrics fontMetrics = g2.getFontMetrics();
        int size = font.getSize();
        String name = font.getName();
        int style = font.getStyle();

        while (!fontFits) {
            if ((fontMetrics.getHeight() <= maxCharHeight)
                    && (fontMetrics.stringWidth(longString) <= xSpace)) {
                fontFits = true;
            } else {
                if (size <= minFontSize) {
                    fontFits = true;
                } else {
                    g2.setFont(font = new Font(name,
                            style,
                            --size));
                    fontMetrics = g2.getFontMetrics();
                }
            }
        }

        return fontMetrics;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);

        Dimension d = getSize();
        int gridWidth = d.width / 6;
        int gridHeight = d.height / 2;

        fontMetrics = pickFont(g2, "Filled and Stroked GeneralPath",
                gridWidth);

        Color fg3D = Color.lightGray;

        g2.setPaint(fg3D);
        g2.draw3DRect(0, 0, d.width - 1, d.height - 1, true);
        g2.draw3DRect(3, 3, d.width - 7, d.height - 7, false);
        g2.setPaint(fg);

        int x = 5;
        int y = 7;
        int rectWidth = gridWidth - 2 * x;
        int stringY = gridHeight - 3 - fontMetrics.getDescent();
        int rectHeight = stringY - fontMetrics.getMaxAscent() - y - 2;

        // draw Line2D.Double
        g2.draw(new Line2D.Double(x, y + rectHeight - 1, x + rectWidth, y));
        g2.drawString("Line2D", x, stringY);
        x += gridWidth;

        // draw Rectangle2D.Double
        g2.setStroke(stroke);
        g2.draw(new Rectangle2D.Double(x, y, rectWidth, rectHeight));
        g2.drawString("Rectangle2D", x, stringY);
        x += gridWidth;

        // draw  RoundRectangle2D.Double
        g2.setStroke(dashed);
        g2.draw(new RoundRectangle2D.Double(x, y, rectWidth,
                rectHeight, 10, 10));
        g2.drawString("RoundRectangle2D", x, stringY);
        x += gridWidth;

        // draw Arc2D.Double
        g2.setStroke(wideStroke);
        g2.draw(new Arc2D.Double(x, y, rectWidth, rectHeight, 90,
                135, Arc2D.OPEN));
        g2.drawString("Arc2D", x, stringY);
        x += gridWidth;

        // draw Ellipse2D.Double
        g2.setStroke(stroke);
        g2.draw(new Ellipse2D.Double(x, y, rectWidth, rectHeight));
        g2.drawString("Ellipse2D", x, stringY);
        x += gridWidth;

        // draw GeneralPath (polygon)
        int x1Points[] = {x, x + rectWidth, x, x + rectWidth};
        int y1Points[] = {y, y + rectHeight, y + rectHeight, y};
        GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
                x1Points.length);
        polygon.moveTo(x1Points[0], y1Points[0]);
        for (int index = 1; index < x1Points.length; index++) {
            polygon.lineTo(x1Points[index], y1Points[index]);
        };
        polygon.closePath();

        g2.draw(polygon);
        g2.drawString("GeneralPath", x, stringY);

        // NEW ROW
        x = 5;
        y += gridHeight;
        stringY += gridHeight;

        // draw GeneralPath (polyline)
        int x2Points[] = {x, x + rectWidth, x, x + rectWidth};
        int y2Points[] = {y, y + rectHeight, y + rectHeight, y};
        GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
                x2Points.length);
        polyline.moveTo(x2Points[0], y2Points[0]);
        for (int index = 1; index < x2Points.length; index++) {
            polyline.lineTo(x2Points[index], y2Points[index]);
        };

        g2.draw(polyline);
        g2.drawString("GeneralPath (open)", x, stringY);
        x += gridWidth;

        // fill Rectangle2D.Double (red)
        g2.setPaint(Color.PINK);
        g2.fill(new Rectangle2D.Double(x, y, rectWidth, rectHeight));
        g2.setPaint(fg);
        g2.drawString("Filled Rectangle2D", x, stringY);
        x += gridWidth;

        // fill RoundRectangle2D.Double
        GradientPaint redtowhite = new GradientPaint(x, y, Color.blue, x + rectWidth, y, Color.orange);
        g2.setPaint(redtowhite);
        g2.fill(new RoundRectangle2D.Double(x, y, rectWidth,
                rectHeight, 10, 10));
        g2.setPaint(fg);
        g2.drawString("Filled RoundRectangle2D", x, stringY);
        x += gridWidth;

        // fill Arc2D
        g2.setPaint(Color.green.darker());
        g2.fill(new Arc2D.Double(x, y, rectWidth, rectHeight, 90,
                180, Arc2D.OPEN)); // 135
        g2.setPaint(fg);
        g2.drawString("Filled Arc2D", x, stringY);
        x += gridWidth;

        // fill Ellipse2D.Double
        redtowhite = new GradientPaint(x, y, Color.magenta, x + rectWidth, y, Color.yellow);
        g2.setPaint(redtowhite);
        g2.fill(new Ellipse2D.Double(x, y, rectWidth, rectHeight));
        g2.setPaint(fg);
        g2.drawString("Filled Ellipse2D", x, stringY);
        x += gridWidth;

        // fill and stroke GeneralPath
        int x3Points[] = {x, x + rectWidth, x, x + rectWidth};
        int y3Points[] = {y, y + rectHeight, y + rectHeight, y};
        GeneralPath filledPolygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
                x3Points.length);
        filledPolygon.moveTo(x3Points[0], y3Points[0]);
        for (int index = 1; index < x3Points.length; index++) {
            filledPolygon.lineTo(x3Points[index], y3Points[index]);
        };
        filledPolygon.closePath();
        g2.setPaint(Color.CYAN);
        g2.fill(filledPolygon);
        g2.setPaint(fg);
        g2.draw(filledPolygon);
        g2.drawString("Filled and Stroked GeneralPath", x, stringY);
    }

    public static void main(String s[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("ShapesDemo2D");
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(scrDim.width / 2, scrDim.height / 2);
                f.setLocationRelativeTo(null);
                JPanel panel = new ShapesDemo2D();
                f.add("Center", panel);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setVisible(true);
            }
        });
    }
}
```

*s28.postimg.org/qylej5rih/Shapes_Demo2_D.jpg

The Areas class demonstrates the CAG (Constructive Area Geometry) operations:
Add(union), Subtract, Intersect, and ExclusiveOR.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import javax.swing.*;

/**
 * The Areas class demonstrates the CAG (Constructive Area Geometry) operations:
 * Add(union), Subtract, Intersect, and ExclusiveOR.
 */
public class Areas extends JApplet {

    public void init() {
        Demo demo = new Demo();
        getContentPane().add(demo);
        getContentPane().add("North", new DemoControls(demo));
    }

    /**
     * The Demo class performs the CAG operations and renders the shapes.
     */
    public class Demo extends JPanel {

        public String areaType = "nop";

        public Demo() {
            setBackground(Color.white);
        }

        public void drawDemo(int w, int h, Graphics2D g2) {
            GeneralPath p1 = new GeneralPath();

            // draws the polygon on the left side
            p1.moveTo(w * .25f, 0.0f);
            p1.lineTo(w * .75f, h * .5f);
            p1.lineTo(w * .25f, (float) h);
            p1.lineTo(0.0f, h * .5f);
            p1.closePath();

            GeneralPath p2 = new GeneralPath();

            // draws the polygon on the right side
            p2.moveTo(w * .75f, 0.0f);
            p2.lineTo((float) w, h * .5f);
            p2.lineTo(w * .75f, (float) h);
            p2.lineTo(w * .25f, h * .5f);
            p2.closePath();

            // creates an area object with the first path
            Area area = new Area(p1);
            g2.setColor(Color.yellow);

            /*
             * fills both paths if 'nop' is selected; otherwise, creates
             * an Area object with p2 and performs the selected CAG
             * operation with the two Area objects
             */
            if (areaType.equals("nop")) {
                g2.fill(p1);
                g2.fill(p2);
                g2.setColor(Color.red);
                g2.draw(p1);
                g2.draw(p2);
                return;
            } else if (areaType.equals("add")) {
                area.add(new Area(p2));
            } else if (areaType.equals("sub")) {
                area.subtract(new Area(p2));
            } else if (areaType.equals("xor")) {
                area.exclusiveOr(new Area(p2));
            } else if (areaType.equals("int")) {
                area.intersect(new Area(p2));
            } else if (areaType.equals("pear")) {

                double sx = w / 100;
                double sy = h / 140;
                g2.scale(sx, sy);
                double x = w / sx / 2;
                double y = h / sy / 2;

                /*
                 * creates the first leaf by filling the intersection of
                 * two Area objects created from an ellipse.
                 */
                Ellipse2D leaf = new Ellipse2D.Double(x - 16, y - 29, 15.0, 15.0);
                Area leaf1 = new Area(leaf);
                leaf.setFrame(x - 14, y - 47, 30.0, 30.0);
                Area leaf2 = new Area(leaf);
                leaf1.intersect(leaf2);
                g2.setColor(Color.green);
                g2.fill(leaf1);

                // creates the second leaf.
                leaf.setFrame(x + 1, y - 29, 15.0, 15.0);
                leaf1 = new Area(leaf);
                leaf2.intersect(leaf1);
                g2.fill(leaf2);

                /*
                 * creates the stem by filling the Area resulting from the
                 * subtraction of two Area objects created from an
                 * ellipse.
                 */
                Ellipse2D stem = new Ellipse2D.Double(x, y - 42, 40.0, 40.0);
                Area st1 = new Area(stem);
                stem.setFrame(x + 3, y - 47, 50.0, 50.0);
                st1.subtract(new Area(stem));
                g2.setColor(Color.black);
                g2.fill(st1);

                /*
                 * creates the pear itself by filling the Area resulting
                 * from the union of two Area objects created by two
                 * different ellipses.
                 */
                Ellipse2D circle = new Ellipse2D.Double(x - 25, y, 50.0, 50.0);
                Ellipse2D oval = new Ellipse2D.Double(x - 19, y - 20, 40.0, 70.0);
                Area circ = new Area(circle);
                circ.add(new Area(oval));

                g2.setColor(Color.yellow);
                g2.fill(circ);
                return;
            }

            g2.fill(area);
            g2.setColor(Color.red);
            g2.draw(area);
        }

        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            Dimension d = getSize();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, d.width, d.height);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            drawDemo(d.width, d.height, g2);
        }
    } // End Demo class

    /**
     * The DemoControls class provides buttons for choosing CAG operations as
     * well as no CAG operations and multiple CAG operations (pear).
     */
    static class DemoControls extends JPanel implements ActionListener {

        Demo demo;
        JToolBar toolbar;
        JComboBox combo;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            add(toolbar = new JToolBar());
            toolbar.setFloatable(false);
            addTool("nop", "no area operation", true);
            addTool("add", "add", false);
            addTool("sub", "subtract", false);
            addTool("xor", "exclusiveOr", false);
            addTool("int", "intersection", false);
            addTool("pear", "pear", false);
        }

        public void addTool(String str, String tooltip, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(str));
            b.setBackground(state ? Color.green : Color.lightGray);
            b.setToolTipText(tooltip);
            b.setSelected(state);
            b.addActionListener(this);
        }

        public void actionPerformed(ActionEvent e) {
            for (int i = 0; i < toolbar.getComponentCount(); i++) {
                JButton b = (JButton) toolbar.getComponentAtIndex(i);
                b.setBackground(Color.lightGray);
            }
            JButton b = (JButton) e.getSource();
            b.setBackground(Color.green);
            demo.areaType = b.getText();
            demo.repaint();
        }
    } // End DemoControls class

    public static void main(String argv[]) {
        final Areas demo = new Areas();
        demo.init();
        Frame f = new Frame("Java 2D(TM) Demo - Areas");
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        f.add(demo);
        f.pack();
        f.setSize(new Dimension(400, 300));
        f.setVisible(true);
    }
}
```

*s27.postimg.org/8aasyucxb/Areas.jpg

The Balls class demonstrates animated color bouncing balls.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.*;
import javax.swing.*;

/**
 * The Balls class demonstrates animated color bouncing balls.
 */
public class Balls extends JPanel {

    Demo demo;

    public Balls() {
        setLayout(new BorderLayout());
        add(demo = new Demo());
        add("North", new DemoControls(demo));
    }

    public void start() {
        demo.start();
    }

    public void stop() {
        demo.stop();
    }

    /**
     * The Demo class performs the animation and painting.
     */
    static class Demo extends JPanel implements Runnable {

        private static Color colors[]
                = {Color.red, Color.orange, Color.yellow, Color.green.darker(),
                    Color.blue, new Color(75, 00, 82), new Color(238, 130, 238)};
        private static final long serialVersionUID = 1L;
        private Thread thread;
        private BufferedImage bimg;
        private long now, deltaT, lasttime;
        private boolean active;
        private boolean clearDemo = true;
        protected Ball balls[] = new Ball[colors.length];


        /*
         * creates a new ball object for each color in the colors
         * array and selects the first, fourth, fifth and seventh balls
         * for initial display
         */
        public Demo() {
            setBackground(Color.black);
            for (int i = 0; i < colors.length; i++) {
                balls[i] = new Ball(colors[i], 40);
            }
            balls[0].isSelected = true;
            balls[3].isSelected = true;
            balls[4].isSelected = true;
            balls[6].isSelected = true;
        }

        public void step(int w, int h) {
            if (lasttime == 0) {
                lasttime = System.currentTimeMillis();
            }
            now = System.currentTimeMillis();
            deltaT = now - lasttime;
            active = false;
            for (int i = 0; i < balls.length; i++) {
                if (balls[i] == null) {
                    return;
                }
                balls[i].step(deltaT, w, h);
                if (balls[i].Vy > .02 || -balls[i].Vy > .02
                        || balls[i].y + balls[i].bsize < h) {
                    active = true;
                }
            }
            if (!active) {
                for (int i = 0; i < balls.length; i++) {
                    balls[i].Vx = (float) Math.random() / 4.0f - 0.125f;
                    balls[i].Vy = -(float) Math.random() / 4.0f - 0.2f;
                }
            }
        }

        public void drawDemo(int w, int h, Graphics2D g2) {
            for (int i = 0; i < balls.length; i++) {
                Ball b = balls[i];
                if (b == null || b.imgs[b.index] == null || !b.isSelected) {
                    continue;
                }
                g2.drawImage(b.imgs[b.index], (int) b.x, (int) b.y, this);
            }
            lasttime = now;
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
            }
            g2 = bimg.createGraphics();
            if (clearDemo) {
                g2.setBackground(getBackground());
                g2.clearRect(0, 0, w, h);
            }
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            thread = null;
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }

        /**
         * The Ball class creates 5 balls. Each ball increasingly blends the
         * color white with the specified color.
         */
        static final class Ball {

            public int bsize;
            public float x, y;
            public float Vx = 0.1f;
            public float Vy = 0.05f;
            public int nImgs = 5;
            public BufferedImage imgs[];
            public int index = (int) (Math.random() * (nImgs - 1));

            private final float inelasticity = .96f;
            private final float Ax = 0.0f;
            private final float Ay = 0.0002f;
            private final float Ar = 0.9f;
            private final int UP = 0;
            private final int DOWN = 1;
            private int indexDirection = UP;
            private boolean collision_x, collision_y;
            private float jitter;
            private Color color;
            private boolean isSelected;

            public Ball(Color color, int bsize) {
                this.color = color;
                makeImages(bsize);
            }

            public void makeImages(int bsize) {
                this.bsize = bsize * 2;
                int R = bsize;
                byte[] data = new byte[R * 2 * R * 2];
                int maxr = 0;
                for (int Y = 2 * R; --Y >= 0;) {
                    int x0 = (int) (Math.sqrt(R * R - (Y - R) * (Y - R)) + 0.5);
                    int p = Y * (R * 2) + R - x0;
                    for (int X = -x0; X < x0; X++) {
                        int x = X + 15;
                        int y = Y - R + 15;
                        int r = (int) (Math.sqrt(x * x + y * y) + 0.5);
                        if (r > maxr) {
                            maxr = r;
                        }
                        data[p++] = r <= 0 ? 1 : (byte) r;
                    }
                }

                imgs = new BufferedImage[nImgs];

                int bg = 255;
                byte red[] = new byte[256];
                red[0] = (byte) bg;
                byte green[] = new byte[256];
                green[0] = (byte) bg;
                byte blue[] = new byte[256];
                blue[0] = (byte) bg;

                // for each image, set its color
                for (int r = 0; r < imgs.length; r++) {
                    float b = 0.5f + (r + 1f) / imgs.length / 2f;
                    for (int i = maxr; i >= 1; --i) {
                        float d = (float) i / maxr;
                        red[i] = (byte) blend(blend(color.getRed(), 255, d), bg, b);
                        green[i] = (byte) blend(blend(color.getGreen(), 255, d), bg, b);
                        blue[i] = (byte) blend(blend(color.getBlue(), 255, d), bg, b);
                    }
                    IndexColorModel icm = new IndexColorModel(8, maxr + 1,
                            red, green, blue, 0);
                    DataBufferByte dbb = new DataBufferByte(data, data.length);
                    int bandOffsets[] = {0};
                    WritableRaster wr = Raster.createInterleavedRaster(dbb,
                            R * 2, R * 2, R * 2, 1, bandOffsets, null);
                    imgs[r] = new BufferedImage(icm, wr, icm.isAlphaPremultiplied(), null);
                }
            }

            // performs the blending of white and color
            private int blend(int fg, int bg, float fgfactor) {
                return (int) (bg + (fg - bg) * fgfactor);
            }

            // performs the action of bouncing off the window "walls"
            public void step(long deltaT, int w, int h) {
                collision_x = false;
                collision_y = false;

                jitter = (float) Math.random() * .01f - .005f;

                x += Vx * deltaT + (Ax / 2.0) * deltaT * deltaT;
                y += Vy * deltaT + (Ay / 2.0) * deltaT * deltaT;
                if (x <= 0.0f) {
                    x = 0.0f;
                    Vx = -Vx * inelasticity + jitter;
                    collision_x = true;
                }
                if (x + bsize >= w) {
                    x = w - bsize;
                    Vx = -Vx * inelasticity + jitter;
                    collision_x = true;
                }
                if (y <= 0) {
                    y = 0;
                    Vy = -Vy * inelasticity + jitter;
                    collision_y = true;
                }
                if (y + bsize >= h) {
                    y = h - bsize;
                    Vx *= inelasticity;
                    Vy = -Vy * inelasticity + jitter;
                    collision_y = true;
                }
                Vy = Vy + Ay * deltaT;
                Vx = Vx + Ax * deltaT;

                if (indexDirection == UP) {
                    index++;
                }
                if (indexDirection == DOWN) {
                    --index;
                }
                if (index + 1 == nImgs) {
                    indexDirection = DOWN;
                }
                if (index == 0) {
                    indexDirection = UP;
                }
            }
        }  // End Ball class

    } // End Demo class

    /**
     * The DemoControls class provides controls for choosing which colored ball
     * to display, the size of the balls and whether or not to clear the drawing
     * surface.
     */
    static class DemoControls extends JPanel implements ActionListener {

        Demo demo;
        JToolBar toolbar;
        JComboBox<String> combo;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            add(toolbar = new JToolBar());
            toolbar.setFloatable(false);
            addTool("Clear", true);
            addTool("R", demo.balls[0].isSelected);
            addTool("O", demo.balls[1].isSelected);
            addTool("Y", demo.balls[2].isSelected);
            addTool("G", demo.balls[3].isSelected);
            addTool("B", demo.balls[4].isSelected);
            addTool("I", demo.balls[5].isSelected);
            addTool("V", demo.balls[6].isSelected);
            add(combo = new JComboBox<>());
            combo.addItem("10");
            combo.addItem("20");
            combo.addItem("30");
            combo.addItem("40");
            combo.addItem("50");
            combo.addItem("60");
            combo.addItem("70");
            combo.addItem("80");
            combo.setSelectedIndex(3);
            combo.addActionListener(this);
        }

        public void addTool(String str, boolean state) {
            JButton b = (JButton) toolbar.add(new JButton(str));
            b.setBackground(state ? Color.green : Color.lightGray);
            b.setSelected(state);
            b.addActionListener(this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() instanceof JComboBox) {
                int size = Integer.parseInt((String) combo.getSelectedItem());
                for (int i = 0; i < demo.balls.length; i++) {
                    demo.balls[i].makeImages(size);
                }
                return;
            }
            JButton b = (JButton) e.getSource();
            b.setSelected(!b.isSelected());
            b.setBackground(b.isSelected() ? Color.green : Color.lightGray);
            if (b.getText().equals("Clear")) {
                demo.clearDemo = b.isSelected();
            } else {
                int index = toolbar.getComponentIndex(b) - 1;
                demo.balls[index].isSelected = b.isSelected();
            }
        }
    } // End DemoControls

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final Balls demo = new Balls();
                JFrame f = new JFrame("Balls");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add(demo);
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(scrDim.width / 2 + 75, scrDim.height / 2 + 150);
                f.setVisible(true);
                demo.start();
            }
        });
    }
} // End Balls class
```

*s28.postimg.org/7aclgr4i1/Balls.jpg

 The BezierAnim class renders an animated Bezier Curve with the drawing style
 and paint and the filling paint selected by the user


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import javax.swing.*;

/**
 * The BezierAnim class renders an animated Bezier Curve with the drawing style
 * and paint and the filling paint selected by the user.
 */
public class BezierAnim extends JPanel {

    Demo demo;

    public BezierAnim() {
        setLayout(new BorderLayout());
        add(demo = new Demo());
        add("North", new DemoControls(demo));
    }

    public void start() {
        demo.start();
    }

    public void stop() {
        demo.stop();
    }

    /**
     * The Demo class performs the animation and the painting.
     */
    public class Demo extends JPanel implements Runnable {

        private Thread thread;
        private BufferedImage bimg;
        private static final int NUMPTS = 6;

        //  solid line stoke
        protected BasicStroke solid = new BasicStroke(10.0f,
                BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
        //  dashed line stroke
        protected BasicStroke dashed = new BasicStroke(10.0f,
                BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10, new float[]{5}, 0);
        private float animpts[] = new float[NUMPTS * 2];
        private float deltas[] = new float[NUMPTS * 2];
        protected Paint fillPaint, drawPaint;

        // indicates whether or not to fill shape
        protected boolean doFill = true;

        // indicates whether or not to draw shape
        protected boolean doDraw = true;
        protected GradientPaint gradient;
        protected BasicStroke stroke;

        public Demo() {
            setBackground(Color.white);
            gradient = new GradientPaint(0, 0, Color.red, 200, 200, Color.yellow);
            fillPaint = gradient;
            drawPaint = Color.blue;
            stroke = solid;
        }

        // generates new points for the path
        public void animate(float[] pts, float[] deltas, int i, int limit) {
            float newpt = pts[i] + deltas[i];
            if (newpt <= 0) {
                newpt = -newpt;
                deltas[i] = (float) (Math.random() * 4.0 + 2.0);
            } else if (newpt >= (float) limit) {
                newpt = 2.0f * limit - newpt;
                deltas[i] = -(float) (Math.random() * 4.0 + 2.0);
            }
            pts[i] = newpt;
        }

        /*
         * generates random points with the specified surface width
         * and height for the path
         */
        public void reset(int w, int h) {
            for (int i = 0; i < animpts.length; i += 2) {
                animpts[i + 0] = (float) (Math.random() * w);
                animpts[i + 1] = (float) (Math.random() * h);
                deltas[i + 0] = (float) (Math.random() * 6.0 + 4.0);
                deltas[i + 1] = (float) (Math.random() * 6.0 + 4.0);
                if (animpts[i + 0] > w / 2.0f) {
                    deltas[i + 0] = -deltas[i + 0];
                }
                if (animpts[i + 1] > h / 2.0f) {
                    deltas[i + 1] = -deltas[i + 1];
                }
            }
            gradient = new GradientPaint(0, 0, Color.red, w * .7f, h * .7f, Color.yellow);
        }

        // calls animate for every point in animpts
        public void step(int w, int h) {
            for (int i = 0; i < animpts.length; i += 2) {
                animate(animpts, deltas, i + 0, w);
                animate(animpts, deltas, i + 1, h);
            }
        }

        // sets the points of the path and draws and fills the path
        public void drawDemo(int w, int h, Graphics2D g2) {
            float[] ctrlpts = animpts;
            int len = ctrlpts.length;
            float prevx = ctrlpts[len - 2];
            float prevy = ctrlpts[len - 1];
            float curx = ctrlpts[0];
            float cury = ctrlpts[1];
            float midx = (curx + prevx) / 2.0f;
            float midy = (cury + prevy) / 2.0f;
            GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
            gp.moveTo(midx, midy);
            for (int i = 2; i <= ctrlpts.length; i += 2) {
                float x1 = (midx + curx) / 2.0f;
                float y1 = (midy + cury) / 2.0f;
                prevx = curx;
                prevy = cury;
                if (i < ctrlpts.length) {
                    curx = ctrlpts[i + 0];
                    cury = ctrlpts[i + 1];
                } else {
                    curx = ctrlpts[0];
                    cury = ctrlpts[1];
                }
                midx = (curx + prevx) / 2.0f;
                midy = (cury + prevy) / 2.0f;
                float x2 = (prevx + midx) / 2.0f;
                float y2 = (prevy + midy) / 2.0f;
                gp.curveTo(x1, y1, x2, y2, midx, midy);
            }
            gp.closePath();
            if (doDraw) {
                g2.setPaint(drawPaint);
                g2.setStroke(stroke);
                g2.draw(gp);
            }
            if (doFill) {
                if (fillPaint instanceof GradientPaint) {
                    fillPaint = gradient;
                }
                g2.setPaint(fillPaint);
                g2.fill(gp);
            }
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
                reset(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            if (bimg != null) {
                g.drawImage(bimg, 0, 0, this);
            }
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            thread = null;
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    break;
                }
            }
            thread = null;
        }
    } // End Demo class

    /**
     * The DemoControls class controls fills and strokes.
     */
    static class DemoControls extends JPanel implements ActionListener {

        static TexturePaint tp1, tp2;

        static {
            BufferedImage bi = new BufferedImage(2, 1, BufferedImage.TYPE_INT_RGB);
            bi.setRGB(0, 0, 0xff00ff00);
            bi.setRGB(1, 0, 0xffff0000);
            tp1 = new TexturePaint(bi, new Rectangle(0, 0, 2, 1));
            bi = new BufferedImage(2, 1, BufferedImage.TYPE_INT_RGB);
            bi.setRGB(0, 0, 0xff0000ff);
            bi.setRGB(1, 0, 0xffff0000);
            tp2 = new TexturePaint(bi, new Rectangle(0, 0, 2, 1));
        }

        Demo demo;
        static Paint drawPaints[]
                = {new Color(0, 0, 0, 0), Color.blue, new Color(0, 0, 255, 126),
                    Color.blue, tp2};
        static String drawName[]
                = {"No Draw", "Blue", "Blue w/ Alpha", "Blue Dash", "Texture"};
        static Paint fillPaints[]
                = {new Color(0, 0, 0, 0), Color.green, new Color(0, 255, 0, 126),
                    tp1, new GradientPaint(0, 0, Color.red, 30, 30, Color.yellow)};
        String fillName[]
                = {"No Fill", "Green", "Green w/ Alpha", "Texture", "Gradient"};

        JMenu fillMenu, drawMenu;
        JMenuItem fillMI[] = new JMenuItem[fillPaints.length];
        JMenuItem drawMI[] = new JMenuItem[drawPaints.length];
        PaintedIcon fillIcons[] = new PaintedIcon[fillPaints.length];
        PaintedIcon drawIcons[] = new PaintedIcon[drawPaints.length];
        Font font = new Font("serif", Font.PLAIN, 10);
        Thread thread;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);

            JMenuBar drawMenuBar = new JMenuBar();
            add(drawMenuBar);

            JMenuBar fillMenuBar = new JMenuBar();
            add(fillMenuBar);

            drawMenu = drawMenuBar.add(new JMenu("Draw Choice"));
            drawMenu.setFont(font);
            for (int i = 0; i < drawPaints.length; i++) {
                drawIcons[i] = new PaintedIcon(drawPaints[i]);
                drawMI[i] = drawMenu.add(new JMenuItem(drawName[i]));
                drawMI[i].setFont(font);
                drawMI[i].setIcon(drawIcons[i]);
                drawMI[i].addActionListener(this);
            }
            drawMenu.setIcon(drawIcons[1]);

            fillMenu = fillMenuBar.add(new JMenu("Fill Choice"));
            fillMenu.setFont(font);
            for (int i = 0; i < fillPaints.length; i++) {
                fillIcons[i] = new PaintedIcon(fillPaints[i]);
                fillMI[i] = fillMenu.add(new JMenuItem(fillName[i]));
                fillMI[i].setFont(font);
                fillMI[i].setIcon(fillIcons[i]);
                fillMI[i].addActionListener(this);
            }
            fillMenu.setIcon(fillIcons[fillPaints.length - 1]);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Object obj = e.getSource();
            for (int i = 0; i < fillPaints.length; i++) {
                if (obj.equals(fillMI[i])) {
                    demo.doFill = true;
                    demo.fillPaint = fillPaints[i];
                    fillMenu.setIcon(fillIcons[i]);
                    break;
                }
            }
            for (int i = 0; i < drawPaints.length; i++) {
                if (obj.equals(drawMI[i])) {
                    demo.doDraw = true;
                    demo.drawPaint = drawPaints[i];
                    if (((JMenuItem) obj).getText().endsWith("Dash")) {
                        demo.stroke = demo.dashed;
                    } else {
                        demo.stroke = demo.solid;
                    }
                    drawMenu.setIcon(drawIcons[i]);
                    break;
                }
            }
            if (obj.equals(fillMI[0])) {
                demo.doFill = false;
            } else if (obj.equals(drawMI[0])) {
                demo.doDraw = false;
            }
        }

        /**
         * The PaintedIcon class provides little filled icons for the fill and
         * stroke menu choices.
         */
        static class PaintedIcon implements Icon {

            Paint paint;

            public PaintedIcon(Paint p) {
                this.paint = p;
            }

            @Override
            public void paintIcon(Component c, Graphics g, int x, int y) {
                Graphics2D g2 = (Graphics2D) g;
                g2.setPaint(paint);
                g2.fillRect(x, y, getIconWidth(), getIconHeight());
                g2.setColor(Color.gray);
                g2.draw3DRect(x, y, getIconWidth() - 1, getIconHeight() - 1, true);
            }

            @Override
            public int getIconWidth() {
                return 12;
            }

            @Override
            public int getIconHeight() {
                return 12;
            }
        } // End PaintedIcon class

    } // End DemoControls class

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final BezierAnim demo = new BezierAnim();
                JFrame f = new JFrame("BezierAnim");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add(demo);
                f.pack();
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2, scrDim.height / 2));
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                demo.start();
            }
        });
    }
} // End BezierAnim class
```

*s3.postimg.org/t2yz4zm2n/Bezier_Anim.jpg

*Rendering Text*

To render a text string, you call Graphics2D.drawString, passing in the string that you want to render. For more information about rendering text and selecting fonts, see “Fonts and Text Layout” on page 45.
2.4.4 Rendering Images

To render an Image, you create the Image and call Graphics2D.drawImage.

*Geometries*

The Java 2D™ API provides several classes that define common geometric objects, such as points, lines, curves, and rectangles. These new geometry classes are part of the java.awt.geom package. For backward compatibility, the geometry classes that existed in previous versions of the JDK software, such as Rectangle, Point, and Polygon, remain in the java.awt package.

The Java 2D API geometries such as GeneralPath, Arc2D, and Rectangle2D implement the Shape interface defined in java.awt. Shape provides a common protocol for describing and inspecting geometric path objects. A new interface, PathIterator, defines methods for retrieving elements from a geometry.

Using the geometry classes, you can easily define and manipulate virtually any two-dimensional object.
3.1 Interfaces and Classes

The following tables list the key geometry interfaces and classes. Most of these interfaces and classes are part of the java.awt.geom package. Some, like Shape, are part of the java.awt package, primarily to maintain backward compatibility with earlier versions of the JDK software.

*Interface         Description*

PathIterator  Defines methods for retrieving elements from a path.

Shape         (java.awt)Provides a common set of methods for describing and inspecting geometric path objects. Implemented by GeneralPath and other geometry classes.

Class             Description

Arc2D             Extends: RectangularShape

Arc2D.Double      Represents an arc defined by a bounding rectangle, start angle, angular extent, and a closure type. Implemented to specify arcs in float and double precision: 

Arc2D.Float       Arc2D.Float and Arc2D.Double.

Area              Implements: Shape, Cloneable
                  Represents an area geometry that supports boolean operations.

CubicCurve2D      Implements: Shape

CubicCurve2D.Double Represents a cubic parametric curve segment in (w) coordinate space. Implemented to specify cubic curves in float and double precision: CubicCurve2D.Float and CubicCurve2D.Double.

CubicCurve2D.Float

Dimension2D       Encapsulates a width and height dimension. Abstract superclass for all objects that store a 2D dimension.


Ellipse2D         Extends: RectangularShape

Ellipse2D.Double  Represents an ellipse defined by a bounding rectangle. Implemented to specify ellipses in float and double precision: Ellipse2D.Float and 

Ellipse2D.Float   Ellipse2D.Double.

FlatteningPathIterator Returns a flattened view of a PathIterator object.
                  Can be used to provide flattening behavior for Shapes that don’t perform the interpolation calculations themselves.


GeneralPath       Implements: Shape
                  Represents a geometric path constructed from lines and quadratic and cubic curves.


Line2D           Implements: Shape

Line2D.Double    Represents a line segment in (x, y) coordinate space. Implemented to specify lines in float and double precision: Line2D.Float and Line2D.Double.

Line2D.Float

Point2D          A point representing a location in (x,y) coordinate space. Implemented to specify points in float and double precision: Point2D.Float and Point2D.Double.

Point2D.Double

Point2D.Float

QuadCurve2D      Implements: Shape
                 Represents a quadratic parametric curve segment in (x, y) coordinate space. Implemented to specify quadratic curves in float and double precision: QuadCurve2D.Float and QuadCurve2D.Double.

QuadCurve2D.Double

QuadCurve2D.Float

Rectangle2D      Extends: RectangularShape
                 Represents a rectangle defined by a location (x, y) and dimension (w x h). Implemented to specify rectangles in float and double precision: Rectangle2D.Float and Rectangle2D.Double.

Rectangle2D.Double

Rectangle2D.Float


RectangularShape Implements: Shape
	         Provides common manipulation routines for operating on shapes that have rectangular bounds.

RoundRectangle2D Extends: RectangularShape
                 Represents a rectangle with rounded corners defined by a location (x, y), a dimension (w x h), and the width and height of the corner arc. Implemented to specify round rectangles in float and double precision: RoundRectangle2D.Float and RoundRectangle2D.Double.

RoundRectangle2D.Double

RoundRectangle2D.Float



*Geometry Concepts*

A Shape is an instance of any class that implements the Shape interface, such as GeneralPath or Rectangle2D.Float. A Shape’s contour (outline) is referred to as its path.

When a Shape is drawn, the pen style defined by the Stroke object in the Graphics2D context is applied to the Shape’s path. When a Shape is filled, the Paint in the Graphics2D context is applied to the area within its path. For more information, see “Rendering with Graphics2D” on page 15.

A Shape’s path can be also used to define a clipping path. A clipping path determines what pixels are rendered—only those pixels that lie within the area defined by the clipping path are rendered. The clipping path is part of the Graphics2D context. For more information, see “Setting the Clipping Path” on page 32.

A GeneralPath is a shape that can be used to represent any two-dimensional object that can be constructed from lines and quadratic or cubic curves. For convenience, java.awt.geom provides additional implementations of the Shape interface that represent common geometric objects such as rectangles, ellipses, arcs, and curves. The Java2D™ API also provides a special type of shape that supports constructive area geometry.
3.2.1 Constructive Area Geometry

Constructive Area Geometry (CAG) is the process of creating new geometric objects by performing boolean operations on existing objects. In the Java 2D API, a special type of Shape called an Area supports boolean operations. You can construct an Area from any Shape.

Areas support the following Boolean operations:

    Union

    Intersection

    Subtraction

    Exclusive OR (XOR)


Bounds and Hit Testing

A bounding box is a rectangle that fully encloses a shape’s geometry. Bounding boxes are used to determine whether or not an object has been selected or “hit” by the user.

The Shape interface defines two methods for retrieving a shape’s bounding box, getBounds and getBounds2D. The getBounds2D method returns a Rectangle2D instead of a Rectangle, providing a higher-precision description of the shape’s bounding box.

Shape also provides methods for determining whether or not:

    A specified point lies within the bounds of the shape (contains)

    A specified rectangle lies totally within the bounds of the shape (contains)

    A specified rectangle intersects the shape (intersects)

3.3 Combining Areas to Create New Shapes

Areas can be used to quickly construct complex Shapes from simple shapes such as circles and squares. To create a new complex Shape by combining Areas:

    Using Shapes, construct the Areas to be combined.

    Call the appropriate Boolean operators: add, subtract, intersect, exclusiveOr.

Creating a Custom Shape

You can implement the Shape interface to create a class that defines a new type of shape. It doesn’t matter how you represent the shape internally, as long as you can implement the Shape interface methods. The Shape must be able to generate a path that specifies its contour.

For example, you could create a simple implementation of Shape that represents polygons as arrays of points. Once the polygon is built, it could be passed to draw, setClip, or any other method that expects a Shape object as an argument.

The PolygonPath class must implement the Shape interface methods:

    contains

    getBounds

    getBounds2D

    getPathIterator

    intersects

Here is a demo that draws an Arcs.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;

/**
 * The Arcs class demonstrates the three closure types for arcs, Arc2D Open,
 * Chord & Pie. Animated Pie Arc.
 */
public class Arcs extends JApplet implements Runnable {

    // array containing arc closure types.
    private static String types[] = {"Arc2D.CHORD", "Arc2D.OPEN", "Arc2D.PIE"};

    // indicates that animated arc's mouth is closed.
    private static final int CLOSE = 0;

    // indicates that animated arc's mouth is open.
    private static final int OPEN = 1;

    // indicate direction of animated arc.
    private static final int FORWARD = 0;
    private static final int BACKWARD = 1;
    private static final int DOWN = 2;
    private static final int UP = 3;
    private static final long serialVersionUID = 1L;

    private Thread thread;
    private BufferedImage bimg;
    private int aw, ah;              // animated arc width & height
    private int x, y;                // position of animated arc
    private int angleStart = 45;     //  the starting angle of the arc
    private int angleExtent = 270;   //  the extent of the angle of the arc
    private int mouth = CLOSE;
    private int direction = FORWARD;

    @Override
    public void init() {
        setBackground(Color.white);
    }

    // Resets the position and size of the animated arc
    public void reset(int w, int h) {
        x = 0;
        y = 0;
        aw = w / 12;
        ah = h / 12;
    }

    public void step(int w, int h) {
        // computes the  direction of the animated arc
        if (x + aw >= w - 5 && direction == FORWARD) {
            direction = DOWN;
        }
        if (y + ah >= h - 5 && direction == DOWN) {
            direction = BACKWARD;
        }
        if (x - aw <= 5 && direction == BACKWARD) {
            direction = UP;
        }
        if (y - ah <= 5 && direction == UP) {
            direction = FORWARD;
        }

        // computes the angle start and extent
        if (mouth == CLOSE) {
            angleStart -= 5;
            angleExtent += 10;
        }
        if (mouth == OPEN) {
            angleStart += 5;
            angleExtent -= 10;
        }
        if (direction == FORWARD) {
            x += 5;
            y = 0;
        }
        if (direction == DOWN) {
            x = w;
            y += 5;
        }
        if (direction == BACKWARD) {
            x -= 5;
            y = h;
        }
        if (direction == UP) {
            x = 0;
            y -= 5;
        }
        if (angleStart == 0) {
            mouth = OPEN;
        }
        if (angleStart > 45) {
            mouth = CLOSE;
        }
    }

    public void drawDemo(int w, int h, Graphics2D g2) {

        g2.setStroke(new BasicStroke(5.0f));

        // draw arcs
        for (int i = 0; i < types.length; i++) {
            Arc2D arc = new Arc2D.Float(i);
            arc.setFrame((i + 1) * w * .2, (i + 1) * h * .2, w * .17, h * .17);
            arc.setAngleStart(45);
            arc.setAngleExtent(270);
            g2.setColor(Color.blue);
            g2.draw(arc);
            g2.setColor(Color.gray);
            g2.fill(arc);
            g2.setColor(Color.black);
            g2.drawString(types[i], (int) ((i + 1) * w * .2), (int) ((i + 1) * h * .2 - 3));
        }

        // draws animated pie arc
        Arc2D pieArc = new Arc2D.Float(Arc2D.PIE);
        pieArc.setFrame(0, 0, aw, ah);
        pieArc.setAngleStart(angleStart);
        pieArc.setAngleExtent(angleExtent);
        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
        switch (direction) {
            case DOWN:
                at.rotate(Math.toRadians(90));
                break;
            case BACKWARD:
                at.rotate(Math.toRadians(180));
                break;
            case UP:
                at.rotate(Math.toRadians(270));
        }
        g2.setColor(Color.blue);
        g2.fill(at.createTransformedShape(pieArc));
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
    }

    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    public static void main(String argv[]) {
        final Arcs demo = new Arcs();
        demo.init();
        Frame f = new Frame("Java2D - Arcs");
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
                demo.start();
            }

            @Override
            public void windowIconified(WindowEvent e) {
                demo.stop();
            }
        });
        f.add(demo);
        f.pack();
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        f.setSize(new Dimension(scrDim.width / 2, scrDim.height / 2));
        f.setVisible(true);
        demo.start();
    }
}
```

*s9.postimg.org/ffzcfosij/Arcs.jpg

The CurveQuadTo class demonstrates Cubic & Quad curves implemented through GeneralPath.

```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.geom.GeneralPath;
import javax.swing.*;

/**
 * The CurveQuadTo class demonstrates Cubic & Quad curves implemented through
 * GeneralPath.
 */
public class CurveQuadTo extends JApplet {

    @Override
    public void init() {
        setBackground(Color.white);
    }

    public void drawDemo(int w, int h, Graphics2D g2) {

        GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
        p.moveTo(w * .2f, h * .25f);

        // adds a cubic curve to the path
        p.curveTo(w * .4f, h * .5f, w * .6f, 0.0f, w * .8f, h * .25f);

        p.moveTo(w * .2f, h * .6f);

        // adds a quad curve to the path
        p.quadTo(w * .5f, h * 1.0f, w * .8f, h * .6f);

        g2.setColor(Color.lightGray);
        g2.fill(p);
        g2.setColor(Color.black);
        g2.draw(p);
        g2.drawString("curveTo", (int) (w * .2), (int) (h * .25f) - 5);
        g2.drawString("quadTo", (int) (w * .2), (int) (h * .6f) - 5);
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        Dimension d = getSize();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, d.width, d.height);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        drawDemo(d.width, d.height, g2);
    }

    public static void main(String argv[]) {
        final CurveQuadTo demo = new CurveQuadTo();
        demo.init();
        JFrame f = new JFrame("Java2D - CurveQuadTo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add("Center", demo);
        f.pack();
        f.setSize(new Dimension(400, 300));
        f.setVisible(true);
    }
}
```

*s1.postimg.org/mr0b3y9hn/Curve_Quad_To.jpg

The Ellipses class draws 25 animated expanding ellipses.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;

/**
 * The Ellipses class draws 25 animated expanding ellipses.
 */
public class Ellipses extends JApplet implements Runnable {

    private static final Color colors[] = {Color.blue, Color.cyan, Color.green,
        Color.magenta, Color.orange, Color.pink, Color.red,
        Color.yellow, Color.lightGray, Color.white};
    private static final long serialVersionUID = 1L;
    private Thread thread;
    private BufferedImage bimg;
    private Ellipse2D.Float[] ellipses;
    private double esize[];
    private float estroke[];
    private double maxSize;

    @Override
    public void init() {
        setBackground(Color.black);

        // an array of type Ellipse2D.Float
        ellipses = new Ellipse2D.Float[25];

        // a double array initialized to the length of the ellipses array
        esize = new double[ellipses.length];

        // a float array initialized to the length of the ellipses array
        estroke = new float[ellipses.length];

        // fills the ellipses array with Ellipse2D.Float objects
        for (int i = 0; i < ellipses.length; i++) {
            ellipses[i] = new Ellipse2D.Float();
            // gets location for each ellipse with the given random size
            getRandomXY(i, 20 * Math.random(), 200, 200);
        }
    }


    /*
     * sets the bounds of the ellipse specified by i, using the given
     * width, height and random size.
     */
    public void getRandomXY(int i, double size, int w, int h) {
        esize[i] = size;
        estroke[i] = 1.0f;
        double x = Math.random() * (w - (maxSize / 2));
        double y = Math.random() * (h - (maxSize / 2));
        ellipses[i].setFrame(x, y, size, size);
    }


    /*
     * resets the bounds of the ellipses with the given width and height
     */
    public void reset(int w, int h) {
        maxSize = w / 10;
        for (int i = 0; i < ellipses.length; i++) {
            getRandomXY(i, maxSize * Math.random(), w, h);
        }
    }


    /*
     * increase each stroke size and ellipse size until maxSize
     */
    public void step(int w, int h) {
        for (int i = 0; i < ellipses.length; i++) {
            estroke[i] += 0.025f;
            esize[i]++;
            if (esize[i] > maxSize) {
                getRandomXY(i, 1, w, h);
            } else {
                ellipses[i].setFrame(ellipses[i].getX(), ellipses[i].getY(),
                        esize[i], esize[i]);
            }
        }
    }

    public void drawDemo(int w, int h, Graphics2D g2) {
        // sets the color and stroke size and draws each ellipse
        for (int i = 0; i < ellipses.length; i++) {
            g2.setColor(colors[i % colors.length]);
            g2.setStroke(new BasicStroke(estroke[i]));
            g2.draw(ellipses[i]);
        }
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
    }

    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    public static void main(String argv[]) {
        final Ellipses demo = new Ellipses();
        demo.init();
        Frame f = new Frame("Java2D - Ellipses");
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
                demo.start();
            }

            @Override
            public void windowIconified(WindowEvent e) {
                demo.stop();
            }
        });
        f.add(demo);
        f.pack();
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        f.setSize(new Dimension(scrDim.width / 2, scrDim.height / 2));
        f.setVisible(true);
        demo.start();
    }
}
```

*s28.postimg.org/vc6wyaj61/Ellipses.jpg

The GradAnim class demonstrates GradientPaint animation.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * The GradAnim class demonstrates GradientPaint animation.
 */
public class GradAnim extends JApplet implements Runnable {

    private static final int MAX_HUE = 256 * 6;
    private animval x1, y1, x2, y2;
    private int hue = (int) (Math.random() * MAX_HUE);
    private Thread thread;
    private BufferedImage bimg;

    @Override
    public void init() {
        setBackground(Color.white);

        // initializes starting and ending coordinates for the gradient
        x1 = new animval(0, 300, 2, 10);
        y1 = new animval(0, 300, 2, 10);
        x2 = new animval(0, 300, 2, 10);
        y2 = new animval(0, 300, 2, 10);
    }

    // resets the limits of the starting and ending coordinates
    public void reset(int w, int h) {
        x1.newlimits(0, w);
        y1.newlimits(0, h);
        x2.newlimits(0, w);
        y2.newlimits(0, h);
    }

    // calls anim() to generate new coordinates and generates a new hue
    public void step(int w, int h) {
        x1.anim();
        y1.anim();
        x2.anim();
        y2.anim();
        hue = (hue + (int) (Math.random() * 10)) % MAX_HUE;
    }

    // generates a new color with the specified hue
    public static Color getColor(int hue) {
        int leg = (hue / 256) % 6;
        int step = (hue % 256) * 2;
        int falling = (step < 256) ? 255 : 511 - step;
        int rising = (step < 256) ? step : 255;
        int r, g, b;
        r = g = b = 0;
        switch (leg) {
            case 0:         // red
                r = 255;
                break;
            case 1:        // reddish-green
                r = falling;
                g = rising;
                break;
            case 2:        // green
                g = 255;
                break;
            case 3:        // greenish-blue
                g = falling;
                b = rising;
                break;
            case 4:       // blue
                b = 255;
                break;
            case 5:       // blueish-red
                b = falling;
                r = rising;
                break;
        }
        return new Color(r, g, b);
    }

    // renders the gradient and the yellow line
    public void drawDemo(int w, int h, Graphics2D g2) {

        Color c1 = getColor(hue);
        Color c2 = getColor(hue + 256 * 3);
        GradientPaint gp = new GradientPaint(x1.getFlt(), y1.getFlt(), c1,
                x2.getFlt(), y2.getFlt(), c2,
                true);
        g2.setPaint(gp);
        g2.fillRect(0, 0, w, h);
        g2.setColor(Color.yellow);
        g2.drawLine(x1.getInt(), y1.getInt(), x2.getInt(), y2.getInt());
    }

    public Graphics2D createGraphics2D(int w, int h) {
        Graphics2D g2 = null;
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
            bimg = (BufferedImage) createImage(w, h);
            reset(w, h);
        }
        g2 = bimg.createGraphics();
        g2.setBackground(getBackground());
        g2.clearRect(0, 0, w, h);
        return g2;
    }

    @Override
    public void paint(Graphics g) {
        Dimension d = getSize();
        step(d.width, d.height);
        Graphics2D g2 = createGraphics2D(d.width, d.height);
        drawDemo(d.width, d.height, g2);
        g2.dispose();
        g.drawImage(bimg, 0, 0, this);
    }

    @Override
    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    @Override
    public synchronized void stop() {
        thread = null;
    }

    @Override
    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                break;
            }
        }
        thread = null;
    }

    /**
     * The animval class generates new coordinate values used to set the
     * starting and ending points of the gradient.
     */
    public final class animval {

        float curval;
        float lowval;
        float highval;
        float currate;
        float lowrate;
        float highrate;

        public animval(int lowval, int highval,
                int lowrate, int highrate) {
            this.lowval = lowval;
            this.highval = highval;
            this.lowrate = lowrate;
            this.highrate = highrate;

            // the current coordinate value
            this.curval = randval(lowval, highval);

            // used as increment for curval
            this.currate = randval(lowrate, highrate);
        }

        public float randval(float low, float high) {
            return (float) (low + Math.random() * (high - low));
        }

        public float getFlt() {
            return curval;
        }

        public int getInt() {
            return (int) curval;
        }

        // called by x1, y1, x2, y2 to obtain new coordinate values
        public void anim() {
            curval += currate;
            clip();
        }

        // resets curval and currate
        public void clip() {
            if (curval > highval) {
                curval = highval - (curval - highval);
                if (curval < lowval) {
                    curval = highval;
                }
                currate = -randval(lowrate, highrate);
            } else if (curval < lowval) {
                curval = lowval + (lowval - curval);
                if (curval > highval) {
                    curval = lowval;
                }
                currate = randval(lowrate, highrate);
            }
        }

        // resets the coordinate limits
        public void newlimits(int lowval, int highval) {
            this.lowval = lowval;
            this.highval = highval;
            clip();
        }
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final GradAnim demo = new GradAnim();
                demo.init();
                JFrame f = new JFrame("Gradient Animation");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                f.pack();
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height / 2 + 150));
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                demo.start();
            }
        });
    }
}
```

*s27.postimg.org/w5dn82eb3/Grad_Anim.jpg

Radial GradientPaint animation.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

/**
 * GradientPaint animation.
 */
public class GradAnim2 extends JApplet {

    Demo demo;

    @Override
    public void init() {
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        add(demo = new Demo());
        add("North", new DemoControls(demo));
    }

    @Override
    public void start() {
        demo.start();
    }

    @Override
    public void stop() {
        demo.stop();
    }

    class Demo extends JPanel implements Runnable {

        private static final int BASIC_GRADIENT = 0;
        private static final int LINEAR_GRADIENT = 1;
        private static final int RADIAL_GRADIENT = 2;
        private static final int FOCUS_GRADIENT = 3;
        private static final int MAX_HUE = 256 * 6;
        private final animval x1, y1, x2, y2;
        private int hue = (int) (Math.random() * MAX_HUE);
        private int gradientType;
        private Thread thread;
        private BufferedImage bimg;
        private volatile boolean running = false;

        public Demo() {
            setBackground(Color.white);
            x1 = new animval(0, 300, 2, 10);
            y1 = new animval(0, 300, 2, 10);
            x2 = new animval(0, 300, 2, 10);
            y2 = new animval(0, 300, 2, 10);
            gradientType = BASIC_GRADIENT;
        }

        public void reset(int w, int h) {
            x1.newlimits(0, w);
            y1.newlimits(0, h);
            x2.newlimits(0, w);
            y2.newlimits(0, h);
        }

        public void step(int w, int h) {
            x1.anim();
            y1.anim();
            x2.anim();
            y2.anim();
            hue = (hue + (int) (Math.random() * 10)) % MAX_HUE;
        }

        public Color getColor(int hue) {
            int leg = (hue / 256) % 6;
            int step = (hue % 256) * 2;
            int falling = (step < 256) ? 255 : 511 - step;
            int rising = (step < 256) ? step : 255;
            int r, g, b;
            r = g = b = 0;
            switch (leg) {
                case 0:
                    r = 255;
                    break;
                case 1:
                    r = falling;
                    g = rising;
                    break;
                case 2:
                    g = 255;
                    break;
                case 3:
                    g = falling;
                    b = rising;
                    break;
                case 4:
                    b = 255;
                    break;
                case 5:
                    b = falling;
                    r = rising;
                    break;
            }
            return new Color(r, g, b);
        }

        /**
         * @return the running
         */
        public boolean running() {
            return running;
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
                reset(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            return g2;
        }

        @Override
        public void paint(Graphics g) {
            Dimension d = getSize();
            step(d.width, d.height);
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            // Render the Gradient animation
            render(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
            running = true;
        }

        public synchronized void stop() {
            thread = null;
            running = false;
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                repaint();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }

        public void render(int w, int h, Graphics2D g2) {
            float fx1 = x1.getFlt();
            float fy1 = y1.getFlt();
            float fx2 = x2.getFlt();
            float fy2 = y2.getFlt();

            if ((fx1 == fx2) && (fy1 == fy2)) {
                // just to prevent the points from being coincident
                fx2++;
                fy2++;
            }

            Color c1 = getColor(hue);
            Color c2 = getColor(hue + 256 * 3);
            Paint gp;

            switch (gradientType) {
                case BASIC_GRADIENT:
                default:
                    gp = new GradientPaint(fx1, fy1, c1,
                            fx2, fy2, c2,
                            true);
                    break;
                case LINEAR_GRADIENT: {
                    float[] fractions = new float[]{0.0f, 0.2f, 1.0f};
                    Color c3 = getColor(hue + 256 * 2);
                    Color[] colors = new Color[]{c1, c2, c3};
                    gp = new LinearGradientPaint(fx1, fy1,
                            fx2, fy2,
                            fractions, colors,
                            CycleMethod.REFLECT);
                }
                break;

                case RADIAL_GRADIENT: {
                    float[] fractions = {0.0f, 0.2f, 0.8f, 1.0f};
                    Color c3 = getColor(hue + 256 * 2);
                    Color c4 = getColor(hue + 256 * 4);
                    Color[] colors = new Color[]{c1, c2, c3, c4};
                    float radius = (float) Point2D.distance(fx1, fy1, fx2, fy2);
                    gp = new RadialGradientPaint(fx1, fy1, radius,
                            fractions, colors,
                            CycleMethod.REFLECT);
                }
                break;

                case FOCUS_GRADIENT: {
                    float[] fractions = {0.0f, 0.2f, 0.8f, 1.0f};
                    Color c3 = getColor(hue + 256 * 4);
                    Color c4 = getColor(hue + 256 * 2);
                    Color[] colors = new Color[]{c1, c2, c3, c4};
                    float radius = (float) Point2D.distance(fx1, fy1, fx2, fy2);
                    float max = Math.max(w, h);
                    // This function will map the smallest radius to
                    // max/10 when the points are next to each other,
                    // max when the points are max distance apart,
                    // and >max when they are further apart (in which
                    // case the focus clipping code in RGP will clip
                    // the focus to be inside the radius).
                    radius = max * (((radius / max) * 0.9f) + 0.1f);
                    gp = new RadialGradientPaint(fx2, fy2, radius,
                            fx1, fy1,
                            fractions, colors,
                            CycleMethod.REPEAT);
                }
                break;
            }
            g2.setPaint(gp);
            g2.fillRect(0, 0, w, h);
            g2.setColor(Color.yellow);
            g2.drawLine(x1.getInt(), y1.getInt(), x2.getInt(), y2.getInt());
        }

        public final class animval {

            float curval;
            float lowval;
            float highval;
            float currate;
            float lowrate;
            float highrate;

            public animval(int lowval, int highval,
                    int lowrate, int highrate) {
                this.lowval = lowval;
                this.highval = highval;
                this.lowrate = lowrate;
                this.highrate = highrate;
                this.curval = randval(lowval, highval);
                this.currate = randval(lowrate, highrate);
            }

            public float randval(float low, float high) {
                return (float) (low + Math.random() * (high - low));
            }

            public float getFlt() {
                return curval;
            }

            public int getInt() {
                return (int) curval;
            }

            public void anim() {
                curval += currate;
                clip();
            }

            public void clip() {
                if (curval > highval) {
                    curval = highval - (curval - highval);
                    if (curval < lowval) {
                        curval = highval;
                    }
                    currate = -randval(lowrate, highrate);
                } else if (curval < lowval) {
                    curval = lowval + (lowval - curval);
                    if (curval > highval) {
                        curval = lowval;
                    }
                    currate = randval(lowrate, highrate);
                }
            }

            public void newlimits(int lowval, int highval) {
                this.lowval = lowval;
                this.highval = highval;
                clip();
            }
        }
    }

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final GradAnim2 demo = new GradAnim2();
                demo.init();
                JFrame f = new JFrame("GradAnim2");
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                    @Override
                    public void windowDeiconified(WindowEvent e) {
                        demo.start();
                    }

                    @Override
                    public void windowIconified(WindowEvent e) {
                        demo.stop();
                    }
                });
                f.add("Center", demo);
                Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
                f.setSize(new Dimension(scrDim.width / 2 + 75, scrDim.height / 2 + 150));
                f.setLocationRelativeTo(null);
                f.setVisible(true);
                demo.start();
            }
        });
    }

    static class DemoControls extends JPanel implements ActionListener {

        Demo demo;
        JComboBox<String> combo;

        [MENTION=139512]Sup[/MENTION]pressWarnings("LeakingThisInConstructor")
        public DemoControls(Demo demo) {
            this.demo = demo;
            combo = new JComboBox<>();
            combo.addActionListener(this);
            combo.addItem("2-color GradientPaint");
            combo.addItem("3-color LinearGradientPaint");
            combo.addItem("4-color RadialGradientPaint");
            combo.addItem("4-color RadialGradientPaint with focus");
            combo.setSelectedIndex(0);
            add(combo);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int index = combo.getSelectedIndex();
            if (index >= 0) {
                demo.gradientType = index;
            }
            if (!demo.running()) {
                demo.repaint();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 41);
        }
    }
}
```

*s8.postimg.org/u902wounl/Grad_Anim2.jpg



```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 *
 * @author Sowndar
 */
public class ImageFlip extends JFrame {

    public static Image image;
    private static final long serialVersionUID = 1L;
    private int imgWidth, imgHeight;
    private File directory = new File("Images/");
    private String[] format = ImageIO.getReaderFormatNames();

    public ImageFlip() {
        setBackground(Color.ORANGE);
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        String imgfiles[] = directory.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                for (int i = 0; i < format.length; i++) {
                    if (name.endsWith(format[i])) {
                        return true;
                    }
                }
                return false;
            }
        });
        int rand = (int) (imgfiles.length * Math.random());
        try {
            image = ImageIO.read(new File(directory + "/" + imgfiles[rand]));
            imgWidth = image.getWidth(null);
            imgHeight = image.getHeight(null);
            // Resize bigger Images
            if (imgWidth > scrDim.width / 3 || imgHeight > scrDim.height / 3) {
                imgWidth /= 2;
                imgHeight /= 2;
                image = image.getScaledInstance(imgWidth, imgHeight, Image.SCALE_SMOOTH);
            }
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
            System.exit(-1);
        }
        setTitle("ImageFlip");
        setSize(imgWidth * 2, imgHeight * 4);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    // Create a BufferedImage with the Star background
    public BufferedImage createBuffImage(Image image, int width, int height) {
        if (image != null) {
            int hh = height / 2;

            BufferedImage bi = new BufferedImage(width, hh, BufferedImage.TYPE_INT_RGB);
            Graphics2D big = bi.createGraphics();

            big.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            big.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            big.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            big.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);

            big.setBackground(getBackground());
            big.clearRect(0, 0, width, hh);

            big.setColor(Color.GREEN.darker().darker());
            GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO);
            p.moveTo(-width / 2.0f, -hh / 8.0f);
            p.lineTo(+width / 2.0f, -hh / 8.0f);
            p.lineTo(-width / 4.0f, +hh / 2.0f);
            p.lineTo(+0.0f, -hh / 2.0f);
            p.lineTo(+width / 4.0f, +hh / 2.0f);
            p.closePath();
            big.translate(width / 2, hh / 2);
            big.fill(p);

            int iw = image.getWidth(this);
            int ih = image.getHeight(this);
            if (hh < ih * 1.5) {
                ih = (int) (ih * ((hh / (ih * 1.5))));
            }
            big.drawImage(image, -image.getWidth(this) / 2, -ih / 2, iw, ih, this);
            return bi;
        }
        return null;
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        // Create a BufferedImage
        BufferedImage buffImg = createBuffImage(image, getWidth(), getHeight());
        // Draw the BufferedImage
        g2d.drawImage(buffImg, 0, 0, this);
        // Draw the invert of the BufferedImage
        g2d.drawImage(buffImg, 0, buffImg.getHeight() * 2, buffImg.getWidth(), -buffImg.getHeight(), this);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ImageFlip();
            }
        });
    }
}
```

*s10.postimg.org/6eytb9idh/Image_Flip.jpg

*Image Processing and Enhancement*

The image package provides a pair of interfaces that define operations on BufferedImage and Raster objects: BufferedImageOp and RasterOp.

The classes that implement these interfaces include AffineTransformOp, BandCombineOp, ColorConvertOp, ConvolveOp, LookupOp, RescaleOp. These classes can be used to geometrically transform, blur, sharpen, enhance contrast, threshold, and color correct images.

The following code illustrates edge detection:


```
float[] elements = { 0.0f, -1.0f, 0.0f, 
                    -1.0f, 4.f, -1.0f, 
                    0.0f, -1.0f, 0.0f}; 
... 
 
BufferedImage bimg = new BufferedImage(bw,bh,BufferedImage.TYPE_INT_RGB); 
Kernel kernel = new Kernel(3, 3, elements); 
ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP,                                             null); 
cop.filter(bi,bimg);
```

The following code demonstrates Lookup-table manipulation:


```
byte reverse[] = new byte[256]; 
   for (int j=0; j<200; j++){  
                reverse[j]=(byte)(256-j);  
        }        
        ByteLookupTable blut=new ByteLookupTable(0, reverse);  
        LookupOp lop = new LookupOp(blut, null);  
   lop.filter(bi,bimg);
```

The following code snippet illustrates rescaling:


```
RescaleOp rop = new RescaleOp(1.5f, 1.0f, null); 
        rop.filter(bi,bimg);
```

Using an Image Processing Operation

Convolution is the process that underlies most spatial filtering algorithms. Convolution is the process of weighting or averaging the value of each pixel in an image with the values of neighboring pixels.This allows each output pixel to be affected by the immediate neighborhood in a way that can be mathematically specified with a kernel. Figure 5-7 illustrates Convolution.

Blurring with Convolution

The following code fragment illustrates how to use one of the image processing classes, ConvolveOp. In this example, each pixel in the source image is averaged equally with the eight pixels that surround it.


```
float weight = 1.0f/9.0f;float[] elements = new float[9]; // create 2D array// fill the array with nine equal elements 
for (i = 0; i < 9; i++) {   elements[i] = weight;}// use the array of elements as argument to create a Kernelprivate Kernel myKernel = new Kernel(3, 3, elements);public ConvolveOp simpleBlur = new ConvolveOp(myKernel);
```
// sourceImage and destImage are instances of BufferedImagesimpleBlur.filter(sourceImage, destImage) // blur the image 

The variable simpleBlur contains a new instance of ConvolveOp that implements a blur operation on a BufferedImage or a Raster. Suppose that sourceImage and destImage are two instances of BufferedImage. When you call filter, the core method of the ConvolveOp class, it sets the value of each pixel in the destination image by averaging the corresponding pixel in the source image with the eight pixels that surround it.

When an image is convolved, the value of each pixel in the destination image is calculated by using the kernel as a set of weights to average the pixel’s value with the values of surrounding pixels. This operation is performed on each channel of the image.

The value of a destination pixel is the sum of the products of the weights in the kernel multiplied by the value of the corresponding source pixel. For many simple operations, the kernel is a matrix that is square and symmetric, and the sum of its weights adds up to one.2

The convolution kernel in this example is relatively simple. It weights each pixel from the source image equally. By choosing a kernel that weights the source image at a higher or lower level, a program can increase or decrease the intensity of the destination image. The Kernel object, which is set in the ConvolveOp constructor, determines the type of filtering that is performed. By setting other values, you can perform other types of convolutions, including blurring (such as Gaussian blur, radial blur, and motion blur), sharpening, and smoothing operations.

Sharpening with Convolution


```
The following code snippet illustrates sharpening with Convolution:

float[] elements = { 0.0f, -1.0f, 0.0f, 
                    -1.0f,  5.f, -1.0f, 
                     0.0f, -1.0f,  0.0f}; 
... 
 
Kernel kernel = new Kernel(3,3,elements); 
ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP,                                                                                                          null); 
        cop.filter(bi,bimg);
```

1 It is up to the programmer to “erase” the previous version of the image before making a new copy at a new location. This can be done by redrawing the background or copying the background from another offscreen buffer.
2 If the sum of the weights in the matrix is one, the intensity of the destination image is unchanged from the source.

The following program demonstrates various image operations like threshold, rescaleop, blur, sharpen, edge detect, invert etc.,


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

/**
 * The ImageOps class displays images drawn using operators such as ConvolveOp
 * LowPass & Sharpen, LookupOp and RescaleOp.
 */
public class ImageOps extends JPanel {

    Demo demo;

    public ImageOps() {
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        setLayout(new BorderLayout());
        add(demo = new Demo());
        add("North", new DemoControls(demo));
        add("West", demo.slider1);
        add("East", demo.slider2);
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        setPreferredSize(new Dimension(scrDim.width / 2 - 200, scrDim.height / 2 + 250));
    }

    /**
     * The Demo class renders the selected image with the selected Image
     * Operation filter.
     */
    static class Demo extends JPanel implements ChangeListener {

        private static String imgName[];
        private static BufferedImage img[];
        private static String opsName[] = {
            "Threshold", "RescaleOp", "Invert", "Yellow Invert", "3x3 Blur",
            "3x3 Sharpen", "3x3 Edge", "5x5 Edge"};
        private static BufferedImageOp biop[] = new BufferedImageOp[opsName.length];
        private static int rescaleFactor = 128;
        private static float rescaleOffset = 0;
        private static int low = 100, high = 200;
        private int opsIndex, imgIndex;
        private String path = "Images/";

        static {

            /*
             * initializes the threshold operation with the specified
             * low and high ranges.  Any pixel value below 100 is
             * assigned the value 0, while every value above 200 is
             * assigned the value 256.
             */
            thresholdOp(low, high);
            int i = 1;

            // multiplies pixel value by 1.0f and adds offset of 0.
            biop[i++] = new RescaleOp(1.0f, 0, null);
            byte invert[] = new byte[256];
            byte ordered[] = new byte[256];
            for (int j = 0; j < 256; j++) {
                invert[j] = (byte) (256 - j);
                ordered[j] = (byte) j;
            }

            // used to invert bytes
            biop[i++] = new LookupOp(new ByteLookupTable(0, invert), null);

            // used to invert the the bytes for the red and green components
            byte[][] yellowInvert = new byte[][]{invert, invert, ordered};
            biop[i++] = new LookupOp(new ByteLookupTable(0, yellowInvert), null);

            int dim[][] = {{3, 3}, {3, 3}, {3, 3}, {5, 5}};

            // holds the kernels used with ConvolveOp
            float data[][] = {{0.1f, 0.1f, 0.1f, // 3x3 blur
                0.1f, 0.2f, 0.1f,
                0.1f, 0.1f, 0.1f},
            {-1.0f, -1.0f, -1.0f, // 3x3 sharpen
                -1.0f, 9.0f, -1.0f,
                -1.0f, -1.0f, -1.0f},
            {0.f, -1.f, 0.f, // 3x3 edge
                -1.f, 5.f, -1.f,
                0.f, -1.f, 0.f},
            {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, // 5x5 edge
                -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
                -1.0f, -1.0f, 24.0f, -1.0f, -1.0f,
                -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
                -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}};
            for (int j = 0; j < data.length; j++, i++) {
                biop[i] = new ConvolveOp(new Kernel(dim[j][0], dim[j][1], data[j]));
            }
        }
        private BufferedImage bimg;
        protected JSlider slider1, slider2;

        public Demo() {
            setBackground(Color.white);
            //Filter out the Images
            imgName = new File(path).list(new FilenameFilter() {
                String[] readFormat = ImageIO.getReaderFormatNames();

                @Override
                public boolean accept(File dir, String name) {
                    for (int i = 0; i < readFormat.length; i++) {
                        if (name.endsWith(readFormat[i])) {
                            return true;
                        }
                    }
                    return false;
                }
            });
            img = new BufferedImage[imgName.length];
            for (int i = 0; i < imgName.length; i++) {
                Image image = getImage(path + imgName[i]);
                int iw = image.getWidth(this);
                int ih = image.getHeight(this);
                img[i] = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);
                img[i].createGraphics().drawImage(image, 0, 0, null);
            }
            slider1 = new JSlider(JSlider.VERTICAL, 0, 255, low);
            slider1.setPreferredSize(new Dimension(15, 100));
            slider1.addChangeListener(this);
            slider2 = new JSlider(JSlider.VERTICAL, 0, 255, high);
            slider2.setPreferredSize(new Dimension(15, 100));
            slider2.addChangeListener(this);
        }

        public Image getImage(String fileName) {
            try {
                return ImageIO.read(new File(fileName));
            } catch (IOException ioe) {
                System.err.println("Error loading Image :" + fileName + "!!");
            }
            return null;
        }
        /*
         * specifies a low value, and high value to control the color
         * component values for each pixel of an image.  Puts the color
         * values into a single byte array.  The single byte array is more
         * suited for gray scale images, [][] byte array would be more
         * appropriate for RGB images.
         */

        public static void thresholdOp(int low, int high) {
            byte threshold[] = new byte[256];
            for (int j = 0; j < 256; j++) {
                if (j > high) {
                    threshold[j] = (byte) 255;
                } else if (j < low) {
                    threshold[j] = (byte) 0;
                } else {
                    threshold[j] = (byte) j;
                }
            }
            biop[0] = new LookupOp(new ByteLookupTable(0, threshold), null);
        }

        // filters the image with the selected filter and draws it
        public void drawDemo(int w, int h, Graphics2D g2) {
            int iw = img[imgIndex].getWidth(null);
            int ih = img[imgIndex].getHeight(null);
            BufferedImage bimg = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);
            biop[opsIndex].filter(img[imgIndex], bimg);
            g2.drawImage(bimg, 0, 0, w, h, null);
        }

        public Graphics2D createGraphics2D(int w, int h) {
            Graphics2D g2 = null;
            if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
                bimg = (BufferedImage) createImage(w, h);
            }
            g2 = bimg.createGraphics();
            g2.setBackground(getBackground());
            g2.clearRect(0, 0, w, h);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            return g2;
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Dimension d = getSize();
            Graphics2D g2 = createGraphics2D(d.width, d.height);
            drawDemo(d.width, d.height, g2);
            g2.dispose();
            g.drawImage(bimg, 0, 0, this);
        }

        @Override
        public void stateChanged(ChangeEvent e) {
            if (e.getSource().equals(slider1)) {
                if (opsIndex == 0) {
                    thresholdOp(slider1.getValue(), high);
                } else {
                    rescaleFactor = slider1.getValue();
                    biop[1] = new RescaleOp((float) rescaleFactor / 128.0f, rescaleOffset, null);
                }
            } else {
                if (opsIndex == 0) {
                    thresholdOp(low, slider2.getValue());
                } else {
                    rescaleOffset = (float) slider2.getValue();
                    biop[1] = new RescaleOp((float) rescaleFactor / 128.0f, rescaleOffset, null);
                }

            }
            repaint();
        }
    } // End Demo class

    /**
     * The DemoControls class provides controls for selecting the imaging
     * operation, the image to be filtered and the ranges to be used in the
     * filtering operation. When rescaling is selected, slider1 controls the
     * rescale factor and slider2 controls the rescale offset. When thresholding
     * is selected, slider1 controls the low range and slider2 controls the high
     * range of the pixel byte value. Since the demo only offers two sliders
     * that control a single byte array of color components, thresholding the
     * gray scale boat.gif image is more practical. To better demonstrate the
     * use of thresholding with RGB images, six sliders, representing the low
     * and the high range for each color component, would be needed. If an
     * imaging operation besides rescaling or thresholding is selected, both
     * sliders are disabled.
     */
    static class DemoControls extends JPanel implements ActionListener, Runnable {

        Demo demo;
        JComboBox<String> imgCombo;
        JComboBox<String> opsCombo;
        Font font = new Font("serif", Font.PLAIN, 10);
        Thread thread;

        public DemoControls(Demo demo) {
            this.demo = demo;
            setBackground(Color.gray);
            add(imgCombo = new JComboBox<>());
            imgCombo.setFont(font);
            for (int i = 0; i < Demo.imgName.length; i++) {
                imgCombo.addItem(Demo.imgName[i]);
            }
            imgCombo.addActionListener(this);
            add(opsCombo = new JComboBox<>());
            opsCombo.setFont(font);
            for (int i = 0; i < Demo.opsName.length; i++) {
                opsCombo.addItem(Demo.opsName[i]);
            }
            opsCombo.addActionListener(this);
            setToolTipText("click to run thread");
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (thread == null) {
                        start();
                    } else {
                        stop();
                    }
                }
            });
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource().equals(opsCombo)) {
                demo.opsIndex = opsCombo.getSelectedIndex();
                if (demo.opsIndex == 0) {
                    demo.slider1.setValue(demo.low);
                    demo.slider2.setValue(demo.high);
                    demo.slider1.setEnabled(true);
                    demo.slider2.setEnabled(true);
                } else if (demo.opsIndex == 1) {
                    demo.slider1.setValue(demo.rescaleFactor);
                    demo.slider2.setValue((int) demo.rescaleOffset);
                    demo.slider1.setEnabled(true);
                    demo.slider2.setEnabled(true);
                } else {
                    demo.slider1.setEnabled(false);
                    demo.slider2.setEnabled(false);
                }
            } else if (e.getSource().equals(imgCombo)) {
                demo.imgIndex = imgCombo.getSelectedIndex();
            }
            demo.repaint();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 32);
        }

        public void start() {
            if (thread != null) {
                return;
            }
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.start();
        }

        public synchronized void stop() {
            if (thread != null) {
                thread.interrupt();
            }
            thread = null;
            notifyAll();
        }

        @Override
        public void run() {
            Thread me = Thread.currentThread();
            while (thread == me) {
                for (int i = 0; i < Demo.imgName.length; i++) {
                    imgCombo.setSelectedIndex(i);
                    for (int j = 0; j < Demo.opsName.length; j++) {
                        opsCombo.setSelectedIndex(j);
                        if (j <= 1) {
                            for (int k = 50; k <= 200; k += 10) {
                                demo.slider1.setValue(k);
                                try {
                                    thread.sleep(200);
                                } catch (InterruptedException e) {
                                    return;
                                }
                            }
                        }
                        try {
                            thread.sleep(4444);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                }
            }
            thread = null;
        }
    } // End DemoControls class

    public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("ImageOps");
                f.add(new ImageOps());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s14.postimg.org/wajdruzyl/Image_Ops.jpg

*Printing*

The Java Printing API enables applications to:

    Print all AWT and Java 2D™ graphics, including composited graphics and images.

    Control document-composition functions such as soft collating, reverse order printing, and booklet printing.

    Invoke printer-specific functions such as duplex (two-sided) printing and stapling.

    Print on all platforms, including Windows and Solaris. This includes printers directly attached to the computer as well as those that the platform software is able to access using network printing protocols.

Not all of these features are supported in the Java™ 2 SDK Printing API and implementation. The API will be extended to support all of these features in future releases. For example, additional printer controls will be added by augmenting the set of named properties of a print job that the application can control.

 Interfaces and Classes

Interface           Description

Printable        The Printable interface is implemented by each page painter, the application class(es) called by the printing system to render a page. The system calls the page painter’s print method to request that a page be rendered.

Pageable         The Pageable interface is implemented by a document that is to be printed by the printing system. Through the Pageable methods, the system can determine the number of pages in the document, the format to use for each page, and the page painter to use to render each page.

PrinterGraphics   The Graphics2D objects that a page painter uses to render a page implement the PrinterGraphics interface. This enables an application to get the PrinterJob object that is controlling the printing.

Class              Description

Book             Implements: Pageable Represents a document in which pages can have different page formats and page painters. This class uses the Pageable interface to interact with a PrinterJob.

PageFormat       Describes the size and orientation of a page to be printed, as well as the Paper used to print it. For example, portrait and landscape paper orientations are represented by PageFormat.

Paper            Describes the physical characteristics of a piece of paper.

PrinterJob       The principal class that controls printing. The application calls PrinterJob methods to set up a job, display a print dialog to the user (optional), and to print the pages in the job.

*Printing Concepts*

The Java Printing API is based on a callback printing model in which the printing system, not the application, controls when pages are printed. The application provides information about the document to be printed and the printing system asks the application to render each page as it needs them.

The printing system might request that a particular page be rendered more than once or request that pages be rendered out of order. The application must be able to generate the proper page image, no matter which page the printing system requests. In this respect, the printing system is similar to the window toolkit, which can request components to repaint at any time, in any order.

The callback printing model is more flexible than traditional application-driven printing models and supports printing on a wider range of systems and printers. For example, if a printer stacks output pages in reverse order, the printing system can ask the application to generate pages in reverse order so that the final stack is in proper reading order.

This model also enables applications to print to a bitmap printer from computers that don’t have enough memory or disk space to buffer a full-page bitmap. In this situation, a page is printed as a series of small bitmaps or bands. For example, if only enough memory to buffer one tenth of a page is available, the page is divided into ten bands. The printing system asks the application to render each page ten times, once to fill each band. The application does not need to be aware of the number or size of the bands; it simply must be able to render each page when requested.
7.2.1 Supporting Printing

An application has to perform two tasks to support printing:

    Job control—initiating and managing the print job.

    Imaging—rendering each page when the printing system requests it.

Job Control

The user often initiates printing by clicking a button or selecting a menu item in an application. When a print operation is triggered by the user, the application creates a PrinterJob object and uses it to manage the printing process.

The application is responsible for setting up the print job, displaying print dialogs to the user, and starting the printing process.
7.2.1.2 Imaging

When a document is printed, the application has to render each page when the printing system requests it. To support this mechanism, the application provides a page painter that implements the Printable interface. When the printing system needs a page rendered, it calls the page painter’s print method.

When a page painter’s print method is called, it is passed a Graphics context to use to render the page image. It is also passed a PageFormat object that specifies the geometric layout of the page, and an integer page index that identifies the ordinal position of the page in the print job.

The printing system supports both Graphics and Graphics2D rendering, To print Java 2D™ Shapes, Text, and Images, you cast the Graphics object passed into the print method to a Graphics2D.

To print documents in which the pages use different page painters and have different formats, you use a pageable job. To create a pageable job, you can use the Book class or your own implementation of the Pageable interface. To implement simple printing operations, you do not need to use a pageable print job; Printable can be used as long as all of the pages share the same page format and painter.
7.2.2 Page Painters

The principal job of a page painter is to render a page using the graphics context that is provided by the printing system. A page painter implements the Printable.print method:

public int print(Graphics g, PageFormat pf, int pageIndex)  

The graphics context passed to the print method is either an instance of Graphics or Graphics2D, depending on the packages loaded in your Java Virtual Machine. To use Graphics2D features, you can cast the Graphics object to a Graphics2D. The Graphics instance passed to print also implements the PrinterGraphics interface.

The PageFormat passed to a Printable describes the geometry of the page being printed. The coordinate system of the graphics context passed to print is fixed to the page: the origin of the coordinate system is at the upper left corner of the paper, X increases to the right, Y increases downward, and the units are 1/72 inch. If the page is in portrait orientation, the x-axis aligns with the paper’s “width,” while the y-axis aligns with the paper’s “height.” (Normally, but not always, a paper’s height exceeds its width.) If the page is in landscape orientation, the roles are reversed: the x-axis aligns with the paper’s “height” and the y-axis with its “width.”

Because many printers cannot print on the entire paper surface, the PageFormat specifies the imageable area of the page: this is the portion of the page in which it’s safe to render. The specification of the imageable area does not alter the coordinate system; it is provided so that the contents of the page can be rendered so that they don’t extend into the area where the printer can’t print.

The graphics context passed to print has a clip region that describes the portion of the imageable area that should be drawn. It is always safe to draw the entire page into the context; the printing system will handle the necessary clipping. However, to eliminate the overhead of drawing portions of the page that won’t be printed, you can use the clipping region to limit the areas that you render. To get the clipping region from the graphics context, call Graphics.getClip. You are strongly encouraged to use the clip region to reduce the rendering overhead.

It is sometimes desirable to launch the entire printing operation “in the background” so that a user can continue to interact with the application while pages are being rendered. To do this, call PrinterJob.print in a separate thread.

If possible, you should avoid graphics operations that require knowledge of the previous image contents, such as copyArea, setXOR, and compositing. These operations can slow rendering and the results might be inconsistent.
7.2.3 Printable Jobs and Pageable Jobs

A Printable job provides the simplest way to print. Only one page painter is used; the application provides a single class that implements the Printable interface. When it’s time to print, the printing system calls the page painter’s print method to render each page. The pages are requested in order, starting with page index 0. However, the page painter might be asked to render each page several times before it advances to the next page. When the last page has been printed, the page painter’s print method returns NO_SUCH_PAGE.

In a Printable job:

    All pages use the same page painter and PageFormat. If a print dialog is presented, it will not display the number of pages in the document because that information is not available to the printing system.

    The printing system always asks the page painter to print each page in indexed order, starting with the page at index 0. No pages are skipped. For example, if a user asks to print pages 2 and 3 of a document, the page painter will be called with indices 0, 1, and 2. The printing system might request that a page be rendered multiple times before moving to the next page.

    The page painter informs the printing system when the end of the document has been reached.

    All page painters are called in the same thread.

    Some printing systems might not be able to achieve the ideal output. For example, the stack of pages emerging from the printer might be in the wrong order, or the pages might not be collated if multiple copies are requested.

A Pageable job is more flexible than a Printable job. Unlike the pages in a Printable job, pages in a Pageable job can differ in layout and implementation. To manage a Pageable job, you can use the Book class or implement your own Pageable class. Through the Pageable, the printing system can determine the number of pages to print, the page painter to use for each page, and the PageFormat to use for each page. Applications that need to print documents that have a planned structure and format should use Pageable jobs.

In a Pageable job:

    Different pages can use different page painters and PageFormats.

    The printing system can ask page painters to print pages in an arbitrary order and some pages might be skipped. For example, if a user asks to print pages 2 and 3 of a document, the page painter will be called with indices 1 and 2 and page index 0 will be skipped.

    Pageable jobs do not need to know in advance how many pages are in the document. However, unlike Printable jobs, they must be able to render pages in any order. There might be gaps in the sequencing and the printing system might request that a page be rendered multiple times before moving to the next page. For example, a request to print pages 2 and 3 of a document might result in a sequence of calls that request pages with indices 2,2,1,1, and 1.

7.2.4 Typical Life-Cycle of a PrinterJob

An application steers the PrinterJob object through a sequence of steps to complete a printing job. The simplest sequence used by an application is:

    Get a new PrinterJob object by calling PrinterJob.getPrinterJob.

    Determine what PageFormat to use for printing. A default PageFormat can be obtained by calling defaultPage or you can invoke pageDialog to present a dialog box that allows the user to specify a format.

    Specify the characteristics of the job to be printed to the PrinterJob. For a Printable job, call setPrintable; for a Pageable job, call setPageable. Note that a Book object is ideal for passing to setPageable.

    Specify additional print job properties, such as the number of copies to print or the name of the job to print on the banner page.

    Call printDialog to present a dialog box to the user. This is optional. The contents and appearance of this dialog can vary across different platforms and printers. On most platforms, the user can use this dialog to change the printer selection. If the user cancels the print job, the printDialog method returns FALSE.

    Call Printerjob.print to print the job. This method in turn calls print on the appropriate page painters.

A job can be interrupted during printing if:

    A PrinterException is thrown—the exception is caught by the print method and the job is halted. A page painter throws a PrinterException if it detects a fatal error.

    PrinterJob.cancel is called—the printing loop is terminated and the job is canceled. The cancel method can be called from a separate thread that displays a dialog box and allows the user to cancel printing by clicking a button in the box.

Pages generated before a print job is stopped might or might not be printed.

The print job is usually not finished when the print method returns. Work is typically still being done by a printer driver, print server, or the printer itself. The state of the PrinterJob object might not reflect the state of the actual job being printed.

Because the state of a PrinterJob changes during its life cycle, it is illegal to invoke certain methods at certain times. For example, calling setPageable after you’ve called print makes no sense. When illegal calls are detected, the PrinterJob throws a java.lang.IllegalStateException.
7.2.5 Dialogs

The Java Printing API requires that applications invoke user-interface dialogs explicitly. These dialogs might be provided by the platform software (such as Windows) or by a Java™ 2 SDK software implementation. For interactive applications, it is customary to use such dialogs. For production printing applications, however, dialogs are not necessary. For example, you wouldn’t want to display a dialog when automatically generating and printing a nightly database report. 
A print job that requires no user interaction is sometimes called a silent print job.
7.2.5.1 Page setup dialog

You can allow the user to alter the page setup information contained in a PageFormat by displaying a page setup dialog. To display the page setup dialog, you call PrinterJob.pageDialog. The page setup dialog is initialized using the parameter passed to pageDialog. If the user clicks the OK button in the dialog, the PageFormat instance is cloned, altered to reflect the user’s selections, and then returned. If the user cancels the dialog, pageDialog returns the original unaltered PageFormat.
7.2.5.2 Print dialog

Typically, an application presents a print dialog to the user when a print menu item or button is activated. To display this print dialog, you call the PrinterJob’s printDialog method. The user’s choices in the dialog are constrained based on the number and format of the pages in the Printable or Pageable that have been furnished to the PrinterJob. If the user clicks OK in the print dialog, printDialog returns TRUE. If the user cancels the print dialog, FALSE is returned and the print job should be considered abandoned.
7.3 Printing with Printables

To provide basic printing support:

    Implement the Printable interface to provide a page painter that can render each page to be printed.

    Create a PrinterJob.

    Call setPrintable to tell the PrinterJob how to print your document.

    Call print on the PrinterJob object to start the job.

In the following example, a Printable job is used to print five pages, each of which displays a green page number. Job control is managed in the main method, which obtains and controls the PrinterJob. Rendering is performed in the page painter’s print method.


```
import java.awt.*; 
import java.awt.print.*; 

public class SimplePrint implements Printable  
{    
  private static Font fnt = new Font("Helvetica",Font.PLAIN,24); 
   
  public static void main(String[] args)  
  {      
    // Get a PrinterJob      
    PrinterJob job = PrinterJob.getPrinterJob();      
    // Specify the Printable is an instance of SimplePrint 
    job.setPrintable(new SimplePrint());      
    // Put up the dialog box      
    if (job.printDialog())  
    {    
      // Print the job if the user didn't cancel printing  
      try { job.print(); } 
      catch (Exception e) 
        { /* handle exception */ } 
    }      
    System.exit(0);    
  } 
 
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  {      
    // pageIndex 0 to 4 corresponds to page numbers 1 to 5. 
    if (pageIndex >= 5) return Printable.NO_SUCH_PAGE;    
    g.setFont(fnt);      
    g.setColor(Color.green);      
    g.drawString("Page " + (pageIndex+1), 100, 100);      
    return Printable.PAGE_EXISTS;    
  }  
}
```

*Using Graphics2D for Rendering*

You can invoke Graphics2D functions in you page painter’s print method by first casting the Graphics context to a Graphics2D.

In the following example, the page numbers are rendered using a red-green gradient. To do this, a GradientPaint is set in the Graphics2D context.


```
import java.awt.*;
import java.awt.print.*; 

public class SimplePrint2D implements Printable  
{    
  private static Font fnt = new Font("Helvetica",Font.PLAIN,24); 
   
  private Paint pnt = new GradientPaint(100f, 100f, Color.red,                                            136f, 100f, Color.green, true); 
   
  public static void main(String[] args)  
  {      
    // Get a PrinterJob      
    PrinterJob job = PrinterJob.getPrinterJob();      
    // Specify the Printable is an instance of SimplePrint2D 
    job.setPrintable(new SimplePrint2D());      
    // Put up the dialog box      
    if (job.printDialog())  
    {    
      // Print the job if the user didn't cancel printing  
      try { job.print(); }           
      catch (Exception e) { /* handle exception */ }      
    }      
  System.exit(0);    
  } 
 
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  {      
    // pageIndex 0 to 4 corresponds to page numbers 1 to 5. 
    if (pageIndex >= 5) return Printable.NO_SUCH_PAGE; 
    Graphics2D g2 = (Graphics2D) g; 
    // Use the font defined above 
    g2.setFont(fnt); 
    // Use the gradient color defined above 
    g2.setPaint(pnt); 
    g2.drawString("Page " + (pageIndex+1), 100f, 100f); 
    return Printable.PAGE_EXISTS;    
  }  
}
```

*Printing a File*

When a page painter’s print method is invoked several times for the same page, it must generate the same output each time.

There are many ways to ensure that repeated requests to render a page yield the same output. For example, to ensure that the same output is generated each time the printing system requests a particular page of a text file, page painter could either store and reuse file pointers for each page or store the actual page data.

In the following example, a “listing” of a text file is printed. The name of the file is passed as an argument to the main method. The PrintListingPainter class stores the file pointer in effect at the beginning of each new page it is asked to render. When the same page is rendered again, the file pointer is reset to the remembered position.


```
import java.awt.*;  
import java.awt.print.*;  
import java.io.*; 
 
public class PrintListing  
{    
  public static void main(String[] args)  
  {      
    // Get a PrinterJob 
    PrinterJob job = PrinterJob.getPrinterJob(); 
    // Ask user for page format (e.g., portrait/landscape) 
    PageFormat pf = job.pageDialog(job.defaultPage()); 
    // Specify the Printable is an instance of 
    // PrintListingPainter; also provide given PageFormat 
    job.setPrintable(new PrintListingPainter(args[0]), pf); 
    // Print 1 copy    
    job.setCopies(1);      
    // Put up the dialog box      
    if (job.printDialog())  
    { 
      // Print the job if the user didn't cancel printing 
      try { job.print(); } 
      catch (Exception e) { /* handle exception */ }      
    }      
    System.exit(0);    
  }  
} 
 
class PrintListingPainter implements Printable  
{ 
  private RandomAccessFile raf;    
  private String fileName;    
  private Font fnt = new Font("Helvetica", Font.PLAIN, 10); 
  private int rememberedPageIndex = -1;    
  private long rememberedFilePointer = -1;    
  private boolean rememberedEOF = false; 
   
  public PrintListingPainter(String file)  
  {  
    fileName = file;      
    try 
    {  
      // Open file       
      raf = new RandomAccessFile(file, "r");      
    }  
    catch (Exception e) { rememberedEOF = true; }    
  } 
 
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  { 
  try  
  {  
    // For catching IOException      
    if (pageIndex != rememberedPageIndex)  
    {  
      // First time we've visited this page 
      rememberedPageIndex = pageIndex;   
      // If encountered EOF on previous page, done  
      if (rememberedEOF) return Printable.NO_SUCH_PAGE; 
      // Save current position in input file 
      rememberedFilePointer = raf.getFilePointer(); 
    }  
    else raf.seek(rememberedFilePointer); 
    g.setColor(Color.black);      
    g.setFont(fnt);  
        int x = (int) pf.getImageableX() + 10; 
        int y = (int) pf.getImageableY() + 12;     
    // Title line      
    g.drawString("File: " + fileName + ", page: " +                                          (pageIndex+1),  x, y); 
    // Generate as many lines as will fit in imageable area 
    y += 36; 
    while (y + 12 < pf.getImageableY()+pf.getImageableHeight()) 
    { 
      String line = raf.readLine(); 
      if (line == null) 
      {  
        rememberedEOF = true; 
        break;  
                } 
        g.drawString(line, x, y);  
        y += 12;      
      } 
      return Printable.PAGE_EXISTS;     
    }  
    catch (Exception e) { return Printable.NO_SUCH_PAGE;} 
  }  
}
```

*Printing with Pageables and Books*

Pageable jobs are suited for applications that build an explicit representation of a document, page by page. The Book class is a convenient way to use Pageables, but you can also build your own Pageable structures if Book does not suit your needs. This section shows you how to use Book.

Although slightly more involved, Pageable jobs are preferred over Printable jobs because the printing system has more flexibility. A major advantage of Pageables is that the number of pages in the document is usually known and can be displayed to the user in the print dialog box. This helps the user to confirm that the job is specified correctly or to select a range of pages for printing.

A Book represents a collection of pages. The pages in a book do not have to share the same size, orientation, or page painter. For example, a Book might contain two letter size pages in portrait orientation and a letter size page in landscape orientation.

When a Book is first constructed, it is empty. To add pages to a Book, you use the append method. This method takes a PageFormat object that defines the page’s size, printable area, and orientation and a page painter that implements the Printable interface.

Multiple pages in a Book can share the same page format and painter. The append method is overloaded to enable you to add a series of pages that have the same attributes by specifying a third parameter, the number of pages.

If you don’t know the total number of pages in a Book, you can pass UNKNOWN_NUMBER_OF_PAGES to the append method. The printing system will then call your page painters in order of increasing page index until one of them returns NO_SUCH_PAGE.

The setPage method can be used to change a page’s page format or painter. The page to be changed is identified by a page index that indicates the page’s location in the Book.

You call setPageable and pass in the Book to prepare the print job. The setPageable and setPrintable methods are mutually exclusive; that is, you should call one or the other but not both when preparing the PrinterJob.

*Using a Pageable Job*

In the following example, a Book is used to reproduce the first simple printing example. (Because this case is so simple, there is little benefit in using a Pageable job instead of a Printable job, but it illustrates the basics of using a Book.) Note that you still have to implement the Printable interface and perform page rendering in the page painter’s print method.


```
import java.awt.*;  
import java.awt.print.*; 
 
public class SimplePrintBook implements Printable  
{    
  private static Font fnt = new Font("Helvetica",Font.PLAIN,24); 
  public static void main(String[] args)  
  {      
    // Get a PrinterJob      
    PrinterJob job = PrinterJob.getPrinterJob();      
    // Set up a book      
    Book bk = new Book();      
    bk.append(new SimplePrintBook(), job.defaultPage(), 5);      
    // Pass the book to the PrinterJob      
    job.setPageable(bk);      
    // Put up the dialog box      
    if (job.printDialog())  
    { 
      // Print the job if the user didn't cancel printing  
      try { job.print(); }           
      catch (Exception e) { /* handle exception */ }      
    }      
    System.exit(0);    
  } 
 
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  {      
    g.setFont(fnt);      
    g.setColor(Color.green);      
    g.drawString("Page " + (pageIndex+1), 100, 100);      
    return Printable.PAGE_EXISTS;    
  } 
}
```

*Using Multiple Page Painters*

In the following example, two different page painters are used: one for a cover page and one for content pages. The cover page is printed in landscape mode and the contents pages are printed in portrait mode.


```
import java.awt.*;  
import java.awt.print.*; 
 
public class PrintBook  
{ 
  public static void main(String[] args)  
  {      
    // Get a PrinterJob      
    PrinterJob job = PrinterJob.getPrinterJob();      
    // Create a landscape page format     
    PageFormat pfl = job.defaultPage();   
    pfl.setOrientation(PageFormat.LANDSCAPE);      
    // Set up a book      
    Book bk = new Book();      
    bk.append(new PaintCover(), pfl);      
    bk.append(new PaintContent(), job.defaultPage(), 2);      
    // Pass the book to the PrinterJob      
    job.setPageable(bk);      
    // Put up the dialog box      
    if (job.printDialog())  
    {  
      // Print the job if the user didn't cancel printing 
      try { job.print(); }  
      catch (Exception e) { /* handle exception */ }      
    }      
  System.exit(0);    
  }  
} 
 
class PaintCover implements Printable  
{    
  Font fnt = new Font("Helvetica-Bold", Font.PLAIN, 72); 
  
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  {      
    g.setFont(fnt);      
    g.setColor(Color.black);      
         int yc = (int) (pf.getImageableY() +  pf.getImageableHeight()/2); 
    g.drawString("Widgets, Inc.", 72, yc+36);      
    return Printable.PAGE_EXISTS;    
  }  
} 
class PaintContent implements Printable  
{    
  public int print(Graphics g, PageFormat pf, int pageIndex) 
  throws PrinterException  
  {     
    Graphics2D g2 = (Graphics2D) g;      
    int useRed = 0;      
   int xo = (int) pf.getImageableX(); 
        int yo = (int) pf.getImageableY();  
    // Fill page with circles or squares, alternating red & green 
        for (int x = 0; x+28 < pf.getImageableWidth(); x += 36) 
    for (int y = 0; y+28 < pf.getImageableHeight(); y += 36) 
    {  
      if (useRed == 0) g.setColor(Color.red); 
      else g.setColor(Color.green); 
      useRed = 1 - useRed; 
      if (pageIndex % 2 == 0) g.drawRect(xo+x+4, yo+y+4, 28, 28); 
      else g.drawOval(xo+x+4, yo+y+4, 28, 28); 
    }      
    return   Printable.PAGE_EXISTS;    
  }  
}
```

This wraps up the Java2D graphics guide!!


----------



## archananair (Mar 13, 2015)

That's being a great guide, i am beginner to java learner and this was really informative and knowledge giving post. Thanks for sharing.


----------

