package robombs.game.util; import java.awt.Color; import java.awt.Dimension; 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; import java.util.SortedMap; import java.util.TreeMap; import com.threed.jpct.FrameBuffer; /** * <p>creates GL renderable (blittable) font out of given AWT font. * a jPCT texture is created and added to TextureManager on the fly.</p> * * <p>in contrast with its name, this class can be used for software renderer too. * but to tell the truth, i would stick to Java2D for software renderer ;)</p> * * this class uses {@link TexturePack} behind the scenes. * * @see TexturePack * * @author hakan eryargi (r a f t) */ public class GLFont { /** standard characters */ public static final String ENGLISH = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`1234567890-=~!@#$%^&*()_+[]{}\\|:;\"'<>,.?/"; /** German specific characters */ public static final String GERMAN = new String(new char[] { '\u00c4', '\u00D6', '\u00DC', '\u00E4', '\u00F6', '\u00FC', '\u00DF' }); /** French specific characters */ public static final String FRENCH = new String(new char[] { '\u00C0', '\u00C2', '\u00C6', '\u00C8', '\u00C9', '\u00CA', '\u00CB', '\u00CE', '\u00CF', '\u00D4', '\u0152', '\u00D9', '\u00DB', '\u00DC', '\u0178', '\u00C7', '\u00E0', '\u00E2', '\u00E6', '\u00E8', '\u00E9', '\u00EA', '\u00EB', '\u00EE', '\u00EF', '\u00F4', '\u0153', '\u00F9', '\u00FB', '\u00FC', '\u00FF', '\u00E7' }); /** Turkish specific characters */ public static final String TURKISH = new String(new char[] { '\u00e7', '\u00c7', '\u011f', '\u011e', '\u0131', '\u0130', '\u00f6', '\u00d6', '\u015f', '\u015e', '\u00fc', '\u00dc' }); /** * same as getGLFont(font, ENGLISH) * @see #getGLFont(Font, String) * */ public static GLFont getGLFont(Font font) { return getGLFont(font, ENGLISH); } /** returns cached font or creates a new one caches and returns it. * note: cache mechanism doesnt take alphabet into account. */ public static GLFont getGLFont(Font font, String alphabet) { String key = font.getName() + "/" + font.getSize() + "/" + font.getStyle(); synchronized (FONTS) { GLFont glFont = FONTS.get(key); if (glFont == null) { glFont = new GLFont(font, alphabet); FONTS.put(key, glFont); } return glFont; } } /** our font cache */ private static SortedMap<String, GLFont> FONTS = new TreeMap<String, GLFont>(); /** the awt font */ public final Font font; /** characters this GLFont is created for */ public final String alphabet; /** regular font height. note some special characters may not fit into this height. * see {@link FontMetrics} for a discussion */ public final int fontHeight; private final int baseline; private final int[] charWidths; private final Dimension stringBounds = new Dimension(); private final TexturePack pack = new TexturePack(); /** * creates a GLFont for given awt Font consists of default characters. * @see #ENGLISH */ public GLFont(Font font) { this(font, ENGLISH); } /** * creates a GLFont for given awt Font consists of characters in given alphabet * @param font the awt font * @param alphabet characters of our alphabet */ public GLFont(Font font, String alphabet) { this.font = font; this.alphabet = eliminateDuplicates(alphabet); this.charWidths = new int[alphabet.length()]; Graphics2D g2d = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); FontMetrics fontMetrics = g2d.getFontMetrics(font); this.fontHeight = fontMetrics.getHeight(); this.baseline = fontMetrics.getMaxAscent(); int height = fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); for (int i = 0; i < alphabet.length(); i++) { String c = alphabet.substring(i, i + 1); Rectangle2D bounds = fontMetrics.getStringBounds(c, g2d); int width = (int) bounds.getWidth(); charWidths[i] = width; BufferedImage charImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D charGraphics = charImage.createGraphics(); charGraphics.setRenderingHints(g2d.getRenderingHints()); charGraphics.setFont(font); charGraphics.setColor(Color.WHITE); charGraphics.drawString(c, 0, baseline); charGraphics.dispose(); pack.addImage(charImage); } pack.pack(TexturePack.ALPHA_USE); } private String eliminateDuplicates(String s) { StringBuilder sb = new StringBuilder(s); for (int i = 0; i < sb.length(); i++) { String c = sb.substring(i, i + 1); int next = -1; while ((next = sb.indexOf(c, i + 1)) != -1) { sb.deleteCharAt(next); } } return sb.toString(); } /** * returns how much area given string occupies. note this method always * returns same Dimension instance */ public Dimension getStringBounds(String s) { int width = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); int index = alphabet.indexOf(c); if (index == -1) index = alphabet.indexOf('?'); if (index != -1) { width += charWidths[index]; } } stringBounds.setSize(width, fontHeight); return stringBounds; } /** * blits given string to frame buffer. works very similar to * awt.Graphics#drawString(..) that is: x coordinate is left most point in * string, y is baseline * * @param buffer * buffer to blit into * @param s * string to blit * @param x * leftmost point * @param transparency * transparency value, make sure >= 0 * @param color * text color * @param y * baseline */ public void blitString(FrameBuffer buffer, String s, int x, int y, int transparency, Color color) { y -= baseline; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); int index = alphabet.indexOf(c); if (index == -1) index = alphabet.indexOf('?'); if (index != -1) { Dimension size = pack.blit(buffer, index, x, y, transparency, false, color); x += size.width; } } } }