Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 6727719
Votes 0
Synopsis Performance of TextLayout.getBounds()
Category java:classes_2d
Reported Against
Release Fixed 7(b54)
State 10-Fix Delivered, bug
Priority: 3-Medium
Related Bugs
Submit Date 21-JUL-2008
Description
FULL PRODUCT VERSION :
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Client VM (build 1.6.0_03-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Windows XP Professional

A DESCRIPTION OF THE PROBLEM :
We have an application that overrides Printable.print method in which we does several
Graphics2D.drawString() in loop.
The problem is that the amount of time to be executed and the  customer  created increases with memory usage
when drawString method is called compared to Java1.4.2.

This leads to Garbage Collection to occur often. In a result, there is a noticeable performance degradation in our application.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following test case

ACTUAL -
Java1.4 - 14813ms
Java6.0 - 57797ms


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.print.*;
import java.awt.geom.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
 
class PrintTest implements Printable {
 
    /** Printer Job Name */
    static String JOB_NAME = "TestPrint";
 
    /** Print Data(1 Line) */
    static String PRINT_DATA = "ABCDE FGHIJ KLMNO PQRST UVWXY Z";
 
    /** Page No */
    static int PAGE = 400;
 
    /** Line */
    static int LINE = 100;
 
    /** Font Name */
    static String FONT_NAME = "Courier New";
 
    /** Font Size */
    static int FONT_SIZE = 8;
 
    /** Font Setting */
    static Font FONT_SETTING = new Font(FONT_NAME, Font.PLAIN, FONT_SIZE);
 
    /** Previous Page */
    private int iOldPage = -1;
 
    public static void main(String[] args) {
        PrintTest pt = new PrintTest();
        pt.printStart();
    }
 
    private void printStart() {
 
        PrintService printService = PrintServiceLookup
                .lookupDefaultPrintService();
        if (printService == null) {
            System.err.println("No Print Service is available");
            System.exit(1);
        }
 
        DocPrintJob job = printService.createPrintJob();
 
        MediaSizeName mediaSizeName = MediaSizeName.ISO_A4;
        PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
        attr.add(new JobName(JOB_NAME, null));
        attr.add(OrientationRequested.PORTRAIT);
        attr.add(mediaSizeName);
 
        Object obj = printService.getSupportedAttributeValues(
                MediaPrintableArea.class,
                DocFlavor.SERVICE_FORMATTED.PRINTABLE, attr);
        MediaPrintableArea[] mpa0 = (MediaPrintableArea[]) obj;
        if (mpa0 == null) {
            System.err.println("Print Service is unsuccessful");
            System.exit(1);
        }
 
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
        Doc doc = new SimpleDoc(this, flavor, null);
 
        try {
            long start = System.currentTimeMillis();
            job.print(doc, attr);
            long end = System.currentTimeMillis() - start;
            System.out.println(end);
        } catch (PrintException e) {
            System.err.println("Print failed");
        }
    }
 
    public int print(Graphics g, PageFormat pf, int index) {
        try {
 
            Graphics2D g2d = (Graphics2D) g;
 
            float x, y;
            float dy = 0;
            Rectangle2D r2d = FONT_SETTING.getMaxCharBounds(g2d
                    .getFontRenderContext());
            if (r2d instanceof Rectangle2D.Double) {
                dy = (float) ((Rectangle2D.Double) r2d).getHeight();
            } else if (r2d instanceof Rectangle2D.Float) {
                dy = (float) ((Rectangle2D.Float) r2d).getHeight();
            }
            x = (float) pf.getImageableX();
            y = (float) pf.getImageableY() + dy;
 
            for (int iLine = 1; iLine <= LINE; iLine++) {
                g2d.drawString(PRINT_DATA, x, y);
 
                y += dy;
            }
 
            if (index < PAGE) {
                return Printable.PAGE_EXISTS;
            } else {
                iOldPage = index;
                return Printable.NO_SUCH_PAGE;
            }
        } finally {
 
            iOldPage = index;
        }
    }
}
---------- END SOURCE ----------

Release Regression From : 6
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.
Posted Date : 2008-07-21 07:29:43.0
Work Around
N/A
Evaluation
This bug was introduced very early in JDk6 - b03 to be exact, in the fix for
5074057: handle glyph visual bounds correctly when frc has transform.

This change was in text code, not printing but it impacts printing because
the 'peek' uses TextLayout to determine the bounds, and that uses this code path.

But as of JDk7 build 08, the fix for 6498340 uses a much cheaper way to check
the bounds to fix this case. So the case as persented is already identified.

However the deterioration in the performance of the TextLayout code could
affect other apps so it probably should still be investigated. 

On one system following program runs in about 2.6 secs on jdk 6 b02, and 26 secs
on jdk6 b03

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;

public class TextLayoutPerf {

    public static void main(String args[]) {

        TextLayout tl;

        String text = "ABCDE FGHIJ KLMNO PQRST UVWXY Z";
        Font font = new Font("monospaced", Font.PLAIN, 8);
        double s = 300/72.0;
        AffineTransform tx = AffineTransform.getScaleInstance(s, s);
        FontRenderContext frc = new FontRenderContext(tx, false, false);
        tl = new TextLayout(text, font, frc);
        long t0 = System.currentTimeMillis();
        for (int i=0;i<10000;i++) {
            tl = new TextLayout(text, font, frc);
            tl.getBounds();
        }
        long t1 = System.currentTimeMillis();
        System.out.println(t1-t0);
    }
}

The primary cause is apparently the creation of a new GeneralPath.
This only occurs if there's a transform, which is bound to be the case
in printing. Need to investigate if there's a way arond this.
Posted Date : 2008-07-22 23:58:52.0

Looks like its not just the GeneralPath - its going down into the rasteriser
to get the glyph outline that costs a chunk. The GP just adds to that.

The real difference is that the old font code used the glyph outline bounds
which is CACHED, and the new one uses the glyph outline which is *NOT* cached.

This was necessary for correctness.

Adding a cache for that too helps get most of the performance back and could assist
performance of code that uses the outline during animation.
Posted Date : 2009-03-11 22:44:39.0
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang