2010-12-02 52 views
13

我遇到问题。如果我在那里使用东方语言,我的应用程序界面工作会更慢特别是我觉得它在JList,JCombobox,JTable等组件中。如果文本中有阿拉伯文或波斯文字符,通过fontmetrics计算的字符串宽度非常慢

如果在文本中至少有一个字母是阿拉伯语或波斯语,我如何发现FontMetrics.stringWidth方法的性能很慢(500+倍)。我怎么知道它是各种摆动部件中常用的方法。

有没有办法提高这种方法的性能?

这里是例子类演示此问题:

import java.awt.Font; 
import java.awt.FontMetrics; 
import java.awt.Graphics; 
import java.awt.image.BufferedImage; 

public class FontMetricsSpeedTest 
{ 

public static void main(String args[]) { 
    String persian="صصصصصصصصصصصصصصصصصصصصص"; 
    String english="abcde()agjklj;lkjelwk"; 
    FontMetrics fm=createFontMetrics(new Font("dialog",Font.PLAIN,12)); 
    int size=50000; 
    long start=System.currentTimeMillis(); 
    for(int i=0;i<size;i++) 
    { 
    fm.stringWidth(persian); 
    } 
    System.out.println("Calculation time for persian: "+(System.currentTimeMillis()-start)+" ms"); 
    start=System.currentTimeMillis(); 
    for(int i=0;i<size;i++) 
    { 
    fm.stringWidth(english); 
    } 
    System.out.println("Calculation time for english: "+(System.currentTimeMillis()-start)+" ms"); 
} 
private static FontMetrics createFontMetrics(Font font) 
{ 
    BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); 
    Graphics g = bi.getGraphics(); 
    FontMetrics fm = g.getFontMetrics(font); 
    g.dispose(); 
    bi = null; 
    return fm; 
} 
} 

对我来说,它提供了一个输出:

计算时间波斯语:5482毫秒

计算时间英语:11毫秒

回答

2

我使用您的代码与其他语言进行了一些测试。 首先你是对的:波斯弦的计算花了很多时间。

我玩的字体类型和大小,并没有看到显着差异。但结果肯定取决于你使用的脚本。这是我在我的机器上得到的结果。

Calculation time for Persian: 2877 ms 
Calculation time for English: 8 ms 
Calculation time for Russian: 47 ms 
Calculation time for Hebrew: 16815 ms 

正如你所看到的,俄语比英语慢了6倍。我相信这是因为字符串的内部表示是unicode。在UTF-8中,英文字符占1个字节,其余2个字节。

我不确定它能满足你:)但希伯来语测试比波斯语慢4倍。两者都很慢,所以我猜想从右到左的计算会使它失效。

看来我们与此无关。

+0

但是至少500倍杀死性能:( – httpdispatch 2010-12-02 10:17:50

+0

只同意如果真要计算数千个指标比较慢。如果你有10个在屏幕上的线条并不重要:绘制UI控件也需要时间。顺便说一句,如果你找到解决方案发布在这里,我很想知道... – AlexR 2010-12-02 10:26:55

+0

我认为解决方案将在重写和进一步取代太阳。 font.FontDesignMetrics类可能会添加一些缓存,但看起来并不那么容易 – httpdispatch 2010-12-02 12:48:53

0

你可以尝试使用Font类的方法。 公共GlyphVector的layoutGlyphVector的(FontRenderContext中FRC, 的char []文字, INT开始, INT限制, INT标志)

和使用的GlyphVector来衡量你的字符串?

或者TextLayout的 公共TextLayout的(串串,字体字型,FontRenderContext的FRC)

4

我已经挖了一小和旁边发现:

从FontDesignMetrics的来源,我们可以看到的主要行动序列

public int stringWidth(String str) { 
float width = 0; 
if (font.hasLayoutAttributes()) { 
    /* TextLayout throws IAE for null, so throw NPE explicitly */ 
    if (str == null) { 
     throw new NullPointerException("str is null"); 
    } 
    if (str.length() == 0) { 
     return 0; 
    } 
    width = new TextLayout(str, font, frc).getAdvance(); 
} else { 
    int length = str.length(); 
    for (int i = 0; i < length; i++) { 
     char ch = str.charAt(i); 
     if (ch < 0x100) { 
      width += getLatinCharWidth(ch); 
     } else if (FontManager.isNonSimpleChar(ch)) { 
      width = new TextLayout(str, font, frc).getAdvance(); 
      break; 
     } else { 
      width += handleCharWidth(ch); 
     } 
    } 
} 
return (int) (0.5 + width); 

}

对于拉丁字符,使用方法getLatinCharWidth(CH)。它缓存所有的字符宽度。但是对于波斯语和阿拉伯字符而言,使用TextLayout而不是。主要目的是因为东部角色可能具有不同的形状和宽度取决于上下文。可以添加缓存字符宽度的方法,但不会给出精确的值,例如忽略不同字符宽度的细微差别。它也会忽略各种连字。

我已经单独测试了TextLayout,并且对于英语和波斯语这两种语言都很慢。所以慢速性能的真正原因是sun.font.TextLayout类的缓慢工作。它用于确定字符串宽度,以防字符串中的字符不简单。不幸的是,我不知道如何提高TextLayout的性能。

如果有人有兴趣在这里对各种字体和文本布局的细微差别的文章是 http://download.oracle.com/javase/1.4.2/docs/guide/2d/spec/j2d-fonts.html

0

计算字符串的宽度,当我使用一个缓存。它不能解决javas自己的类做内部调用,但它解决了我与波斯信件(我使用了很多自己的渲染等)的性能问题。 Pair类只是一个两个对象的类型豆...

public class GuiUtils { 
private static final Map<Pair<Boolean, Pair<FontMetrics, String>>, Integer> stringWidthCache = new HashMap<Pair<Boolean, Pair<FontMetrics, String>>, Integer>(); 

public static int getStringWidth(FontMetrics fm, String text){ 
    return getStringWidth(null, fm, text); 
} 

public static int getStringWidth(Graphics g, FontMetrics fm, String text){ 
    if(text == null || text.equals("")) { 
     return 0; 
    } 
    Pair<Boolean, Pair<FontMetrics, String>> cacheKey = 
      new Pair<Boolean, Pair<FontMetrics, String>>(g != null, new Pair<FontMetrics, String>(fm, text)); 
    if (!stringWidthCache.containsKey(cacheKey)) { 
     stringWidthCache.put(
       cacheKey, 
       g != null ? 
         (int)Math.ceil(fm.getStringBounds(text, g).getWidth()) : 
          fm.stringWidth(text)); 
    } 
    return stringWidthCache.get(cacheKey); 
} 

}