1

I need to write a generic method that draw a centered String. In order to do that I need to know the width of the String and in order to calculate that I have different alternatives:

Rectangle2D rect=Toolkit.getDefaultToolkit().getFontMetrics(gc.getFont()).getStringBounds(text, gc);

or

AffineTransform affinetransform = new AffineTransform();    
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);    
Rectangle2D rect=gc.getFont().getStringBounds(text, frc);

or

FontMetrics metrics = g.getFontMetrics(font);
metrics.stringWidth(text)

But does't matter which approach I use it takes around 1 second to calculate that, that's crazy I'm using Eclipse with Java 1.8.0_121 on a on Macbook 3,1 GHz Intel Core i7, that's crazy!

What is wrong with it?

First Example:

import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class Main {


  public static void main(String[] args) {
    JFrame jf = new JFrame("Demo");
    Container cp = jf.getContentPane();
    MyCanvas1 tl = new MyCanvas1();
    cp.add(tl);
    jf.setSize(300, 200);
    jf.setVisible(true);
  }
}

class MyCanvas1 extends JComponent {

  public void paint(Graphics g) {
      long start=System.currentTimeMillis();
    Graphics2D g2 = (Graphics2D) g;

    drawCenteredString(g2, "Example", getBounds(), g.getFont());
    System.out.println("executed in:"+(System.currentTimeMillis()-start));


  }

  public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
        FontMetrics metrics = g.getFontMetrics(font);

        int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2;
        // Determine the Y coordinate for the text (note we add the ascent, as in java 2d 0 is top of the screen)
        int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent();
        // Set the font
        g.setFont(font);
        // Draw the String
        g.drawString(text, x, y);
    }


}

executed in:922

Example 2:

import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class Main {


  public static void main(String[] args) {
    JFrame jf = new JFrame("Demo");
    Container cp = jf.getContentPane();
    MyCanvas1 tl = new MyCanvas1();
    cp.add(tl);
    jf.setSize(300, 200);
    jf.setVisible(true);
  }
}

class MyCanvas1 extends JComponent {

  public void paint(Graphics g) {
      long start=System.currentTimeMillis();
    Graphics2D g2 = (Graphics2D) g;

    drawCenteredString(g2, "Example", getBounds(), g.getFont());
    System.out.println("executed in:"+(System.currentTimeMillis()-start));


  }

  public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
      AffineTransform affinetransform = new AffineTransform();    
      FontRenderContext frc = new FontRenderContext(affinetransform,true,true);    
      Rectangle2D rect2=font.getStringBounds(text, frc);
        // Draw the String
        g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
    }


}

executed in:916

Example 3:

import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class Main {


  public static void main(String[] args) {
    JFrame jf = new JFrame("Demo");
    Container cp = jf.getContentPane();
    MyCanvas1 tl = new MyCanvas1();
    cp.add(tl);
    jf.setSize(300, 200);
    jf.setVisible(true);
  }
}

class MyCanvas1 extends JComponent {

  public void paint(Graphics g) {
      long start=System.currentTimeMillis();
    Graphics2D g2 = (Graphics2D) g;

    drawCenteredString(g2, "Example", getBounds(), g.getFont());
    System.out.println("executed in:"+(System.currentTimeMillis()-start));


  }

  public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
      AffineTransform affinetransform = new AffineTransform();    
      FontRenderContext frc = new FontRenderContext(affinetransform,true,true);    
      Rectangle2D rect2=Toolkit.getDefaultToolkit().getFontMetrics(font).getStringBounds(text, g);

        // Draw the String
        g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
    }


}

executed in:908

It seems somebody else had a similar problem but without any solution:

Java: Fastest way to draw text?

Does somebody see where the problem could be?

EDIT: Just an update using Java 8u221 same results, using the latest Java 13 I got a little improvement instead of 900 they are executed in around 700 milliseconds... I cannot believe that... It seems this operations are very slow on Mac... doesn't matter the Java version I use...

EDIT2:

Even executing this code:

public class Main {


  public static void main(String[] args) {
      long start=System.currentTimeMillis();
      BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = image.createGraphics();

        drawCenteredString(g2, "Example", new Rectangle (0,0,300,300), g2.getFont());
        System.out.println("executed in:"+(System.currentTimeMillis()-start));
  }




  public static void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {

      Rectangle2D rect2=Toolkit.getDefaultToolkit().getFontMetrics(font).getStringBounds(text, g);
        // Draw the String
        g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
    }


}

I get: executed in:1342

navy1978
  • 1,411
  • 1
  • 15
  • 35
  • Executed in 21, 46 and 21. Using HP ZBook 15 G3 – Yoshikage Kira Sep 17 '19 at 19:53
  • Which java version ? – navy1978 Sep 17 '19 at 20:03
  • My java version is 1.8.0_212. Do you have another system? Try running this program on it. Also try running this via executable jar file. – Yoshikage Kira Sep 17 '19 at 20:05
  • I will try with another java version and another system... – navy1978 Sep 17 '19 at 20:30
  • I tried this in both Java 8 and Java 11, on 64-bit Debian Linux. The first time, I got 388. Each time after that, it was either 0 milliseconds or 1 millisecond. Which is what I would expect. – VGR Sep 17 '19 at 20:38
  • Yes the first time is the one that counts... so 388 is a lot better than mine but still very bad performance I would say... – navy1978 Sep 17 '19 at 20:39
  • You misunderstand how Java benchmarks work. The first time the code runs, it is necessary to load a number of classes. This is why a Java benchmark is not valid if it only times the first execution of a piece of code. – VGR Sep 17 '19 at 20:41
  • Java 8 which version exactly? – navy1978 Sep 17 '19 at 20:43
  • I think I misunderstood what you wrote the first time I thought you executed it in a loop so was normal the first time was s a lot slower the the others . But if you got this bumbers in different execution it’s also strange ... for me the result are very constant across different executions – navy1978 Sep 17 '19 at 20:45
  • I installed 8u221 same values and with the last java 13 I got 700 instead of 900 but that is still crazy! each execution takes so long ... I cannot believe it.... – navy1978 Sep 17 '19 at 21:35

0 Answers0