blob: deb78a4e267faa6ff621b3041835ef32c50c208c [file] [log] [blame]
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
/*
* (C) Copyright IBM Corp. 2003, All Rights Reserved.
* This technology is protected by multiple US and International
* patents. This notice and attribution to IBM may not be removed.
*/
package j2dbench.tests.text;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.font.NumericShaper;
import java.awt.font.TextAttribute;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JComponent;
import j2dbench.Destinations;
import j2dbench.Group;
import j2dbench.Node;
import j2dbench.Option;
import j2dbench.Option.ObjectList;
import j2dbench.Result;
import j2dbench.Test;
import j2dbench.TestEnvironment;
public abstract class TextTests extends Test {
public static boolean hasGraphics2D;
static {
try {
hasGraphics2D = (Graphics2D.class != null);
} catch (NoClassDefFoundError e) {
}
}
// core data
static final int[] tlengths = {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512
};
static final String[] tscripts = {
// german, vietnamese, surrogate, dingbats
"english", "arabic", "greek", "hebrew", "hindi", "japanese", "korean", "thai",
"english-arabic", "english-greek", "english-hindi", "english-arabic-hindi"
};
static final float[] fsizes = {
1f, 6f, 8f, 10f, 12f, 12.5f, 13f, 13.5f, 16f, 20f, 36f, 72f, 128f
};
static final float[] fintsizes = {
1f, 6f, 8f, 10f, 12f, 13f, 16f, 20f, 36f, 72f, 128f
};
// utilties
static Float[] floatObjectList(float[] input) {
Float[] result = new Float[input.length];
for (int i = 0; i < result.length; ++i) {
result[i] = new Float(input[i]);
}
return result;
}
static String[] floatStringList(float[] input) {
return floatStringList("", input, "");
}
static String[] floatStringList(float[] input, String sfx) {
return floatStringList("", input, sfx);
}
static String[] floatStringList(String pfx, float[] input, String sfx) {
String[] result = new String[input.length];
for (int i = 0; i < result.length; ++i) {
result[i] = pfx + input[i] + sfx;
}
return result;
}
static String[] intStringList(int[] input) {
return intStringList("", input, "");
}
static String[] intStringList(int[] input, String sfx) {
return intStringList("", input, sfx);
}
static String[] intStringList(String pfx, int[] input, String sfx) {
String[] result = new String[input.length];
for (int i = 0; i < result.length; ++i) {
result[i] = pfx + input[i] + sfx;
}
return result;
}
static final String[] txNames;
static final String[] txDescNames;
static final AffineTransform[] txList;
static final Map[] maps;
static {
AffineTransform identity = new AffineTransform();
AffineTransform sm_scale = AffineTransform.getScaleInstance(.5, .5);
AffineTransform lg_scale = AffineTransform.getScaleInstance(2, 2);
AffineTransform wide = AffineTransform.getScaleInstance(2, .8);
AffineTransform tall = AffineTransform.getScaleInstance(.8, 2);
AffineTransform x_trans = AffineTransform.getTranslateInstance(50, 0);
AffineTransform y_trans = AffineTransform.getTranslateInstance(0, -30);
AffineTransform xy_trans = AffineTransform.getTranslateInstance(50, -30);
AffineTransform sm_rot = AffineTransform.getRotateInstance(Math.PI / 3);
AffineTransform lg_rot = AffineTransform.getRotateInstance(Math.PI * 4 / 3);
AffineTransform pi2_rot = AffineTransform.getRotateInstance(Math.PI / 2);
AffineTransform x_shear = AffineTransform.getShearInstance(.4, 0);
AffineTransform y_shear = AffineTransform.getShearInstance(0, -.4);
AffineTransform xy_shear = AffineTransform.getShearInstance(.3, .3);
AffineTransform x_flip = AffineTransform.getScaleInstance(-1, 1);
AffineTransform y_flip = AffineTransform.getScaleInstance(1, -1);
AffineTransform xy_flip = AffineTransform.getScaleInstance(-1, -1);
AffineTransform w_rot = AffineTransform.getRotateInstance(Math.PI / 3);
w_rot.scale(2, .8);
AffineTransform w_y_shear = AffineTransform.getShearInstance(0, -.4);
w_y_shear.scale(2, .8);
AffineTransform w_r_trans = AffineTransform.getTranslateInstance(3, -7);
w_r_trans.rotate(Math.PI / 3);
w_r_trans.scale(2, .8);
AffineTransform w_t_rot = AffineTransform.getRotateInstance(Math.PI / 3);
w_t_rot.translate(3, -7);
w_t_rot.scale(2, .8);
AffineTransform w_y_s_r_trans = AffineTransform.getTranslateInstance(3, -7);
w_y_s_r_trans.rotate(Math.PI / 3);
w_y_s_r_trans.shear(0, -.4);
w_y_s_r_trans.scale(2, .8);
txNames = new String[] {
"ident",
"smsc", "lgsc", "wide", "tall",
"xtrn", "ytrn", "xytrn",
"srot", "lrot", "hrot",
"xshr", "yshr", "xyshr",
"flx", "fly", "flxy",
"wr", "wys", "wrt",
"wtr", "wysrt"
};
txDescNames = new String[] {
"Identity",
"Sm Scale", "Lg Scale", "Wide", "Tall",
"X Trans", "Y Trans", "XY Trans",
"Sm Rot", "Lg Rot", "PI/2 Rot",
"X Shear", "Y Shear", "XY Shear",
"FlipX", "FlipY", "FlipXY",
"WRot", "WYShear", "WRTrans",
"WTRot", "WYSRTrans"
};
txList = new AffineTransform[] {
identity,
sm_scale, lg_scale, wide, tall,
x_trans, y_trans, xy_trans,
sm_rot, lg_rot, pi2_rot,
x_shear, y_shear, xy_shear,
x_flip, y_flip, xy_flip,
w_rot, w_y_shear, w_r_trans,
w_t_rot, w_y_s_r_trans,
};
// maps
HashMap fontMap = new HashMap();
fontMap.put(TextAttribute.FONT, new Font("Dialog", Font.ITALIC, 18));
HashMap emptyMap = new HashMap();
HashMap simpleMap = new HashMap();
simpleMap.put(TextAttribute.FAMILY, "Lucida Sans");
simpleMap.put(TextAttribute.SIZE, new Float(14));
simpleMap.put(TextAttribute.FOREGROUND, Color.blue);
HashMap complexMap = new HashMap();
complexMap.put(TextAttribute.FAMILY, "Serif");
complexMap.put(TextAttribute.TRANSFORM, tall);
complexMap.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
complexMap.put(TextAttribute.RUN_DIRECTION,
TextAttribute.RUN_DIRECTION_RTL);
try {
complexMap.put(TextAttribute.NUMERIC_SHAPING,
NumericShaper.getContextualShaper(NumericShaper.ALL_RANGES));
} catch (NoSuchFieldError e) {
}
maps = new Map[] {
fontMap,
emptyMap,
simpleMap,
complexMap,
};
}
static String getString(Object key, int len) {
String keyString = key.toString();
String[] strings = new String[4]; // leave room for index == 3 to return null
int span = Math.min(32, len);
int n = keyString.indexOf('-');
if (n == -1) {
strings[0] = getSimpleString(keyString);
} else {
strings[0] = getSimpleString(keyString.substring(0, n));
int m = keyString.indexOf('-', n+1);
if (m == -1) {
strings[1] = getSimpleString(keyString.substring(n+1));
// 2 to 1 ratio, short spans between 1 and 16 chars long
span = Math.max(1, Math.min(16, len / 3));
} else {
strings[1] = getSimpleString(keyString.substring(n+1, m));
strings[2] = getSimpleString(keyString.substring(m+1));
span = Math.max(1, Math.min(16, len / 4));
}
}
String s = "";
int pos = 0;
int strx = 0;
while (s.length() < len) {
String src;
if (strings[strx] == null) {
src = strings[0]; // use strings[0] twice for each other string
strx = 0;
} else {
src = strings[strx++];
}
if (pos + span > src.length()) {
pos = 0; // we know all strings are longer than span
}
s += src.substring(pos, pos+span);
pos += span;
}
return s.substring(0, len);
}
static HashMap strcache = new HashMap(tscripts.length);
private static String getSimpleString(Object key) {
String s = (String)strcache.get(key);
if (s == null) {
String fname = "textdata/" + key + ".ut8.txt";
try {
InputStream is = TextTests.class.getResourceAsStream(fname);
if (is == null) {
throw new IOException("Can't load resource " + fname);
}
BufferedReader r =
new BufferedReader(new InputStreamReader(is, "utf8"));
StringBuffer buf = new StringBuffer(r.readLine());
while (null != (s = r.readLine())) {
buf.append(" ");
buf.append(s);
}
s = buf.toString();
if (s.charAt(0) == '\ufeff') {
s = s.substring(1);
}
}
catch (IOException e) {
s = "This is a dummy ascii string because " +
fname + " was not found.";
}
strcache.put(key, s);
}
return s;
}
static Group textroot;
static Group txoptroot;
static Group txoptdataroot;
static Group txoptfontroot;
static Group txoptgraphicsroot;
static Group advoptsroot;
static Option tlengthList;
static Option tscriptList;
static Option fnameList;
static Option fstyleList;
static Option fsizeList;
static Option ftxList;
static Option taaList;
static Option tfmTog;
static Option gaaTog;
static Option gtxList;
static Option gvstyList;
static Option tlrunList;
static Option tlmapList;
// core is textlength, text script, font name/style/size/tx, frc
// drawing
// drawString, drawChars, drawBytes, drawGlyphVector, TextLayout.draw, drawAttributedString
// length of text
// 1, 2, 4, 8, 16, 32, 64, 128, 256 chars
// script of text
// simple: latin-1, japanese, arabic, hebrew, indic, thai, surrogate, dingbats
// mixed: latin-1 + x (1, 2, 3, 4 pairs)
// font of text
// name (composite, not), style, size (6, 12, 18, 24, 30, 36, 42, 48, 54, 60), transform (full set)
// text rendering hints
// aa, fm, gaa
// graphics transform (full set)
// (gv) gtx, gpos
// (tl, as) num style runs
//
// querying/measuring
// ascent/descent/leading
// advance
// (gv) lb, vb, pb, glb, gvb, glb, gp, gjust, gmet, gtx
// (tl) bounds, charpos, cursor
//
// construction/layout
// (bidi) no controls, controls, styles
// (gv) createGV, layoutGV
// (tl) TL constructors
// (tm) line break
public static void init() {
textroot = new Group("text", "Text Benchmarks");
textroot.setTabbed();
txoptroot = new Group(textroot, "opts", "Text Options");
txoptroot.setTabbed();
txoptdataroot = new Group(txoptroot, "data", "Text Data");
tlengthList = new Option.IntList(txoptdataroot, "tlength",
"Text Length",
tlengths,
intStringList(tlengths),
intStringList(tlengths, " chars"),
0x10);
((Option.ObjectList) tlengthList).setNumRows(5);
tscriptList = new Option.ObjectList(txoptdataroot, "tscript",
"Text Script",
tscripts,
tscripts,
tscripts,
tscripts,
0x1);
((Option.ObjectList) tscriptList).setNumRows(4);
txoptfontroot = new Group(txoptroot, "font", "Font");
fnameList = new FontOption(txoptfontroot, "fname", "Family Name");
fstyleList = new Option.IntList(txoptfontroot, "fstyle",
"Style",
new int[] {
Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD + Font.ITALIC,
},
new String[] {
"plain", "bold", "italic", "bolditalic",
},
new String[] {
"Plain", "Bold", "Italic", "Bold Italic",
},
0x1);
float[] fsl = hasGraphics2D ? fsizes : fintsizes;
fsizeList = new Option.ObjectList(txoptfontroot, "fsize",
"Size",
floatStringList(fsl),
floatObjectList(fsl),
floatStringList(fsl),
floatStringList(fsl, "pt"),
0x40);
((Option.ObjectList) fsizeList).setNumRows(5);
if (hasGraphics2D) {
ftxList = new Option.ObjectList(txoptfontroot, "ftx",
"Transform",
txDescNames,
txList,
txNames,
txDescNames,
0x1);
((Option.ObjectList) ftxList).setNumRows(6);
txoptgraphicsroot = new Group(txoptroot, "graphics", "Graphics");
String[] taaNames;
Object[] taaHints;
try {
taaNames = new String[] {
"Off", "On",
"LCD_HRGB", "LCD_HBGR", "LCD_VRGB", "LCD_VBGR"
};
taaHints = new Object[] {
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR,
};
} catch (NoSuchFieldError e) {
taaNames = new String[] {
"Off", "On"
};
taaHints = new Object[] {
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON,
};
}
taaList = new Option.ObjectList(txoptgraphicsroot, "textaa",
"Text AntiAlias",
taaNames, taaHints,
taaNames, taaNames,
0x1);
((Option.ObjectList) taaList).setNumRows(6);
// add special TextAAOpt for backwards compatibility with
// older options files
new TextAAOpt();
tfmTog = new Option.Toggle(txoptgraphicsroot, "tfm",
"Fractional Metrics", Option.Toggle.Off);
gaaTog = new Option.Toggle(txoptgraphicsroot, "gaa",
"Graphics AntiAlias", Option.Toggle.Off);
gtxList = new Option.ObjectList(txoptgraphicsroot, "gtx",
"Transform",
txDescNames,
txList,
txNames,
txDescNames,
0x1);
((Option.ObjectList) gtxList).setNumRows(6);
advoptsroot = new Group(txoptroot, "advopts", "Advanced Options");
gvstyList = new Option.IntList(advoptsroot, "gvstyle", "Style",
new int[] { 0, 1, 2, 3 },
new String[] { "std", "wave", "twist", "circle" },
new String[] { "Standard",
"Positions adjusted",
"Glyph angles adjusted",
"Layout to circle"
},
0x1);
int[] runs = { 1, 2, 4, 8 };
tlrunList = new Option.IntList(advoptsroot, "tlruns", "Attribute Runs",
runs,
intStringList(runs),
intStringList(runs, " runs"),
0x1);
String[] tlmapnames = new String[] { "FONT", "Empty", "Simple", "Complex" };
tlmapList = new Option.ObjectList(advoptsroot, "maptype", "Map",
tlmapnames,
maps,
new String[] { "font", "empty", "simple", "complex" },
tlmapnames,
0x1);
}
}
/**
* This "virtual Node" implementation is here to maintain backward
* compatibility with older J2DBench releases, specifically those
* options files that were created before we added LCD-optimized text
* hints in JDK 6. This class will translate the text antialias settings
* from the old "taa" On/Off/Both choice into the new expanded version.
*/
private static class TextAAOpt extends Node {
public TextAAOpt() {
super(txoptgraphicsroot, "taa", "Text AntiAlias");
}
public JComponent getJComponent() {
return null;
}
public void restoreDefault() {
// no-op
}
public void write(PrintWriter pw) {
// no-op (the old "taa" choice will be saved as part of the
// new "textaa" option)
}
public String setOption(String key, String value) {
String opts;
if (value.equals("On")) {
opts = "On";
} else if (value.equals("Off")) {
opts = "Off";
} else if (value.equals("Both")) {
opts = "On,Off";
} else {
return "Bad value";
}
return ((Option.ObjectList)taaList).setValueFromString(opts);
}
}
public static class Context {
void init(TestEnvironment env, Result result) {}
void cleanup(TestEnvironment env) {}
}
public static class TextContext extends Context {
Graphics graphics;
String text;
char[] chars;
Font font;
public void init(TestEnvironment env, Result result) {
// graphics
graphics = env.getGraphics();
// text
String sname = (String)env.getModifier(tscriptList);
int slen = env.getIntValue(tlengthList);
text = getString(sname, slen);
// chars
chars = text.toCharArray();
// font
String fname = (String)env.getModifier(fnameList);
if ("Physical".equals(fname)) {
fname = physicalFontNameFor(sname, slen, text);
}
int fstyle = env.getIntValue(fstyleList);
float fsize = ((Float)env.getModifier(fsizeList)).floatValue();
AffineTransform ftx = (AffineTransform)env.getModifier(ftxList);
font = new Font(fname, fstyle, (int)fsize);
if (hasGraphics2D) {
if (fsize != Math.floor(fsize)) {
font = font.deriveFont(fsize);
}
if (!ftx.isIdentity()) {
font = font.deriveFont(ftx);
}
}
// graphics
if (hasGraphics2D) {
Graphics2D g2d = (Graphics2D)graphics;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
env.getModifier(taaList));
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
env.isEnabled(tfmTog)
? RenderingHints.VALUE_FRACTIONALMETRICS_ON
: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
env.isEnabled(gaaTog)
? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.transform((AffineTransform)env.getModifier(gtxList));
}
// set result
result.setUnits(text.length());
result.setUnitName("char");
}
public void cleanup(TestEnvironment env) {
graphics.dispose();
graphics = null;
}
}
public static class G2DContext extends TextContext {
Graphics2D g2d;
FontRenderContext frc;
public void init(TestEnvironment env, Result results){
super.init(env, results);
g2d = (Graphics2D)graphics;
frc = g2d.getFontRenderContext();
}
}
public TextTests(Group parent, String nodeName, String description) {
super(parent, nodeName, description);
addDependency(Destinations.destroot);
addDependencies(txoptroot, true);
}
public Context createContext() {
return new TextContext();
}
public Object initTest(TestEnvironment env, Result result) {
Context ctx = createContext();
ctx.init(env, result);
return ctx;
}
public void cleanupTest(TestEnvironment env, Object ctx) {
((Context)ctx).cleanup(env);
}
static Map physicalMap = new HashMap();
public static String physicalFontNameFor(String textname, int textlen, String text) {
Map lenMap = (Map)physicalMap.get(textname);
if (lenMap == null) {
lenMap = new HashMap();
physicalMap.put(textname, lenMap);
}
Integer key = new Integer(textlen);
Font textfont = (Font)lenMap.get(key);
if (textfont == null) {
Font[] fontsToTry = null;
if (lenMap.isEmpty()) {
fontsToTry = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
} else {
Set fontset = new HashSet();
java.util.Iterator iter = lenMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry e = (Map.Entry)iter.next();
fontset.add(e.getValue());
}
fontsToTry = (Font[])fontset.toArray(new Font[fontset.size()]);
}
Font bestFont = null;
int bestCount = 0;
for (int i = 0; i < fontsToTry.length; ++i) {
Font font = fontsToTry[i];
int count = 0;
for (int j = 0, limit = text.length(); j < limit; ++j) {
if (font.canDisplay(text.charAt(j))) {
++count;
}
}
if (count > bestCount) {
bestFont = font;
bestCount = count;
}
}
textfont = bestFont;
lenMap.put(key, textfont);
}
return textfont.getName();
}
static class FontOption extends ObjectList {
static String[] optionnames = {
"default", "serif", "lucida", "physical"
};
static String[] descnames = {
"Default", "Serif", "Lucida Sans", "Physical"
};
public FontOption(Group parent, String nodeName, String description) {
super(parent, nodeName, description,
optionnames, descnames, optionnames, descnames, 0xa);
}
public String getValString(Object value) {
return value.toString();
}
public String getAbbreviatedModifierDescription(Object value) {
return value.toString();
}
}
}