(, , ). , :
Font#getStringBounds
FontRenderContext
Graphics2D
, .
"default" () FontRenderContext
( StanislavL ) ( ), , , , ( 0,5).
, , birds : 1,0, 'm , FontMetrics
.
:
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
public class StringBoundsUtils
{
private static final Graphics2D DEFAULT_GRAPHICS;
static
{
BufferedImage bi = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
DEFAULT_GRAPHICS = bi.createGraphics();
DEFAULT_GRAPHICS.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
public static Rectangle2D computeStringBounds(String string, Font font)
{
return computeStringBounds(string, font, new Rectangle2D.Double());
}
public static Rectangle2D computeStringBounds(
String string, Font font, Rectangle2D result)
{
final float helperFontSize = 1000.0f;
final float fontSize = font.getSize2D();
final float scaling = fontSize / helperFontSize;
Font helperFont = font.deriveFont(helperFontSize);
FontMetrics fontMetrics = DEFAULT_GRAPHICS.getFontMetrics(helperFont);
double stringWidth = fontMetrics.stringWidth(string) * scaling;
double stringHeight = fontMetrics.getHeight() * scaling;
if (result == null)
{
result = new Rectangle2D.Double();
}
result.setRect(
0, -fontMetrics.getAscent() * scaling,
stringWidth, stringHeight);
return result;
}
}
( / Graphics2D
, , FontMetrics
, ...)
, , : , , FontMetrics
, , (, , ), ... .
( , ):
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
public class StringBoundsUtilsPerformance
{
public static void main(String[] args)
{
String strings[] = {
"a", "AbcXyz", "AbCdEfGhIjKlMnOpQrStUvWxYz",
"AbCdEfGhIjKlMnOpQrStUvWxYz" +
"AbCdEfGhIjKlMnOpQrStUvWxYz" +
"AbCdEfGhIjKlMnOpQrStUvWxYz" +
"AbCdEfGhIjKlMnOpQrStUvWxYz" +
"AbCdEfGhIjKlMnOpQrStUvWxYz" };
float fontSizes[] = { 1.0f, 10.0f, 100.0f };
int runs = 1000000;
long before = 0;
long after = 0;
double resultA = 0;
double resultB = 0;
for (float fontSize : fontSizes)
{
Font font = new Font("Dialog", Font.PLAIN, 10).deriveFont(fontSize);
for (String string : strings)
{
before = System.nanoTime();
for (int i=0; i<runs; i++)
{
Rectangle2D r = computeStringBoundsDefault(string, font);
resultA += r.getWidth();
}
after = System.nanoTime();
resultA /= runs;
System.out.printf(Locale.ENGLISH,
"A: time %14.4f result %14.4f, fontSize %3.1f, length %d\n",
(after-before)/1e6, resultA, fontSize, string.length());
before = System.nanoTime();
for (int i=0; i<runs; i++)
{
Rectangle2D r =
StringBoundsUtils.computeStringBounds(string, font);
resultB += r.getWidth();
}
after = System.nanoTime();
resultB /= runs;
System.out.printf(Locale.ENGLISH,
"B: time %14.4f result %14.4f, fontSize %3.1f, length %d\n",
(after-before)/1e6, resultB, fontSize, string.length());
}
}
}
private static final FontRenderContext DEFAULT_FONT_RENDER_CONTEXT =
new FontRenderContext(null, true, true);
public static Rectangle2D computeStringBoundsDefault(
String string, Font font)
{
return font.getStringBounds(string, DEFAULT_FONT_RENDER_CONTEXT);
}
}
, :
A: time 1100.4441 result 14.7813, fontSize 1.0, length 26
B: time 218.6409 result 14.7810, fontSize 1.0, length 26
...
A: time 1167.1569 result 147.8125, fontSize 10.0, length 26
B: time 200.6532 result 147.8100, fontSize 10.0, length 26
...
A: time 1179.7873 result 1478.1253, fontSize 100.0, length 26
B: time 208.9414 result 1478.1003, fontSize 100.0, length 26
, StringBoundsUtils
, Font#getStringBounds
5 ( ).
result
, , Font#getStringBounds
, StringBoundsUtils
, .
, , widhts, . :
, " " - , , StringBoundsUtils
, 0,5.
: ( Viewer, JAR Maven)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import de.javagl.viewer.Painter;
import de.javagl.viewer.Viewer;
public class StringBoundsUtilsTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static final Font DEFAULT_FONT =
new Font("Dialog", Font.PLAIN, 10);
private static Font font = DEFAULT_FONT.deriveFont(10f);
private static void createAndShowGUI()
{
JFrame f = new JFrame("Viewer");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
Viewer viewer = new Viewer();
String string = "AbcXyz";
viewer.addPainter(new Painter()
{
@Override
public void paint(Graphics2D g, AffineTransform worldToScreen,
double w, double h)
{
AffineTransform at = g.getTransform();
g.setColor(Color.BLACK);
g.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle2D boundsA =
StringBoundsUtilsPerformance.computeStringBoundsDefault(
string, font);
Rectangle2D boundsB =
StringBoundsUtils.computeStringBounds(string, font);
g.setFont(new Font("Monospaced", Font.BOLD, 12));
g.setColor(Color.GREEN);
g.drawString(createString(boundsA), 10, 20);
g.setColor(Color.RED);
g.drawString(createString(boundsB), 10, 40);
g.setFont(font);
g.transform(worldToScreen);
g.drawString(string, 0, 0);
g.setTransform(at);
g.setColor(Color.GREEN);
g.draw(worldToScreen.createTransformedShape(boundsA));
g.setColor(Color.RED);
g.draw(worldToScreen.createTransformedShape(boundsB));
}
});
f.getContentPane().add(viewer, BorderLayout.CENTER);
f.getContentPane().add(
new JLabel("Mouse wheel: Zoom, "
+ "Right mouse drags: Move, "
+ "Left mouse drags: Rotate"),
BorderLayout.NORTH);
JSpinner fontSizeSpinner =
new JSpinner(new SpinnerNumberModel(10.0, 0.1, 100.0, 0.1));
fontSizeSpinner.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
Object object = fontSizeSpinner.getValue();
Number number = (Number)object;
float fontSize = number.floatValue();
font = DEFAULT_FONT.deriveFont(fontSize);
viewer.repaint();
}
});
JPanel p = new JPanel();
p.add(new JLabel("Font size"), BorderLayout.WEST);
p.add(fontSizeSpinner, BorderLayout.CENTER);
f.getContentPane().add(p, BorderLayout.SOUTH);
viewer.setPreferredSize(new Dimension(1000,500));
viewer.setDisplayedWorldArea(-15,-15,30,30);
f.pack();
viewer.setPreferredSize(null);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static String createString(Rectangle2D r)
{
return String.format(Locale.ENGLISH,
"x=%12.4f y=%12.4f w=%12.4f h=%12.4f",
r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
}