Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4650997

java.awt.font.TextLayout.getBounds() returns a wrong result for rotated font

    Details

    • Subcomponent:
      2d
    • Resolved In Build:
      mantis
    • CPU:
      sparc
    • OS:
      solaris_8

      Description

      The java.awt.font.TextLayout.getBounds() method return a completely wrong
      result for a rotated font (other than zero, -PI) with j2se-1.4.0. The same
      code works with j2se-1.3.1

      Versions:
      SunOS suncity 5.8 Generic_108528-13 sun4u sparc SUNW,Sun-Blade-1000
       
      % java -version
      java version "1.3.1"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24)
      Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode)

      suncity: /home/mkh
      % /usr/j2sdk1.4.0/bin/java -version
      java version "1.4.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
      Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

      Here are two examples, first j2se1.4, followed by j2se1.3.

      % /usr/j2sdk1.4.0/bin/java FontFlip
      Angle: -0.0
      getAscent: 23.0
      getAdvance: 195.0
      getBaseline: 0
      getBounds:
      java.awt.geom.Rectangle2D$Float[x=2.1875,y=-17.46875,w=190.39062,h=22.515625]
      getDescent: 7.0
      getLeading: 1.0
      getVisibleAdvance: 194.0
      .
      Angle: -0.7853981633974483
      getAscent: 16.104034
      getAdvance: 137.883
      getBaseline: 0
      getBounds:
      java.awt.geom.Rectangle2D$Float[x=-6.800354,y=-1135.0493,w=142.96808,h=1135.0493]
      getDescent: 5.0
      getLeading: 1.0
      getVisibleAdvance: 137.883
      .


      % /usr/j2se/bin/java FontFlip
      Angle: -0.0
      getAscent: 21.0
      getAdvance: 195.0
      getBaseline: 0
      getBounds: java.awt.geom.Rectangle2D$Float[x=2.0,y=-18.0,w=191.0,h=24.0]
      getDescent: 6.0
      getLeading: 1.0
      getVisibleAdvance: 195.0
      .
      Angle: -0.7853981633974483
      getAscent: 16.0
      getAdvance: 146.0
      getBaseline: 0
      getBounds: java.awt.geom.Rectangle2D$Float[x=-7.0,y=-133.0,w=152.0,h=133.0]
      getDescent: 4.0
      getLeading: 1.0
      getVisibleAdvance: 146.0
      .

      Here is the test program (a single file):

      % cat FontFlip.java

      import java.awt.*;
      import java.awt.event.*;
      import java.text.*;
      import java.awt.font.*;
      import java.awt.image.*;
      import java.awt.geom.*;
      import java.util.*;

      import javax.swing.*;

      public class FontFlip extends JComponent{
        int counter = 0;
        // Text we render
        String text = ".This is a STRINg.";

        // The following transform performs a flip along the x axis
        AffineTransform flip;

        // Rendering context parameters
        Font font = new Font("Default", Font.PLAIN, 24);
        Font flippedFont;

        public FontFlip(){
          setPreferredSize(new Dimension(700,700));
        }

        public void paint(Graphics _g){
          Graphics2D g = (Graphics2D)_g;
          Dimension d = getSize();
          counter++;
          int x = d.width/2;
          int y = d.height/2;
          FontRenderContext frc = g.getFontRenderContext();

          for (int i = 0; i < 8; i++) {
              double angle = -Math.PI/4.0*i;
              flip = AffineTransform.getRotateInstance(angle);
              flippedFont = font.deriveFont(flip);
              TextLayout tl = new TextLayout(text, flippedFont, frc);
              Rectangle2D bb = tl.getBounds();
              g.setPaint(Color.black);
              tl.draw(g, x, y);
              g.setPaint(Color.red);
              g.drawRect(x + (int) bb.getX(), y + (int) bb.getY(),
                  (int) bb.getWidth(), (int) bb.getHeight());

              if (counter == 1) {
                  System.out.println("Angle: "+angle);
                  System.out.println("getAscent: "+tl.getAscent());
                  System.out.println("getAdvance: "+tl.getAdvance());
                  System.out.println("getBaseline: "+tl.getBaseline());
                  System.out.println("getBounds: "+tl.getBounds());
                  System.out.println("getDescent: "+tl.getDescent());
                  System.out.println("getLeading: "+tl.getLeading());
                  System.out.println("getVisibleAdvance: "+tl.getVisibleAdvance());
                  System.out.println(".");
              } else if (i == 0) {
                  System.out.println("Paint, counter="+counter);
              }
          }

        }

        public static void main(String args[]){
          new SnippetFrame(new FontFlip());
        }
      }

      class SnippetFrame extends JFrame{
        /**
         * @param cmp component to add to the frame's content pane
         */
        public SnippetFrame(JComponent cmp){
          super();

          getContentPane().add(cmp);

          // Set white background as a default
          getContentPane().setBackground(Color.white);

          // Pack to fit cmp's preferred size
          pack();

          // Add a listener to close the application when
          // user closes the frame.
          addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
              System.exit(0);
            }
          });

          setVisible(true);
        }
      }



      Notice also that the bounds are correct so long as the text is horizontal (even if it is rotated upside down). The text is correctly rotated -- it's the bounds that are wrong.

      A slight modification to the example shows that 1.4 also suffers a huge
      performance degradation over 1.3, even though a feature of 1.4 is supposed to be
      improved graphics 2D performance. On the Windows2000 platform the attached
      example runs in about 3.3 seconds under 1.3.1, but it takes about 20 seconds
      under 1.4 (it does not matter whether or not the bounding box is drawn).

      Here is the example that shows the performance problem:

      <<TL.java>>
      import java.awt.*;
      import java.awt.event.*;
      import java.text.*;
      import java.awt.font.*;
      import java.awt.image.*;
      import java.awt.geom.*;
      import java.util.*;

      import javax.swing.*;

      public class TL extends JComponent{
        int number = 360;
        boolean print = false;
        boolean box = true;
        int counter = 0;
        // Text we render
        String text = ".This is a STRINg.";

        // The following transform performs a flip along the x axis
        AffineTransform flip;

        // Rendering context parameters
        Font font = new Font("Default", Font.PLAIN, 24);
        Font flippedFont;

        public TL(){
          setPreferredSize(new Dimension(700,700));
        }

        public void paint(Graphics _g){
          long start = System.currentTimeMillis();
          Graphics2D g = (Graphics2D)_g;
          Dimension d = getSize();
          counter++;
          int x = d.width/2;
          int y = d.height/2;
          FontRenderContext frc = g.getFontRenderContext();

          for (int i = 0; i < number; i++) {
      double angle = -Math.PI*2.0/number*i;
      flip = AffineTransform.getRotateInstance(angle);
      flippedFont = font.deriveFont(flip);
      TextLayout tl = new TextLayout(text, flippedFont, frc);
      Rectangle2D bb = tl.getBounds();
      g.setPaint(Color.black);
      tl.draw(g, x, y);
      g.setPaint(Color.red);
      if (box) {
      g.drawRect(x + (int) bb.getX(), y + (int) bb.getY(),
      (int) bb.getWidth(), (int) bb.getHeight());
      }

      if (print && counter == 1) {
      System.out.println("Angle: "+angle);
      System.out.println("getAscent: "+tl.getAscent());
      System.out.println("getAdvance: "+tl.getAdvance());
      System.out.println("getBaseline: "+tl.getBaseline());
      System.out.println("getBounds: "+tl.getBounds());
      System.out.println("getDescent: "+tl.getDescent());
      System.out.println("getLeading: "+tl.getLeading());
      System.out.println("getVisibleAdvance: "+tl.getVisibleAdvance());
      System.out.println(".");
      } else if (print && i == 0) {
      System.out.println("Paint, counter="+counter);
      }
          }
          System.out.println("Time="+(System.currentTimeMillis()-start));
        }

        public static void main(String args[]){
          new SnippetFrame(new TL());
        }
      }

      class SnippetFrame extends JFrame{
        /**
         * @param cmp component to add to the frame's content pane
         */
        public SnippetFrame(JComponent cmp){
          super();

          getContentPane().add(cmp);

          // Set white background as a default
          getContentPane().setBackground(Color.white);

          // Pack to fit cmp's preferred size
          pack();

          // Add a listener to close the application when
          // user closes the frame.
          addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent evt){
      System.exit(0);
            }
          });

          setVisible(true);
        }
      }

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                dougfelt Doug Felt
                Reporter:
                duke J. Duke (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                0 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: