Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures


Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips


Submit News
Search the site here...

Swing Chapter 23. (Special topics) Java2D. Easy for reading, Click here!

Custom Search
Swing Chapter 23. (Special topics) Java2D. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 3/4 

Previous Page Previous Page (2/4) - Next Page (4/4) Next Page
Subpages: 1. Java2D API overview
2. Rendering charts
3. Rendering text strings
4. Rendering images

23.3  Rendering strings

In this section we'll demonstrate advantages of using Java2D for rendering strings. This is especially useful  for relatively big fonts used to display titles. The following example introduces a custom label component which is capable of rendering strings with various visual effects, including such things as animation using an image, continuously changing foreground color, and outlining.

Figure 23.3 JLabel2Ds with various visual effects, and a plain JLabel for comparison.


The Code: Labels2D.java

see \Chapter23\2

import java.awt.*;

import java.awt.event.*;

import java.awt.font.*;

import java.awt.geom.*;

import java.util.*;

import javax.swing.*;

import javax.swing.border.*;

public class Labels2D extends JFrame


  public Labels2D() {

    super("2D Labels");

    setSize(600, 250);

    getContentPane().setLayout(new GridLayout(6, 1, 5, 5));


    Font bigFont = new Font("Helvetica",Font.BOLD, 24);

    JLabel2D lbl = new JLabel2D("Simple JLabel2D With Outline",




    lbl.setBorder(new LineBorder(Color.black));



    lbl.setStroke(new BasicStroke(5f));




    lbl = new JLabel2D("JLabel2D With Color Gradient",





    GradientPaint gp = new GradientPaint(0, 0,

      Color.red, 100, 50, Color.blue, true);



    lbl = new JLabel2D(

      "JLabel2D Filled With Image", JLabel.CENTER);



    ImageIcon icon = new ImageIcon("mars.gif");




    lbl = new JLabel2D("JLabel2D With Image Animation",




    icon = new ImageIcon("ocean.gif");





    lbl = new JLabel2D("JLabel2D With Color Animation",








    JLabel lbl1 = new JLabel("Plain JLabel For Comparison",





    WindowListener wndCloser = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {







  public static void main(String argv[]) { new Labels2D(); }


class JLabel2D extends JLabel


  public static final int EFFECT_PLAIN = 0;

  public static final int EFFECT_GRADIENT = 1;

  public static final int EFFECT_IMAGE = 2;

  public static final int EFFECT_IMAGE_ANIMATION = 3;

  public static final int EFFECT_COLOR_ANIMATION = 4;

  protected int m_effectIndex = EFFECT_PLAIN;

  protected double m_shearFactor = 0.0;

  protected Color  m_outlineColor;

  protected Stroke m_stroke;

  protected GradientPaint m_gradient;

  protected Image  m_foregroundImage;

  protected Thread m_animator;

  protected boolean m_isRunning = false;

  protected int m_delay;

  protected int m_xShift;

  public JLabel2D() { super(); }

  public JLabel2D(String text) { super(text); }

  public JLabel2D(String text, int alignment) {

    super(text, alignment);


  public void setEffectIndex(int effectIndex) {

    m_effectIndex = effectIndex;



  public int getEffectIndex() { return m_effectIndex; }

  public void setShearFactor(double shearFactor) {

    m_shearFactor = shearFactor;



  public double getShearFactor() { return m_shearFactor; }

  public void setOutlineColor(Color outline) {

    m_outlineColor = outline;



  public Color getOutlineColor() { return m_outlineColor; }

  public void setStroke(Stroke stroke) {

    m_stroke = stroke;



  public Stroke getStroke() { return m_stroke; }

  public void setGradient(GradientPaint gradient) {

    m_gradient = gradient;



  public GradientPaint getGradient() { return m_gradient; }

  public void setForegroundImage(Image img) { 

    m_foregroundImage = img;



  public Image getForegroundImage() { return m_foregroundImage; }

  public void startAnimation(int delay) {

    if (m_animator != null)


    m_delay = delay;

    m_xShift = 0;

    m_isRunning = true;

    m_animator = new Thread() {

      double arg = 0;

      public void run() {

        while(m_isRunning) {

          if (m_effectIndex==EFFECT_IMAGE_ANIMATION)

            m_xShift += 10;

          else if (

           m_effectIndex==EFFECT_COLOR_ANIMATION &&

           m_gradient != null) {

            arg += Math.PI/10;

            double cos = Math.cos(arg);

            double f1 = (1+cos)/2;

            double f2 = (1-cos)/2;

            arg = arg % (Math.PI*2);

            Color c1 = m_gradient.getColor1();

            Color c2 = m_gradient.getColor2();

            int r = (int)(c1.getRed()*f1+c2.getRed()*f2);

            r = Math.min(Math.max(r, 0), 255);

            int g = (int)(c1.getGreen()*f1+c2.getGreen()*f2);

            g = Math.min(Math.max(g, 0), 255);

            int b = (int)(c1.getBlue()*f1+c2.getBlue()*f2);

            b = Math.min(Math.max(b, 0), 255);

            setForeground(new Color(r, g, b));



          try { sleep(m_delay); }

          catch (InterruptedException ex) { break; }






  public void stopAnimation() {

    m_isRunning = false;

    m_animator = null;


  public void paintComponent(Graphics g) {

    Dimension d= getSize();

    Insets ins = getInsets();

    int x = ins.left;

    int y = ins.top;

    int w = d.width-ins.left-ins.right;

    int h = d.height-ins.top-ins.bottom;

    if (isOpaque()) {


      g.fillRect(0, 0, d.width, d.height);



    Graphics2D g2 = (Graphics2D) g;





    FontRenderContext frc = g2.getFontRenderContext();

    TextLayout tl = new TextLayout(getText(), getFont(), frc);

    AffineTransform shear = AffineTransform.

      getShearInstance(m_shearFactor, 0.0);

    Shape src = tl.getOutline(shear);

    Rectangle rText = src.getBounds();

    float xText = x - rText.x;

    switch (getHorizontalAlignment()) {

    case CENTER:

      xText = x + (w-rText.width)/2;


    case RIGHT:

      xText = x + (w-rText.width);



    float yText = y + h/2 + tl.getAscent()/4;

    AffineTransform shift = AffineTransform.

      getTranslateInstance(xText, yText);

    Shape shp = shift.createTransformedShape(src);

    if (m_outlineColor != null) {


      if (m_stroke != null)




    switch (m_effectIndex) {


        if (m_gradient == null)





      case EFFECT_IMAGE:

        fillByImage(g2, shp, 0);







        if (m_foregroundImage == null)


        int wImg = m_foregroundImage.getWidth(this);

        if (m_xShift > wImg)

          m_xShift = 0;

        fillByImage(g2, shp, m_xShift-wImg);








  // Method fillByImage taken from JChart2D (section 23.2)


Understanding the Code

Class Labels2D

This class extends JFrame and provides the main container for our example. Its constructor creates five instances of our JLabel2D custom component, and one JLabel used for comparison. All labels are placed in a GridLayout containing one column, and each uses the same font. Various settings are used to render these custom labels, as defined in our JLabel2D class:

The first label uses border and background settings to demonstrate that they can be used the same way as with any other Swing components. (Note that we still have to set the opaque property to true for the background to be filled.) The outlineColor and stroke properties are set to outline the label's text. Finally the shearFactor property is set to make the text lean to the left.

The second label uses a GradientPaint instance (using red and blue colors) to fill the text's interior.

The third label uses an image to fill the text's interior.

The fourth label uses an image to fill the text's interior with animation of that image (cyclical shifting of the image in the horizontal direction).

The fifth label uses the same GradientPaint as the second one, but to produce an effect of color animation (a solid foreground color which is changed cyclically).

The sixth label is a plain JLabel component without any special effects.

Class JLabel2D

This class extends JLabel and provides various visual effects in text rendering. Constants are defined representing each available type of visual effect:

int EFFECT_PLAIN: no special effects used.

int EFFECT_GRADIENT: use gradient painting of the text's foreground.

int EFFECT_IMAGE: use an image to fill the text's foreground.

int EFFECT_IMAGE_ANIMATION: use a moving image to fill the text's foreground.

int EFFECT_COLOR_ANIMATION: use a cyclically changing color to fill the text's foreground.

Several instance variables are needed to hold data used by this class:

int m_effectIndex: type of the current effect used to render the text (defaults to EFFECT_PLAIN).

double m_shearFactor: shearing factor determining how the text will lean (positive: lean to the left; negative: lean to the right).

Color m_outlineColor: color used to outline the text.

Stroke m_stroke: stroke instance used to outline the text.

GradientPaint m_gradient: color gradient used to fill the text (this only takes effect when m_effectIndex is set to EFFECT_GRADIENT).

Image m_foregroundImage: image used to fill the text (this only takes effect when m_effectIndex is set to EFFECT_IMAGE).

Thread m_animator: thread which produces animation and color cycling.

boolean m_isRunning: true if the m_animator thread is running, false otherwise.

int m_delay: delay time in ms which determines the speed of animation.

int m_xShift: the current image offset in the horizontal direction (this only takes effect when m_effectIndex is set to EFFECT_IMAGE_ANIMATION).

There are three JLabel2D constructors, and each calls the corresponding superclass (JLabel) constructor. This class also defines several self-explanatory set/get accessors for our instance variables listed above.

The startAnimation() method starts our m_animator thread which executes every m_delay ms. In the case of image animation, this thread periodically increases our m_xShift variable which is used in the rendering routine below as an offset. Depending on how smooth we want our animation we can increase or decrease this shift value. Increasing it would give the appearance of speeding up the animation but would make it jumpier. Decreasing it would slow it down but it would appear much smoother.

In the case of color animation, the startAnimation() method determines the two colors of the current m_gradient, and calculates an intermediate color by 'drawing' a cosine curve between the red, green, and blue components of these colors. Local variable arg is incremented by Math.PI/10 each iteration. We take the cosine of this value and calculate two new values, f1 and f2, based on this result:

    arg += Math.PI/10;

    double cos = Math.cos(arg);

    double f1 = (1+cos)/2;

    double f2 = (1-cos)/2;

    arg = arg % (Math.PI*2);

f1 and f2 will always sum to 1, and because arg is incremented by Math.PI/10, we will obtain a consistent cycle of 20 different combinations of f1 and f2 as shown below.
































































We then use f1 and f2 as factors for determining how much of the red, green, and blue component of each of the gradient's colors to use for the foreground image:

    Color c1 = m_gradient.getColor1();

    Color c2 = m_gradient.getColor2();

    int r = (int)(c1.getRed()*f1+c2.getRed()*f2);

    r = Math.min(Math.max(r, 0), 255);

    int g = (int)(c1.getGreen()*f1+c2.getGreen()*f2);

    g = Math.min(Math.max(g, 0), 255);

    int b = (int)(c1.getBlue()*f1+c2.getBlue()*f2);

    b = Math.min(Math.max(b, 0), 255);

    setForeground(new Color(r, g, b));

This gives us 20 distinct colors. We can always increase or decrease this count by increasing or decreasing the denominator of arg respectively (e.g. arg = Math.PI/20 will give 40 distinct colors, and arg = Math.PI/5 will give 10 distinct colors).

The paintComponent() method first calculates the coordinates of the area available for rendering. Since we avoid the call to super.paintComponent() we are responsible for filling the background and border ourselves. So we first check the opaque property (inherited from JLabel) and, if it is set to true, the background is painted manually by filling the component's available region with its background color. We then call the paintBorder() method to render the border (if any) around the component.

Then the Graphics parameter is cast to a Graphics2D instance to obtain access to Java2D features. As we discussed above, two rendering hints are specified using the setRenderingHint() method: anti-aliasing and the priority of quality over speed (see 23.1.10).

A FontRenderContext instance is retrieved from the Graphics2D context and used to create a TextLayout instance using our label's text (refer back to 23.1.13 and 23.1.14). TextLayout's getOutline() method retrieves a Shape which outlines the given text. This method takes an AffineTransform instance as an optional parameter. We use this parameter to shear the text (if our m_shearFactor property is set to a non-zero value).

We then calculate the location of the text depending on the horizontal alignment of our label (center, or right; left needs no special handling). The y-coordinate of the text's baseline is calculated using the top margin, y, the height over 2, h/2, and the ascent over 4, tl.getAscent()/4 (see Figure 23.4).  Why ascent/4? See chapter 2, section 2.8.3 for an explanation.

    float yText = y + h/2 + tl.getAscent()/4;

Figure 23.4 Vertical positioning of the label text.


We then create an AffineTransform for translation to the calculated position. The createTransformedShape() method is then used to get a Shape representing the outline of the text at that position.

If the m_outlineColor property is set, we draw the text's outline using that color and the assigned stroke instance, m_stroke (if set). The rest of the code fills the interior of the text's Shape we retrieved above using the specified visual effect:

For EFFECT_GRADIENT we use a GradientPaint instance to fill the interior.

For EFFECT_IMAGE we use our fillByImage() method to fill the interior with the specified image.

For EFFECT_COLOR_ANIMATION we simply fill the interior with the foreground color (which changes cyclically in the animation thread).

For EFFECT_IMAGE_ANIMATION we use our fillByImage() method with the shift parameter (which is periodically incremented by the animation thread).

For EFFECT_PLAIN we simply fill the interior with the foreground color.

Running the Code

Figure 23.3 shows our JLabel2D demo application in action. You can modify the settings of each label specified in the Labels2D class to try out different combinations of available visual effects. Try out different animation speeds and note how it affects the performance of the application. Choosing too small of a delay may virtually freeze the application. We might consider enforcing a lower bound to ensure that this does not happen.

Note that our JLabel2D component can be easily plugged into any Swing application and used by developers without specific knowledge of Java2D.

[ Return to Swing (Book) ]

Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color

[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3

[ More in News Section ]

Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy