/* * Java port of Bullet (c) 2008 Martin Dvorak * * Bullet Continuous Collision Detection and Physics Library * Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ //package com.bulletphysics.demos.opengl; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.font.*; import java.awt.geom.Rectangle2D; import java.awt.image.*; import java.io.*; import java.nio.*; import java.util.Hashtable; import org.lwjgl.BufferUtils; import javax.media.opengl.GL; import javax.media.opengl.glu.GLU; //import static org.lwjgl.opengl.GL11.*; //import static org.lwjgl.opengl.GL13.*; //import static org.lwjgl.opengl.ARBTextureCompression.*; //import static org.lwjgl.util.glu.GLU.*; /** * * @author jezek2 */ public class FontRender { //private static final File cacheDir = new File("/path/to/font/cache/dir/"); private FontRender() { } protected static class Glyph { int x,y,w,h; int list = -1; } public static class GLFont { protected int texture; protected int width, height; protected Glyph[] glyphs = new Glyph[128-32]; public GLFont() { for (int i=0; i= 36) imgw <<= 1; if (font.getSize() >= 72) imgw <<= 1; //BufferedImage img = new BufferedImage(imgw, 1024, BufferedImage.TYPE_INT_ARGB); BufferedImage img = createImage(imgw, 1024, true); Graphics2D g = (Graphics2D)img.getGraphics(); if (antialiasing) { g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } g.setColor(Color.WHITE); g.setFont(font); int x=0, y=0,rowsize=0; for (int c=32; c<128; c++) { String s = ""+(char)c; Rectangle2D rect = font.getStringBounds(s, frc); LineMetrics lm = font.getLineMetrics(s, frc); int w = (int)rect.getWidth()+1; int h = (int)rect.getHeight()+2; if (x+w+2 > img.getWidth()) { x = 0; y += rowsize; rowsize = 0; } g.drawString(s, x+1, y+(int)lm.getAscent()+1); if (glyphs != null) { glyphs[c-32].x = x+1; glyphs[c-32].y = y+1; glyphs[c-32].w = w; glyphs[c-32].h = h; } w += 2; h += 2; x += w; rowsize = Math.max(rowsize, h); } y += rowsize; g.dispose(); if (y < 128) img = img.getSubimage(0, 0, img.getWidth(), 128); else if (y < 256) img = img.getSubimage(0, 0, img.getWidth(), 256); else if (y < 512) img = img.getSubimage(0, 0, img.getWidth(), 512); return img; } private static void renderGlyph(GL gl, GLFont font, Glyph g) { if (g.list != -1) { gl.glCallList(g.list); return; } g.list = gl.glGenLists(1); gl.glNewList(g.list, gl.GL_COMPILE); float tw = font.width; float th = font.height; int x=0, y=0; gl.glBegin(gl.GL_QUADS); gl.glTexCoord2f((float)(g.x)/tw, (float)(g.y)/th); gl.glVertex3f(x, y, 1); gl.glTexCoord2f((float)(g.x+g.w-1)/tw, (float)(g.y)/th); gl.glVertex3f(x+g.w-1, y, 1); gl.glTexCoord2f((float)(g.x+g.w-1)/tw, (float)(g.y+g.h-1)/th); gl.glVertex3f(x+g.w-1, y+g.h-1, 1); gl.glTexCoord2f((float)(g.x)/tw, (float)(g.y+g.h-1)/th); gl.glVertex3f(x, y+g.h-1, 1); gl.glEnd(); gl.glEndList(); gl.glCallList(g.list); } public static void drawString(GL gl, GLFont font, CharSequence s, int x, int y, float red, float green, float blue) { drawString(gl, font, s, x, y, red, green, blue, 1); } public static void drawString(GL gl, GLFont font, CharSequence s, int x, int y, float red, float green, float blue, float alpha) { gl.glEnable(gl.GL_BLEND); gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA); gl.glPushMatrix(); gl.glTranslatef(x, y, 0); gl.glBindTexture(gl.GL_TEXTURE_2D, font.texture); gl.glEnable(gl.GL_TEXTURE_2D); gl.glColor4f(red, green, blue, alpha); //glColor4f(1, 1, 1, 1); for (int i=0, n=s.length(); i 128) c = '?'; Glyph g = font.glyphs[c-32]; renderGlyph(gl,font, g); //x += g.w; //glTranslatef(g.w, 0, 0); gl.glTranslatef(g.w-2, 0, 0); } gl.glDisable(gl.GL_TEXTURE_2D); gl.glPopMatrix(); gl.glDisable(gl.GL_BLEND); } private static ColorModel glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,0}, false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE); private static ColorModel glColorModelAlpha = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,8}, true, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE); private static int createTexture(GL gl, GLU glu, BufferedImage img, boolean mipMap) { boolean USE_COMPRESSION = false; int[] id = new int[1]; gl.glGenTextures(1, IntBuffer.wrap(id)); int tex = id[0]; gl.glBindTexture(gl.GL_TEXTURE_2D, tex); gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, mipMap? gl.GL_LINEAR_MIPMAP_LINEAR : gl.GL_LINEAR); gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR); byte[] data = ((DataBufferByte)img.getRaster().getDataBuffer()).getData(); ByteBuffer buf = ByteBuffer.allocateDirect(data.length); buf.order(ByteOrder.nativeOrder()); buf.put(data, 0, data.length); buf.flip(); boolean alpha = img.getColorModel().hasAlpha(); //glTexImage2D(GL_TEXTURE_2D, 0, alpha? GL_RGBA:GL_RGB, img.getWidth(), img.getHeight(), 0, alpha? GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, buf); gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, USE_COMPRESSION? (alpha? gl.GL_COMPRESSED_RGBA:gl.GL_COMPRESSED_RGB) : (alpha? gl.GL_RGBA:gl.GL_RGB), img.getWidth(), img.getHeight(), 0, alpha? gl.GL_RGBA:gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf); if (mipMap) { glu.gluBuild2DMipmaps(gl.GL_TEXTURE_2D, USE_COMPRESSION? (alpha? gl.GL_COMPRESSED_RGBA:gl.GL_COMPRESSED_RGB) : (alpha? gl.GL_RGBA:gl.GL_RGB), img.getWidth(), img.getHeight(), alpha? gl.GL_RGBA:gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf); //gluBuild2DMipmaps(GL_TEXTURE_2D, GL_COMPRESSED_RGB, img.getWidth(), img.getHeight(), GL_RGB, GL_UNSIGNED_BYTE, buf); } return tex; } private static BufferedImage createImage(int width, int height, boolean alpha) { if (alpha) { WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 4, null); return new BufferedImage(glColorModelAlpha, raster, false, new Hashtable()); } WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 3, null); return new BufferedImage(glColorModel, raster, false, new Hashtable()); } }