Subversion Repositories AndroidProjects

Rev

Rev 421 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.gebauz.tools;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Vector;

import javax.imageio.ImageIO;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.gebauz.Bauzoid.file.FileUtil;
import com.gebauz.Bauzoid.file.FileWriter;
import com.gebauz.Bauzoid.parser.ScanException;
import com.gebauz.Bauzoid.parser.Tokenizer;

public class FontConverter
{
        public static final String LOG_TAG = "FontPot";
        public static boolean verbose = true;
       
        private static class CharacterRect
        {
                public float x;
                public float y;
                public float w;
                public float h;
               
                public CharacterRect(float _x, float _y, float _w, float _h)
                {
                        x = _x;
                        y = _y;
                        w = _w;
                        h = _h;
                }
        }
       
        private static class CharacterOffset
        {
                public float x;
                public float y;
               
                public CharacterOffset(float _x, float _y)
                {
                        x = _x;
                        y = _y;
                }
        }
       
        private static class KerningPair
        {
                public char first;
                public char second;
               
                public KerningPair(char a, char b)
                {
                        first = a;
                        second = b;
                }
        }
       
        public static int getNextPot(int number)
        {
                int pot = 2;
                while (pot < number)
                {
                        pot *= 2;
                }
                return pot;
        }
       
        public static void convertFont(FileHandle file) throws ScanException
        {
                String fileContents = file.readString();
               
                Vector<Character> charList = null;
                Vector<Float> widthList = null;
                Vector<CharacterRect> rectList = null;
                Vector<CharacterOffset> offsetList = null;
                Vector<KerningPair> kerningPairs = new Vector<KerningPair>();
                Vector<Integer> kerningValues = new Vector<Integer>();
       
                String textureName = "";
                String texturePath = "";
                float ascent = 1.0f;
                float multiplier = 1.0f;
               
                BufferedImage srcImage = null;
               
                boolean convertGreyScaleToAlpha = true;

                //Font font = new Font(graphics);
                //Texture texture = null;
               
                try
                {
                        Tokenizer tokenizer = new Tokenizer(fileContents);
                        tokenizer.setStringDelimiter(new char[] {'\'', '"'} );
                       
                        while (!tokenizer.checkNoMoreTokens())
                        {
                                String identifier = tokenizer.readIdentifier();
                               
                                if (identifier.equalsIgnoreCase("Define"))
                                {
                                        // Define
                                        String define = tokenizer.readIdentifier();
                                       
                                        if (define.equalsIgnoreCase("CharList"))
                                        {
                                                charList = parseCharList(tokenizer);
                                        }
                                        else if (define.equalsIgnoreCase("WidthList"))
                                        {
                                                widthList = parseWidthList(tokenizer);
                                        }
                                        else if (define.equalsIgnoreCase("RectList"))
                                        {
                                                rectList = parseRectList(tokenizer);
                                        }
                                        else if (define.equalsIgnoreCase("OffsetList"))
                                        {
                                                offsetList = parseOffsetList(tokenizer);
                                        }
                                        else if (define.equalsIgnoreCase("KerningPairs"))
                                        {
                                                kerningPairs = parseKerningPairs(tokenizer);                                           
                                        }
                                        else if (define.equalsIgnoreCase("KerningValues"))
                                        {
                                                kerningValues = parseKerningValues(tokenizer);
                                        }
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("CreateLayer"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetImage"))
                                {
                                        tokenizer.readIdentifier();
                                        // TODO: actually extract path from given file
                                        textureName = tokenizer.readString();
                                        texturePath = FileUtil.extractPath(file.path()) + "/_" + textureName + ".png";
                                       
                                        try
                                        {
                                                srcImage = ImageIO.read(new File(texturePath));
                                        }
                                        catch (IOException e)
                                        {
                                                throw new ScanException(LOG_TAG, "IOException: could not read file '" + texturePath + "': " + e.getMessage());
                                        }
                                       
                                        //texture = new Texture(Gdx.files.internal(filename));
                                       
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerRegionMultiplier"))
                                {
                                        tokenizer.readIdentifier();
                                       
                                        multiplier = tokenizer.readNumber();
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetAscent"))
                                {
                                        tokenizer.readIdentifier();
                                        ascent = tokenizer.readNumber();
                                        //font.setAscent(ascent);
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetCharWidths"))
                                {
                                        tokenizer.readIdentifier();
                                        if (tokenizer.checkIdentifier())
                                        {
                                                tokenizer.readIdentifier();
                                                tokenizer.readIdentifier();
                                        }
                                        else if (tokenizer.checkToken("("))
                                        {
                                                Vector<Character> charListExtra = parseCharList(tokenizer);
                                                Vector<Float> widthListExtra = parseWidthList(tokenizer);
                                               
                                                charList.addAll(charListExtra);
                                                widthList.addAll(widthListExtra);
                                        }
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetImageMap"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetCharOffsets"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readToken(";");
                                }              
                                else if (identifier.equalsIgnoreCase("LayerSetAscentPadding"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readNumber();
                                        tokenizer.readToken(";");
                                }              
                                else if (identifier.equalsIgnoreCase("LayerSetLineSpacingOffset"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readNumber();
                                        tokenizer.readToken(";");
                                }              
                                else if (identifier.equalsIgnoreCase("LayerSetPointSize"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readNumber();
                                        tokenizer.readToken(";");
                                }              
                                else if (identifier.equalsIgnoreCase("SetDefaultPointSize"))
                                {
                                        tokenizer.readNumber();
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("LayerSetKerningPairs"))
                                {
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readIdentifier();
                                        tokenizer.readToken(";");
                                }
                                else if (identifier.equalsIgnoreCase("ConvertGreyScaleToAlpha"))
                                {
                                        int num = (int)tokenizer.readNumber();
                                       
                                        convertGreyScaleToAlpha = num != 0;
                                       
                                        tokenizer.readToken(";");
                                }
                        }
                       
                }
                catch (ScanException ex)
                {
                        ex.log(LOG_TAG);
                }
               
                {
                        // checks
                        if (srcImage == null)
                        {
                                throw new ScanException(LOG_TAG, "No font texture specified!");
                        }
               
                        if (widthList.size() != charList.size())
                        {
                                throw new ScanException(LOG_TAG, "Character and Width list do not match in size!");                            
                        }                                      
                       
                        if (rectList.size() != offsetList.size())
                        {
                                throw new ScanException(LOG_TAG, "Rect and Offset Lists do not match in size!");
                        }
                       
                        if (kerningValues.size() != kerningPairs.size())
                        {
                                throw new ScanException(LOG_TAG, "Kerning Values list and Kerning Pairs list do not match in size!");
                        }
                }
               
                int type = srcImage.getType();
               
                final int MAX_WIDTH = 512;
                final int CHAR_MARGINS = 1;
               
                int maxCharHeight = 0;
                int w = 0;
                int numRows = 1;
               
                // get max height of characters
                for (int i = 0; i < rectList.size(); i++)
                {
                        CharacterRect ch = rectList.elementAt(i);
                       
                        if (ch.h > maxCharHeight)
                                maxCharHeight = (int)ch.h;
                       
                        // accumulate to find out how many rows we need
                        if ((w + ch.w + CHAR_MARGINS) > MAX_WIDTH)
                        {
                                w = 0;
                                numRows++;
                        }
                       
                        w += ch.w + CHAR_MARGINS;
                }
               
                int finalTextureWidth = MAX_WIDTH;
                int finalTextureHeight = getNextPot(maxCharHeight * numRows);
               
                BufferedImage destImage = new BufferedImage(finalTextureWidth, finalTextureHeight, BufferedImage.TYPE_4BYTE_ABGR);
               
                Vector<CharacterRect> rectListNew = new Vector<CharacterRect>();
               
                int dx = 0;
                int dy = 0;
                for (int i = 0; i < rectList.size(); i++)
                {
                        CharacterRect ch = rectList.elementAt(i);

                        // if this character exceeds max width, start new line
                        if ((dx + ch.w + CHAR_MARGINS) > MAX_WIDTH)
                        {
                                dx = 0;
                                dy += maxCharHeight;
                        }

                        copyRect(srcImage, destImage, (int)ch.x, (int)ch.y, (int)ch.w, (int)ch.h, dx, dy, convertGreyScaleToAlpha);
                        rectListNew.add(new CharacterRect(dx, dy, ch.w, ch.h));
                       
                        dx += ch.w + CHAR_MARGINS;                     
                }
               
                try
                {
                        File outputfile = new File(FileUtil.extractPath(file.path()) + "/" + textureName + ".png");
                        ImageIO.write(destImage, "png", outputfile);
                }
                catch (IOException e)
                {
                        throw new ScanException(LOG_TAG, "Could not write output file!");
                }
               
                // finally, write binary font file
                String fontFileName = FileUtil.extractPath(file.path()) + "/" + file.nameWithoutExtension() + ".bzf";
                               
                try
                {
                        FileHandle f = Gdx.files.absolute(fontFileName);
                       
                        FileWriter fw = new FileWriter(f.write(false));
                       
                        fw.writeInt(100);                       // Version Tag (int)
                        fw.writeString(textureName + ".png");   // Texture name (String)
                        fw.writeFloat(ascent);          // Ascent (float)
                        fw.writeFloat(multiplier);      // Multiplier (float)
                       
                        // Write character list (char * num)
                        fw.writeInt(charList.size()); // CharList size (int)
                        for (int i = 0; i < charList.size(); i++)
                        {
                                Character c = charList.elementAt(i);
                                fw.writeChar(c);
                        }
                       
                        // Write width list (float * num)
                        fw.writeInt(widthList.size()); // widthList size (int)
                        for (int i = 0; i < widthList.size(); i++)
                        {
                                Float width = widthList.elementAt(i);
                                fw.writeFloat(width);
                        }
                       
                        // Write rect list (CharRect * num)
                        fw.writeInt(rectListNew.size()); // rectList size (int)
                        for (int i = 0; i < rectListNew.size(); i++)
                        {
                                CharacterRect r = rectListNew.elementAt(i);
                                fw.writeFloat(r.x);
                                fw.writeFloat(r.y);
                                fw.writeFloat(r.w);
                                fw.writeFloat(r.h);
                        }
                       
                        // Write offset list (CharOffset * num)
                        fw.writeInt(offsetList.size()); // offsetList size (int)
                        for (int i = 0; i < offsetList.size(); i++)
                        {
                                CharacterOffset o = offsetList.elementAt(i);
                                fw.writeFloat(o.x);
                                fw.writeFloat(o.y);
                        }
                       
                        // number of kerning pairs
                        fw.writeInt(kerningPairs.size());
                       
                        // Write kerning pairs + values
                        for (int i = 0; i < kerningPairs.size(); i++)
                        {
                                KerningPair p = kerningPairs.elementAt(i);
                                Integer v = kerningValues.elementAt(i);
                               
                                fw.writeChar(p.first);
                                fw.writeChar(p.second);
                                fw.writeInt(v);
                        }
                       
                        fw.close();
                }
                catch (IOException e)
                {
                        Gdx.app.log(LOG_TAG, "Could not write file: " + fontFileName);                 
                }      
                //File fontFile = new File(fontFileName);
               
                // calculate basic height of first character/ascent -> calc next power of two -> H             
                // initialize output array with 512xH
                // rightExtent = 0;
                // iterate character by character in charList
                //    get character's rightMost extent in rectList
                //    if rightMost extent exceeds 512
                //        -> new line, rightExtent <- 0, y += H
                //        resize array
                //    
                //    write character pixels to output PNG
                //
                //
               
                // write binary array to BUfferedImage
                // write info to binary file <filename>.bfont
               
        }
       
        private static void copyRect(BufferedImage src, BufferedImage dest, int sx, int sy, int sw, int sh, int dx, int dy, boolean convertGreyScaleToAlpha)
        {
                byte[] srcbuf = ((DataBufferByte)src.getRaster().getDataBuffer()).getData();
                byte[] destbuf = ((DataBufferByte)dest.getRaster().getDataBuffer()).getData();
               
                int width = (int)sw;
                int height = (int)sh;
               
                int dstoffs = dx + dy * dest.getWidth();
                int srcoffs = sx + sy * src.getWidth();
               
                for (int y = 0; y < height; y++)
                {
                        //System.arraycopy(srcbuf, srcoffs*4, destbuf, dstoffs*4, width*4);
                       
                        // reinterpret into alpha channel
                        for (int x = 0; x < width; x++)
                        {
                                if (convertGreyScaleToAlpha)
                                {
                                        byte b = srcbuf[(srcoffs + x) * 4 + 1]; // B
                                        byte g = srcbuf[(srcoffs + x) * 4 + 2]; // G
                                        byte r = srcbuf[(srcoffs + x) * 4 + 3]; // R
                                        byte a = (byte)((b + g + r) / 3);
                                                       
                                        destbuf[(dstoffs + x) * 4 + 1] = (byte)255;
                                        destbuf[(dstoffs + x) * 4 + 2] = (byte)255;
                                        destbuf[(dstoffs + x) * 4 + 3] = (byte)255;
                                       
                                        //byte a = (byte) ((byte)(g + b + r) / 3);
                                        //byte a = srcbuf[(srcoffs + x) * 4 + 0]; // A
                                       
                                       
                                        destbuf[(dstoffs + x) * 4 + 0] = a;
                                }
                                else
                                {
                                        byte a = srcbuf[(srcoffs + x) * 4 + 0]; // A
                                        byte b = srcbuf[(srcoffs + x) * 4 + 1]; // B
                                        byte g = srcbuf[(srcoffs + x) * 4 + 2]; // G
                                        byte r = srcbuf[(srcoffs + x) * 4 + 3]; // R
                                       
                                        destbuf[(dstoffs + x) * 4 + 0] = a;
                                        destbuf[(dstoffs + x) * 4 + 1] = b;
                                        destbuf[(dstoffs + x) * 4 + 2] = g;
                                        destbuf[(dstoffs + x) * 4 + 3] = r;
                                }
                        }
                       
                        dstoffs += dest.getWidth();
                        srcoffs += src.getWidth();
                }                              
               
                /*private static void copySrcIntoDstAt(final BufferedImage src,
                        final BufferedImage dst, final int dx, final int dy) {
                    int[] srcbuf = ((DataBufferInt) src.getRaster().getDataBuffer()).getData();
                    int[] dstbuf = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData();
                    int width = src.getWidth();
                    int height = src.getHeight();
                    int dstoffs = dx + dy * dst.getWidth();
                    int srcoffs = 0;
                    for (int y = 0 ; y < height ; y++ , dstoffs+= dst.getWidth(), srcoffs += width ) {
                        System.arraycopy(srcbuf, srcoffs , dstbuf, dstoffs, width);
                    }
                }*/

        }
       
        private static Vector<Character> parseCharList(Tokenizer t) throws ScanException
        {
                Vector<Character> charList = new Vector<Character>();
               
                t.readToken("(");
               
                while (!t.checkToken(")"))
                {
                        String character = t.readString();
                       
                        if (character.length() > 1)
                                throw new ScanException("Character can only have length 1!", t.getSurroundings());
                       
                        charList.add(character.charAt(0));
                       
                        if (t.checkToken(")"))
                                break;
                       
                        t.readToken(",");
                }                              
               
                t.readToken(")");
               
                return charList;
        }
       
        private static Vector<Float> parseWidthList(Tokenizer t) throws ScanException
        {
                Vector<Float> widthList = new Vector<Float>();
               
                t.readToken("(");
               
                while (!t.checkToken(")"))
                {
                        float width = t.readNumber();
                       
                        widthList.add(width);
                       
                        if (t.checkToken(")"))
                                break;
                       
                        //Gdx.app.log(LOG_TAG, "[" + width + "]");
                       
                        t.readToken(",");
                }                              
               
                t.readToken(")");
               
                return widthList;
        }
       
        private static Vector<CharacterRect> parseRectList(Tokenizer t) throws ScanException
        {
                Vector<CharacterRect> rectList = new Vector<CharacterRect>();
               
                t.readToken("(");
               
                while (t.checkToken("("))
                {
                        t.readToken("(");
                       
                        float x = t.readNumber();
                        t.readToken(",");
                        float y = t.readNumber();
                        t.readToken(",");
                        float w = t.readNumber();
                        t.readToken(",");
                        float h = t.readNumber();
                       
                        rectList.add(new CharacterRect(x, y, w, h));
                       
                        t.readToken(")");
                       
                        if (!t.checkToken(","))
                                break;
                       
                        t.readToken(",");
                }              
               
                if (!t.checkToken(")"))
                        throw new ScanException("Syntax error in RectList!", t.getSurroundings());
               
                t.readToken(")");
               
                return rectList;
        }
       
        private static Vector<CharacterOffset> parseOffsetList(Tokenizer t) throws ScanException
        {
                Vector<CharacterOffset> offsetList = new Vector<CharacterOffset>();
               
                t.readToken("(");
               
                while (t.checkToken("("))
                {
                        t.readToken("(");
                       
                        float x = t.readNumber();
                        t.readToken(",");
                        float y = t.readNumber();
                       
                        offsetList.add(new CharacterOffset(x, y));
                       
                        t.readToken(")");
                       
                        if (!t.checkToken(","))
                                break;
                       
                        t.readToken(",");
                }              
               
                if (!t.checkToken(")"))
                        throw new ScanException("Syntax error in OffsetList!", t.getSurroundings());
               
                t.readToken(")");
               
                return offsetList;
        }
       
        private static Vector<KerningPair> parseKerningPairs(Tokenizer t) throws ScanException
        {
                Vector<KerningPair> kerningPairs = new Vector<KerningPair>();
               
                t.readToken("(");
               
                while (!t.checkToken(")"))
                {
                        String pair = t.readString();
                       
                        kerningPairs.add(new KerningPair(pair.charAt(0), pair.charAt(1)));
                       
                        if (t.checkToken(")"))
                                break;
                       
                        //Gdx.app.log(LOG_TAG, "[" + width + "]");
                       
                        t.readToken(",");
                }                              
               
                t.readToken(")");
               
                return kerningPairs;
        }
       
        private static Vector<Integer> parseKerningValues(Tokenizer t) throws ScanException
        {
                Vector<Integer> kerningValues = new Vector<Integer>();
               
                t.readToken("(");
               
                while (!t.checkToken(")"))
                {
                        int value = (int)t.readNumber();
                       
                        kerningValues.add(value);
                       
                        if (t.checkToken(")"))
                                break;
                       
                        //Gdx.app.log(LOG_TAG, "[" + width + "]");
                       
                        t.readToken(",");
                }                              
               
                t.readToken(")");
               
                return kerningValues;
        }
}