Outlined text and outlineColor and inner fillColor?

http://koti.mbnet.fi/akini/java/png/outlinetext/

I have tested outlined text rendering. See link, example png and full sources. Two questions.

  1. fill color for outlined text
    How should I give a fill color for the outlined text or gradient paint. Now I render it two times. First render a normal text or gradient paint. Then I render outline text on top of it. But see an example picture how background and outline does not always match properly. “W” char is best to how color overflow.

  2. reset state of Graphics2D before next method call
    I call “sayWorld” method twice but with different parameters. I set various g2d properties before drawing a text. All settings reflect to the end of current paint method. Is there an easy way to reset g2d back to the state it was before calling a method. I would like a method call see it always as a “clean state” object.


import java.awt.*; 
import javax.swing.*;
import java.awt.font.*; 
import java.awt.geom.*; 
import java.awt.image.*;

class THello extends JFrame {

  public THello() {
    super("THello"); setBounds(50,50,500,400);
    Container con = this.getContentPane(); //inherit frame
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    TCanvas canvas = new TCanvas(); 
    con.add(canvas); 
    setVisible(true);
  }
 
  public static void main(String arg[]) {
    new THello();
  }
}

class TCanvas extends Canvas {
  Font font; FontMetrics fontMetrics; // make these global

  TCanvas() {
    font = new Font("Dialog",Font.BOLD,40);
    fontMetrics = getFontMetrics(font);
  }

  public void paint(Graphics g) {
    Graphics2D g2D = (Graphics2D)g;
    g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                         RenderingHints.VALUE_ANTIALIAS_ON);
    g2D.setBackground(Color.white); 
    g2D.setFont(font);

    sayWorld(g2D, 100, 100, true);
    sayWorld(g2D, 100, 160, false);
  }

  //outlined text
  public void sayWorld(Graphics2D g2D, int x, int y, boolean shear) {
      final String txt = "Hello World!";

      // gradient color from blue to red
      GradientPaint gp = new GradientPaint((float)x, (float)y, Color.blue,
                             x+100, y+20, Color.red);
      g2D.setPaint(gp); 
      if (shear) g2D.shear(-0.5,0.0);
      else       g2D.shear(+0.5, 0);
      g2D.drawString(txt, x, y);

      FontRenderContext frc = new FontRenderContext(null,false,false);

      TextLayout tl = new TextLayout(txt, font, frc);
      AffineTransform textAt = new AffineTransform();
      //textAt.translate(0, (float)tl.getBounds().getHeight());
      textAt.translate(x,y); 
      //textAt.shear(-0.5,0.0);

      Shape outline = tl.getOutline(textAt); 
      g2D.setColor(Color.yellow);
      BasicStroke wideStroke = new BasicStroke(2);
      g2D.setStroke(wideStroke); 
      g2D.draw(outline);
  }

}

Hi Whome,

If you already have the shape, why not use it again with g2D.fill(outline) ?
Solves the problem :slight_smile: Apparently drawString calculates a little different :slight_smile:


      Shape outline = tl.getOutline(textAt);
      g2D.setPaint(gp); 
      g2D.fill(outline);
      //
      g2D.setColor(Color.yellow);
      BasicStroke wideStroke = new BasicStroke(2);
      g2D.setStroke(wideStroke); 
      g2D.draw(outline);

For your second question, you can create scratch instances of the Graphics object using the create() method,
as you will modify these the original Graphics object will not be changed :slight_smile:


    Graphics2D sub=(Graphics2D)g2D.create();
    try {
      sayWorld(g2D, 100, 100, true);
    } finally {
      sub.dispose();
    }    
    sub=(Graphics2D)g2D.create();
    try {
      sayWorld(g2D, 100, 160, false);
    } finally {
      sub.dispose();
    }

Have Fun,

Erik

Thx, both were so simple answers. I have uploaded outlinetext.zip and image2.png showing how it looks after suggested changes. A lot better results.

I take it that temporary g2d instances constain all attributes I have set at the start of paint method. I set rendering hints, default color and font.

Outlined text does not look good for smaller <20px font sizes, but it must be due to a smaller count of pixels available for rendering. I think I try creating +40px font size texts and then scale image to smaller if that gives better results for smaller text lines. But thx for these two valuable bits.


Graphics2D g2D = (Graphics2D)g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                         RenderingHints.VALUE_ANTIALIAS_ON);
g2D.setBackground(Color.white); 
g2D.setFont(font);

// create temporary g2d instance for this method call only
Graphics2D gT = (Graphics2D)g2D.create();
sayWorld(gT, 100, 100, true);
gT.dispose();
...continue...

To get nicely rendered small outlined text you can also tell the Graphics2D object to
scale all your rendering operations using the scale() method.

The second thing you can try is to turn on the rendering hint for fractional metrics


    g2D.setRenderingHint( 
          RenderingHints.KEY_FRACTIONALMETRICS, 
          RenderingHints.VALUE_FRACTIONALMETRICS_ON
    );

and tell your font render context that you’re using these as well :


  FontRenderContext frc = new FontRenderContext(null,true,true);

Ofcourse you must change the thickness of the outline accordingly :


  BasicStroke wideStroke = new BasicStroke(0.5f);  // or something