Java tutorial
/******************************************************************************* * Copyright (c) 2013 Matthew Gruda. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * matthew - initial API and implementation ******************************************************************************/ package terminal.gld; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.util.HashMap; import java.util.Map; import java.awt.GraphicsEnvironment; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; /** * A TrueType font implementation originally for Slick, edited for Bobjob's * Engine * * @original author James Chambers (Jimmy) * @original author Jeremy Adams (elias4444) * @original author Kevin Glass (kevglass) * @original author Peter Korzuszek (genail) * * @new version edited by David Aaron Muhar (bobjob) */ public class TrueTypeFont { public final static int ALIGN_LEFT = 0, ALIGN_RIGHT = 1, ALIGN_CENTER = 2; /** Array that holds necessary information about the font characters */ private IntObject[] charArray = new IntObject[256]; /** Map of user defined font characters (Character <-> IntObject) */ private Map<Character, IntObject> customChars = new HashMap<Character, IntObject>(); /** Boolean flag on whether AntiAliasing is enabled or not */ private boolean antiAlias; /** Font's size */ private int fontSize = 0; /** Font's height */ private int fontHeight = 0; /** Texture used to cache the font 0-255 characters */ private int fontTextureID; /** Default font texture width */ private int textureWidth = 512; /** Default font texture height */ private int textureHeight = 512; /** A reference to Java's AWT Font that we create our font texture from */ private Font font; /** The font metrics for our Java AWT font */ private FontMetrics fontMetrics; private int correctL = 9, correctR = 8; private class IntObject { /** Character's width */ public int width; /** Character's height */ public int height; /** Character's stored x position */ public int storedX; /** Character's stored y position */ public int storedY; } public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) { this.font = font; this.fontSize = font.getSize() + 3; this.antiAlias = antiAlias; createSet(additionalChars); fontHeight -= 1; if (fontHeight <= 0) fontHeight = 1; } public TrueTypeFont(Font font, boolean antiAlias) { this(font, antiAlias, null); } public void setCorrection(boolean on) { if (on) { correctL = 2; correctR = 1; } else { correctL = 0; correctR = 0; } } private BufferedImage getFontImage(char ch) { // Create a temporary image to extract the character's size BufferedImage tempfontImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) tempfontImage.getGraphics(); if (antiAlias == true) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } g.setFont(font); fontMetrics = g.getFontMetrics(); int charwidth = fontMetrics.charWidth(ch) + 8; if (charwidth <= 0) { charwidth = 7; } int charheight = fontMetrics.getHeight() + 3; if (charheight <= 0) { charheight = fontSize; } // Create another image holding the character we are creating BufferedImage fontImage; fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB); Graphics2D gt = (Graphics2D) fontImage.getGraphics(); if (antiAlias == true) { gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } gt.setFont(font); gt.setColor(Color.WHITE); int charx = 3; int chary = 1; gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent()); return fontImage; } private void createSet(char[] customCharsArray) { // If there are custom chars then I expand the font texture // twice if (customCharsArray != null && customCharsArray.length > 0) { textureWidth *= 2; } // In any case this should be done in other way. Texture with // size 512x512 // can maintain only 256 characters with resolution of 32x32. // The texture // size should be calculated dynamicaly by looking at character // sizes. try { BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) imgTemp.getGraphics(); g.setColor(new Color(0, 0, 0, 1)); g.fillRect(0, 0, textureWidth, textureHeight); int rowHeight = 0; int positionX = 0; int positionY = 0; int customCharsLength = (customCharsArray != null) ? customCharsArray.length : 0; for (int i = 0; i < 256 + customCharsLength; i++) { // get 0-255 characters and then custom // characters char ch = (i < 256) ? (char) i : customCharsArray[i - 256]; BufferedImage fontImage = getFontImage(ch); IntObject newIntObject = new IntObject(); newIntObject.width = fontImage.getWidth(); newIntObject.height = fontImage.getHeight(); if (positionX + newIntObject.width >= textureWidth) { positionX = 0; positionY += rowHeight; rowHeight = 0; } newIntObject.storedX = positionX; newIntObject.storedY = positionY; if (newIntObject.height > fontHeight) { fontHeight = newIntObject.height; } if (newIntObject.height > rowHeight) { rowHeight = newIntObject.height; } // Draw it here g.drawImage(fontImage, positionX, positionY, null); positionX += newIntObject.width; if (i < 256) { // standard characters charArray[i] = newIntObject; } else { // custom characters customChars.put(new Character(ch), newIntObject); } fontImage = null; } fontTextureID = loadImage(imgTemp); // .getTexture(font.toString(), imgTemp); } catch (Exception e) { System.err.println("Failed to create font."); e.printStackTrace(); } } private void drawQuad(float drawX, float drawY, float drawX2, float drawY2, float srcX, float srcY, float srcX2, float srcY2) { float DrawWidth = drawX2 - drawX; float DrawHeight = drawY2 - drawY; float TextureSrcX = -.01f + srcX / textureWidth; float TextureSrcY = srcY / textureHeight; float SrcWidth = srcX2 - srcX; float SrcHeight = srcY2 - srcY; float RenderWidth = (SrcWidth / textureWidth); float RenderHeight = (SrcHeight / textureHeight); GL11.glTexCoord2f(TextureSrcX, TextureSrcY); GL11.glVertex3f(drawX, drawY, .1f); GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight); GL11.glVertex3f(drawX, drawY + DrawHeight, .1f); GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight); GL11.glVertex3f(drawX + DrawWidth, drawY + DrawHeight, .1f); GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY); GL11.glVertex3f(drawX + DrawWidth, drawY, .1f); } public int getWidth(String whatchars) { int totalwidth = 0; IntObject intObject = null; int currentChar = 0; for (int i = 0; i < whatchars.length(); i++) { currentChar = whatchars.charAt(i); if (currentChar < 256) { intObject = charArray[currentChar]; } else { intObject = customChars.get(new Character((char) currentChar)); } if (intObject != null) totalwidth += intObject.width; } return totalwidth; } public int getHeight() { return fontHeight; } public int getHeight(String HeightString) { return fontHeight; } public int getLineHeight() { return fontHeight; } public void drawString(float x, float y, String whatchars, float scaleX, float scaleY) { drawString(x, y, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, ALIGN_LEFT, null); } public void drawString(float x, float y, String whatchars, float scaleX, float scaleY, int format) { drawString(x, y, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, format, null); } public void drawString(float x, float y, String whatchars, int startIndex, int endIndex, float scaleX, float scaleY, int format, Color[] colarr) { IntObject intObject = null; int charCurrent; int totalwidth = 0; int i = startIndex, d, c; float startY = 0; switch (format) { case ALIGN_RIGHT: { d = -1; c = correctR; while (i < endIndex) { if (whatchars.charAt(i) == '\n') startY -= fontHeight; i++; } break; } case ALIGN_CENTER: { for (int l = startIndex; l <= endIndex; l++) { charCurrent = whatchars.charAt(l); if (charCurrent == '\n') break; if (charCurrent < 256) { intObject = charArray[charCurrent]; } else { intObject = customChars.get(new Character((char) charCurrent)); } totalwidth += intObject.width - correctL; } totalwidth /= -2; } case ALIGN_LEFT: default: { d = 1; c = correctL; break; } } GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID); GL11.glBegin(GL11.GL_QUADS); boolean usecol = colarr != null && colarr.length == whatchars.length(); while (i >= startIndex && i <= endIndex) { charCurrent = whatchars.charAt(i); if (charCurrent < 256) { intObject = charArray[charCurrent]; } else { intObject = customChars.get(new Character((char) charCurrent)); } if (intObject != null) { if (d < 0) totalwidth += (intObject.width - c) * d; if (charCurrent == '\n') { startY -= fontHeight * d; totalwidth = 0; if (format == ALIGN_CENTER) { for (int l = i + 1; l <= endIndex; l++) { charCurrent = whatchars.charAt(l); if (charCurrent == '\n') break; if (charCurrent < 256) { intObject = charArray[charCurrent]; } else { intObject = customChars.get(new Character((char) charCurrent)); } totalwidth += intObject.width - correctL; } totalwidth /= -2; } // if center get next lines total // width/2; } else { if (usecol) { Color col = colarr[i]; GL11.glColor4f(col.getRed() / 255f, col.getGreen() / 255f, col.getBlue() / 255f, col.getAlpha() / 255f); } drawQuad((totalwidth + intObject.width) * scaleX + x, startY * scaleY + y, totalwidth * scaleX + x, (startY + intObject.height) * scaleY + y, intObject.storedX + intObject.width, intObject.storedY + intObject.height, intObject.storedX, intObject.storedY); if (d > 0) totalwidth += (intObject.width - c) * d; } i += d; } } GL11.glEnd(); } public static int loadImage(BufferedImage bufferedImage) { try { short width = (short) bufferedImage.getWidth(); short height = (short) bufferedImage.getHeight(); // textureLoader.bpp = // bufferedImage.getColorModel().hasAlpha() ? (byte)32 : // (byte)24; int bpp = (byte) bufferedImage.getColorModel().getPixelSize(); ByteBuffer byteBuffer; DataBuffer db = bufferedImage.getData().getDataBuffer(); if (db instanceof DataBufferInt) { int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData(); byte newI[] = new byte[intI.length * 4]; for (int i = 0; i < intI.length; i++) { byte b[] = intToByteArray(intI[i]); int newIndex = i * 4; newI[newIndex] = b[1]; newI[newIndex + 1] = b[2]; newI[newIndex + 2] = b[3]; newI[newIndex + 3] = b[0]; } byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()) .put(newI); } else { byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()) .put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData()); } byteBuffer.flip(); int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA; IntBuffer textureId = BufferUtils.createIntBuffer(1); ; GL11.glGenTextures(textureId); GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0)); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width, height, format, GL11.GL_UNSIGNED_BYTE, byteBuffer); return textureId.get(0); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } return -1; } public static boolean isSupported(String fontname) { Font font[] = getFonts(); for (int i = font.length - 1; i >= 0; i--) { if (font[i].getName().equalsIgnoreCase(fontname)) return true; } return false; } public static Font[] getFonts() { return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); } public static byte[] intToByteArray(int value) { return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value }; } public void destroy() { IntBuffer scratch = BufferUtils.createIntBuffer(1); scratch.put(0, fontTextureID); GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); GL11.glDeleteTextures(scratch); } }