package org.goko.tools.viewer.jogl.utils.render.text.v2; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; import javax.media.opengl.GL3; import org.goko.core.common.exception.GkException; import org.goko.core.common.exception.GkTechnicalException; import org.goko.core.log.GkLog; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; /** * Bitmap font file parser * * Documentation * http://www.angelcode.com/products/bmfont/doc/file_format.html * * @author Psyko */ public class BitmapFontFile { private static final GkLog LOG = GkLog.getLogger(BitmapFontFile.class); private Map<Integer, CharBlock> mapChars; private Map<Integer, PageBlock> mapPages; private int textureWidth; private int textureHeight; private Texture texture; private ByteBuffer buffer; private int lineHeight; private int base; private int size; protected CharBlock getCharacterInfo(char character){ return mapChars.get((int)character); } protected void load(String bffFileName) throws GkException{ try{ InputStream inputStream = getClass().getResourceAsStream("/resources/font/Y145M-2009.fnt");//new FileInputStream(f); mapChars = new HashMap<Integer, CharBlock>(); mapPages = new HashMap<Integer, PageBlock>(); loadFileIdentifier(inputStream); loadInfoBlock(inputStream); loadCommonBlock(inputStream); loadPagesBlock(inputStream); loadCharsBlock(inputStream); }catch(Exception e){ throw new GkTechnicalException(e); } } protected void loadBuffer(String imageFile) throws IOException{ //LOG.debug("Loading image "+imageFile); // open image InputStream imgPath = getClass().getResourceAsStream("/resources/font/"+imageFile); BufferedImage bufferedImage = ImageIO.read(imgPath); // get DataBufferBytes from Raster WritableRaster raster = bufferedImage .getRaster(); DataBufferByte data = (DataBufferByte) raster.getDataBuffer(); buffer = ByteBuffer.wrap(data.getData()); } /** * Loads the file identifier * @param inputStream the {@link InputStream} * @throws IOException IOException */ private void loadFileIdentifier(InputStream inputStream) throws IOException { byte[] identifier = new byte[3]; inputStream.read(identifier); //LOG.debug("File identifier : "+ String.valueOf(identifier)); byte[] version = new byte[1]; inputStream.read(version); //LOG.debug("File version : "+ String.valueOf((int)version[0])); } /** * Loads data about the information in the bitmap file * @param inputStream the {@link InputStream} * @throws IOException IOException */ private void loadInfoBlock(InputStream inputStream) throws IOException { ByteBuffer data = getBlockData(inputStream); size = Math.abs(getInt16(data)); //LOG.debug("Font size :"+size); } /** * Loads commons data in the bitmap file * @param inputStream the {@link InputStream} * @throws IOException IOException */ private void loadCommonBlock(InputStream inputStream) throws IOException { ByteBuffer data = getBlockData(inputStream); // lineHeight 2 uint 0 // base 2 uint 2 // scaleW 2 uint 4 // scaleH 2 uint 6 // pages 2 uint 8 // bitField 1 bits 10 bits 0-6: reserved, bit 7: packed // alphaChnl 1 uint 11 // redChnl 1 uint 12 // greenChnl 1 uint 13 // blueChnl 1 uint 14 lineHeight = getUint16(data); base = getUint16(data); textureWidth = getUint16(data); textureHeight = getUint16(data); } /** * Loads data about the pages in the bitmap file * @param inputStream the {@link InputStream} * @throws IOException IOException */ private void loadPagesBlock(InputStream inputStream) throws IOException { ByteBuffer data = getBlockData(inputStream); int size = data.limit(); int id = 0; StringBuffer buffer = new StringBuffer(); for (int i = 0; i < size; i++) { byte c = data.get(); if(c == 0){ // Page detected mapPages.put(id, new PageBlock(id, buffer.toString())); //LOG.debug("Detected image "+buffer.toString()); loadBuffer(mapPages.get(id).getFile()); id++; buffer.setLength(0); }else{ buffer.append((char)c); } } } /** * Loads data about the characters in the bitmap file * @param inputStream the {@link InputStream} * @throws IOException IOException */ private void loadCharsBlock(InputStream inputStream) throws IOException { ByteBuffer data = getBlockData(inputStream); int nbChars = data.limit() / 20; //LOG.debug("Font line height : "+lineHeight); for (int i = 0; i < nbChars; i++) { int id = getUint32(data); int x = getUint16(data); int y = getUint16(data); int width = getUint16(data); int height = getUint16(data); int xOffset = getUint16(data); int yOffset = getUint16(data); int xAdvance= getUint16(data); int page = getUint8(data); int chnl = getUint8(data); mapChars.put(id, new CharBlock(id, x, y, width, height, xOffset, yOffset, xAdvance, page, chnl)); ////LOG.debug((char)id+", id:"+id+", x:"+x+", y:"+ y+", width:"+ width+", height:"+ height+", xOffset:"+ xOffset+", yOffset:"+ yOffset+", xAdvance:"+ xAdvance+", page:"+page+", chnl:"+chnl); } } /** * Builds the ByteBuffer using the bloc kdata information at the beginning of the given input stream * @param inputStream the input stream * @return ByteBuffer * @throws IOException IOException */ private ByteBuffer getBlockData(InputStream inputStream) throws IOException{ byte[] blockHeader = new byte[5]; inputStream.read(blockHeader); ByteBuffer data = ByteBuffer.wrap(blockHeader); int blockId = getUint8(data); int length = getUint32(data); byte[] blockData = new byte[length]; inputStream.read(blockData); return ByteBuffer.wrap(blockData); } /** * Extract a signed int in this buffer using the 4 first bytes * @param buffer the buffer to extract data from * @return an int */ public static int getInt32(ByteBuffer buffer) { byte[] val = new byte[4]; buffer.get(val); return val[3] << 24 | val[2] << 16 | val[1] << 8 | val[0]; } /** * Extract a signed unsigned int in this buffer using the 4 first bytes * @param buffer the buffer to extract data from * @return an int */ private int getUint32(ByteBuffer buffer) { byte[] val = new byte[4]; buffer.get(val); return (val[3] & 0xFFFFFFFF) << 24 | (val[2]& 0xFFFFFF) << 16 | (val[1]& 0xFFFF) << 8 | (val[0] & 0xFF); } /** * Extract a signed int in this buffer using the 2 first bytes * @param buffer the buffer to extract data from * @return an int */ private int getInt16(ByteBuffer buffer) { byte[] val = new byte[2]; buffer.get(val); return val[1] << 8 | val[0]; } /** * Extract an unsigned int in this buffer using the 2 first bytes * @param buffer the buffer to extract data from * @return an int */ private int getUint16(ByteBuffer buffer) { byte[] val = new byte[2]; buffer.get(val); return (val[1]& 0xFFFF) << 8 | (val[0] & 0xFF); } /** * Extract a signed int in this buffer using the 1 first byte * @param buffer the buffer to extract data from * @return an int */ private int getInt8(ByteBuffer buffer) { return buffer.get(); } /** * Extract a unsigned int in this buffer using the 2 first bytes * @param buffer the buffer to extract data from * @return an int */ private int getUint8(ByteBuffer buffer) { return buffer.get() & 0xFF; } /** * Bodge to get unsigned byte values * @param val the value * @return the uint value */ private int getUnsignedByteVal(byte val) { return val & 0xFF; } /** * @return the textureWidth */ public int getTextureWidth() { return textureWidth; } /** * @param textureWidth the textureWidth to set */ public void setTextureWidth(int textureWidth) { this.textureWidth = textureWidth; } /** * @return the textureHeight */ public int getTextureHeight() { return textureHeight; } /** * @param textureHeight the textureHeight to set */ public void setTextureHeight(int textureHeight) { this.textureHeight = textureHeight; } /** * @param gl * @return */ public Texture getTexture(GL3 gl) { if(texture == null){ TextureData tData = new TextureData(gl.getGLProfile(), //GLProfile glp, GL3.GL_RGBA, //int internalFormat, getTextureWidth(), //int width, getTextureHeight(), //int height, 0, //int border, GL3.GL_RGBA, //int pixelFormat, GL3.GL_UNSIGNED_BYTE, //int pixelType, false, //boolean mipmap, false, //boolean dataIsCompressed, false, //boolean mustFlipVertically, getBuffer(), //Buffer buffer, null); //TextureData.Flusher flusher) texture = new Texture(gl, tData); } return texture; } /** * @return the buffer */ public ByteBuffer getBuffer() { return buffer; } /** * @return the lineHeight */ public int getLineHeight() { return lineHeight; } /** * @param lineHeight the lineHeight to set */ public void setLineHeight(int lineHeight) { this.lineHeight = lineHeight; } /** * @return the base */ public int getBase() { return base; } /** * @param base the base to set */ public void setBase(int base) { this.base = base; } /** * @return the size */ public int getSize() { return size; } /** * @param size the size to set */ public void setSize(int size) { this.size = size; } }