Package com.ardor3d.ui.text

Source Code of com.ardor3d.ui.text.BMFont$Page

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.ui.text;

import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ardor3d.annotation.SavableFactory;
import com.ardor3d.image.Texture;
import com.ardor3d.image.TextureStoreFormat;
import com.ardor3d.renderer.queue.RenderBucketType;
import com.ardor3d.renderer.state.BlendState;
import com.ardor3d.renderer.state.TextureState;
import com.ardor3d.renderer.state.ZBufferState;
import com.ardor3d.scenegraph.Spatial;
import com.ardor3d.util.TextureKey;
import com.ardor3d.util.TextureManager;
import com.ardor3d.util.export.InputCapsule;
import com.ardor3d.util.export.OutputCapsule;
import com.ardor3d.util.export.Savable;
import com.ardor3d.util.resource.ResourceSource;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
* Loads a font generated by BMFont (http://www.angelcode.com/products/bmfont/).
* <ul>
* <li>Font info file *must* be in XML format.
* <li>The texture should be saved in 32 bit PNG format - TGA does not appear to work.
* <li>This class only supports a single page (see BMFont documentation for details on pages)
* </ul>
*/
public class BMFont implements Savable {
    private static Logger logger = Logger.getLogger(BMFont.class.getName());

    private final Map<Integer, Char> _charMap = Maps.newHashMap();
    private final Map<Integer, Map<Integer, Integer>> _kernMap = Maps.newHashMap();

    private String _styleName; // e.g. "Courier-12-bold"
    private final ArrayList<Page> _pages = new ArrayList<Page>();
    private Texture _pageTexture;
    private RenderStateSetter _blendStateSetter = null;
    private RenderStateSetter _alphaStateSetter = null;
    private boolean _useMipMaps;
    private int _maxCharAdv;
    private Common _common = null;
    private Info _info = null;

    /**
     * This constructor should be used when loading as Savable
     */
    public BMFont() {}

    /**
     * Reads an XML BMFont description file and loads corresponding texture. Note that the TGA written by BMFont does
     * not seem to be read properly by the Ardor3D loader. PNG works fine.
     *
     * @param fileUrl
     *            - the location of the .fnt font file. Can not be null.
     * @param useMipMaps
     *            if true, use trilinear filtering with max anisotropy, else min filter is bilinear. MipMaps result in
     *            blurrier text, but less shimmering.
     * @throws IOException
     *             if there are any problems reading the .fnt file.
     */
    public BMFont(final ResourceSource source, final boolean useMipMaps) throws IOException {
        _useMipMaps = useMipMaps;

        parseFontFile(source);
        initialize(source);
    }

    /** apply default render states to spatial */
    public void applyRenderStatesTo(final Spatial spatial, final boolean useBlend) {
        if (useBlend) {
            if (_blendStateSetter == null) {
                _blendStateSetter = new RenderStateSetter(_pageTexture, true);
            }
            _blendStateSetter.applyTo(spatial);
        } else {
            if (_alphaStateSetter == null) {
                _alphaStateSetter = new RenderStateSetter(_pageTexture, false);
            }
            _alphaStateSetter.applyTo(spatial);
        }
    }

    public String getStyleName() {
        return _styleName;
    }

    public int getSize() {
        return Math.abs(_info.size);
    }

    public int getLineHeight() {
        return _common.lineHeight;
    }

    public int getBaseHeight() {
        return _common.base;
    }

    public int getTextureWidth() {
        return _common.scaleW;
    }

    public int getTextureHeight() {
        return _common.scaleH;
    }

    /**
     * @param chr
     *            ascii character code
     * @return character descriptor for chr. If character is not in the char set, return '?' (if '?' is not in the char
     *         set, return will be null)
     */
    public BMFont.Char getChar(int chr) {
        BMFont.Char retVal = _charMap.get(chr);
        if (retVal == null) {
            chr = '?';
            retVal = _charMap.get(chr);
            if (retVal == null) { // if still null, use the first char
                final Iterator<Char> it = _charMap.values().iterator();
                retVal = it.next();
            }
        }
        return retVal;
    }

    /**
     * @return kerning information for this character pair
     */
    public int getKerning(final int chr, final int nextChr) {
        final Map<Integer, Integer> map = _kernMap.get(chr);
        if (map != null) {
            final Integer amt = map.get(nextChr);
            if (amt != null) {
                return amt;
            }
        }
        return 0;
    }

    /**
     * @return the largest xadvance in this char set
     */
    public int getMaxCharAdvance() {
        return _maxCharAdv;
    }

    public int getOutlineWidth() {
        return _info.outline;
    }

    public Info getInfo() {
        return _info;
    }

    /**
     * Writes the XML for this font out to the OutputStream provided.
     *
     * @param outputStream
     *            the OutputStream to which the XML for this font will be written to
     * @throws IOException
     *             thrown if there is any problem writing out to the OutputStream
     */
    public void writeXML(final OutputStream outputStream) throws IOException {
        final StringBuilder xml = new StringBuilder();

        xml.append("<?xml version=\"1.0\"?>\n");
        xml.append("<font>\n");
        xml.append(generateInfoXML());
        xml.append(generateCommonXML());
        xml.append(generatePagesXML());
        xml.append(generateCharsXML());
        xml.append(generateKerningsXML());
        xml.append("</font>");

        // Write out to the output stream now
        outputStream.write(xml.toString().getBytes());
        outputStream.flush();

        return;
    }

    private String generateInfoXML() {
        final StringBuilder xml = new StringBuilder();

        xml.append("  <info face=\"");
        xml.append(_info.face);
        xml.append("\" size=\"");
        xml.append(_info.size);
        xml.append("\" bold=\"");
        xml.append(_info.bold ? "1" : "0");
        xml.append("\" italic=\"");
        xml.append(_info.italic ? "1" : "0");
        xml.append("\" charset=\"");
        xml.append(_info.charset);
        xml.append("\" unicode=\"");
        xml.append(_info.unicode ? "1" : "0");
        xml.append("\" stretchH=\"");
        xml.append(_info.stretchH);
        xml.append("\" smooth=\"");
        xml.append(_info.smooth ? "1" : "0");
        xml.append("\" aa=\"");
        xml.append(_info.aa ? "1" : "0");
        xml.append("\" padding=\"");

        for (int i = 0; i < _info.padding.length; i++) {
            xml.append(_info.padding[i]);

            if (i < (_info.padding.length - 1)) {
                xml.append(',');
            }
        }

        xml.append("\" spacing=\"");

        for (int i = 0; i < _info.spacing.length; i++) {
            xml.append(_info.spacing[i]);

            if (i < (_info.spacing.length - 1)) {
                xml.append(',');
            }
        }

        xml.append("\" outline=\"");
        xml.append(_info.outline);
        xml.append("\"/>\n");

        return xml.toString();
    }

    private String generateCommonXML() {
        final StringBuilder xml = new StringBuilder();

        xml.append("  <common lineHeight=\"");
        xml.append(_common.lineHeight);
        xml.append("\" base=\"");
        xml.append(_common.base);
        xml.append("\" scaleW=\"");
        xml.append(_common.scaleW);
        xml.append("\" scaleH=\"");
        xml.append(_common.scaleH);
        xml.append("\" pages=\"");
        xml.append(_common.pages);
        xml.append("\" packed=\"");
        xml.append(_common.packed ? "1" : "0");
        xml.append("\" alphaChnl=\"");
        xml.append(_common.alphaChnl);
        xml.append("\" redChnl=\"");
        xml.append(_common.redChnl);
        xml.append("\" greenChnl=\"");
        xml.append(_common.greenChnl);
        xml.append("\" blueChnl=\"");
        xml.append(_common.blueChnl);
        xml.append("\"/>\n");

        return xml.toString();
    }

    private String generatePagesXML() {
        final StringBuilder xml = new StringBuilder();

        xml.append("  <pages>\n");

        for (final Iterator<Page> iterator = _pages.iterator(); iterator.hasNext();) {
            final Page page = iterator.next();

            xml.append("    <page id=\"");
            xml.append(page.id);
            xml.append("\" file=\"");
            xml.append(page.file);
            xml.append("\" />\n");
        }

        xml.append("  </pages>\n");

        return xml.toString();
    }

    private String generateCharsXML() {
        final StringBuilder xml = new StringBuilder();

        xml.append("  <chars count=\"");
        xml.append(_charMap.size());
        xml.append("\">\n");

        for (final Iterator<Integer> iterator = _charMap.keySet().iterator(); iterator.hasNext();) {
            final Integer key = iterator.next();
            final Char character = _charMap.get(key);

            xml.append("    <char id=\"");
            xml.append(character.id);
            xml.append("\" x=\"");
            xml.append(character.x);
            xml.append("\" y=\"");
            xml.append(character.y);
            xml.append("\" width=\"");
            xml.append(character.width);
            xml.append("\" height=\"");
            xml.append(character.height);
            xml.append("\" xoffset=\"");
            xml.append(character.xoffset);
            xml.append("\" yoffset=\"");
            xml.append(character.yoffset);
            xml.append("\" xadvance=\"");
            xml.append(character.xadvance);
            xml.append("\" page=\"");
            xml.append(character.page);
            xml.append("\" chnl=\"");
            xml.append(character.chnl);
            xml.append("\" />\n");
        }

        xml.append("  </chars>\n");

        return xml.toString();
    }

    private String generateKerningsXML() {
        final StringBuilder xml = new StringBuilder();
        int count = 0;

        for (final Iterator<Integer> iterator = _kernMap.keySet().iterator(); iterator.hasNext();) {
            final Integer first = iterator.next();
            final Map<Integer, Integer> amtHash = _kernMap.get(first);

            for (final Iterator<Integer> iterator2 = amtHash.keySet().iterator(); iterator2.hasNext();) {
                final Integer second = iterator2.next();
                final Integer amount = amtHash.get(second);

                xml.append("    <kerning first=\"");
                xml.append(first);
                xml.append("\" second=\"");
                xml.append(second);
                xml.append("\" amount=\"");
                xml.append(amount);
                xml.append("\" />\n");

                count++;
            }
        }

        final String xmlString = "  <kernings count=\"" + count + "\">\n" + xml.toString() + "  </kernings>\n";

        return xmlString;
    }

    /**
     * load the texture and create default render states. Only a single page is supported.
     *
     * @param fontUrl
     */
    // ----------------------------------------------------------
    protected void initialize(final ResourceSource source) throws MalformedURLException {
        _styleName = _info.face + "-" + _info.size;

        if (_info.bold) {
            _styleName += "-bold";
        } else {
            _styleName += "-medium";
        }

        if (_info.italic) {
            _styleName += "-italic";
        } else {
            _styleName += "-regular";
        }

        // only a single page is supported
        if (_pages.size() > 0) {
            final Page page = _pages.get(0);

            final ResourceSource texSrc = source.getRelativeSource("./" + page.file);

            Texture.MinificationFilter minFilter;
            Texture.MagnificationFilter magFilter;

            magFilter = Texture.MagnificationFilter.Bilinear;
            minFilter = Texture.MinificationFilter.BilinearNoMipMaps;
            if (_useMipMaps) {
                minFilter = Texture.MinificationFilter.Trilinear;
            }
            final TextureKey tkey = TextureKey.getKey(texSrc, false, TextureStoreFormat.GuessNoCompressedFormat,
                    minFilter);
            _pageTexture = TextureManager.loadFromKey(tkey, null, null);
            _pageTexture.setMagnificationFilter(magFilter);

            // Add a touch higher mipmap selection.
            _pageTexture.setLodBias(-1);

            if (_useMipMaps) {
                _pageTexture.setAnisotropicFilterPercent(1.0f);
            }
        }
    }

    public Texture getPageTexture() {
        return _pageTexture;
    }

    public List<Integer> getMappedChars() {
        return Lists.newArrayList(_charMap.keySet());
    }

    public Map<Integer, Integer> getKerningsForCharacter(final int val) {
        return _kernMap.get(val);
    }

    public Map<Integer, Map<Integer, Integer>> getKerningMap() {
        return _kernMap;
    }

    /**
     *
     * @param fontUrl
     * @throws IOException
     */
    protected void parseFontFile(final ResourceSource source) throws IOException {
        _maxCharAdv = 0;
        _charMap.clear();
        _pages.clear();
        try {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder db = dbf.newDocumentBuilder();
            final Document doc = db.parse(source.openStream());

            doc.getDocumentElement().normalize();
            recurse(doc.getElementsByTagName("font").item(0));

            // db.reset();
        } catch (final Throwable t) {
            final IOException ex = new IOException("Error loading font file " + source.toString());
            ex.initCause(t);
            throw ex;
        }
    }

    private void recurse(final Node node) {
        final NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            final Node child = children.item(i);
            processNode(child);
            recurse(child);
        }
    }

    private void processNode(final Node node) {
        final String tagName = node.getNodeName();
        if (tagName != null) {
            if (tagName.equals("info")) {
                processInfoNode(node);
            } else if (tagName.equals("common")) {
                processCommonNode(node);
            } else if (tagName.equals("page")) {
                processPageNode(node);
            } else if (tagName.equals("char")) {
                processCharNode(node);
            } else if (tagName.equals("kerning")) {
                procesKerningNode(node);
            }
        }
    }

    private void processInfoNode(final Node node) {
        final NamedNodeMap attribs = node.getAttributes();
        _info = new Info();
        _info.face = getStringAttrib("face", attribs);
        _info.size = getIntAttrib("size", attribs);
        _info.bold = getBoolAttrib("bold", attribs);
        _info.italic = getBoolAttrib("italic", attribs);
        _info.charset = getStringAttrib("charset", attribs);
        _info.unicode = getBoolAttrib("unicode", attribs);
        _info.stretchH = getIntAttrib("stretchH", attribs);
        _info.smooth = getBoolAttrib("smooth", attribs);
        _info.aa = getBoolAttrib("aa", attribs);
        _info.padding = getIntArrayAttrib("padding", attribs);
        _info.spacing = getIntArrayAttrib("spacing", attribs);
        _info.outline = getIntAttrib("outline", attribs);
    }

    private void processCommonNode(final Node node) {
        final NamedNodeMap attribs = node.getAttributes();
        _common = new Common();
        _common.lineHeight = getIntAttrib("lineHeight", attribs);
        _common.base = getIntAttrib("base", attribs);
        _common.scaleW = getIntAttrib("scaleW", attribs);
        _common.scaleH = getIntAttrib("scaleH", attribs);
        _common.pages = getIntAttrib("pages", attribs);
        _common.packed = getBoolAttrib("packed", attribs);
        _common.alphaChnl = getIntAttrib("alphaChnl", attribs);
        _common.redChnl = getIntAttrib("redChnl", attribs);
        _common.greenChnl = getIntAttrib("greenChnl", attribs);
        _common.blueChnl = getIntAttrib("blueChnl", attribs);
    }

    private void processCharNode(final Node node) {
        final NamedNodeMap attribs = node.getAttributes();
        final Char c = new Char();
        c.id = getIntAttrib("id", attribs);
        c.x = getIntAttrib("x", attribs);
        c.y = getIntAttrib("y", attribs);
        c.width = getIntAttrib("width", attribs);
        c.height = getIntAttrib("height", attribs);
        c.xoffset = getIntAttrib("xoffset", attribs);
        c.yoffset = getIntAttrib("yoffset", attribs);
        c.xadvance = getIntAttrib("xadvance", attribs);
        c.page = getIntAttrib("page", attribs);
        c.chnl = getIntAttrib("chnl", attribs);
        _charMap.put(c.id, c);
        if (c.xadvance > _maxCharAdv) {
            _maxCharAdv = c.xadvance;
        }
    }

    private void processPageNode(final Node node) {
        final NamedNodeMap attribs = node.getAttributes();
        final Page page = new Page();
        page.id = getIntAttrib("id", attribs);
        page.file = getStringAttrib("file", attribs);
        _pages.add(page);
        if (_pages.size() > 1) {
            logger.warning("multiple pages defined in font description file, but only a single page is supported.");
        }
    }

    private void procesKerningNode(final Node node) {
        final NamedNodeMap attribs = node.getAttributes();
        final int first = getIntAttrib("first", attribs);
        final int second = getIntAttrib("second", attribs);
        final int amount = getIntAttrib("amount", attribs);
        Map<Integer, Integer> amtHash;
        amtHash = _kernMap.get(first);
        if (amtHash == null) {
            amtHash = Maps.newHashMap();
            _kernMap.put(first, amtHash);
        }
        amtHash.put(second, amount);
    }

    // == xml attribute getters ============================
    int getIntAttrib(final String name, final NamedNodeMap attribs) {
        final Node node = attribs.getNamedItem(name);
        return Integer.parseInt(node.getNodeValue());
    }

    String getStringAttrib(final String name, final NamedNodeMap attribs) {
        final Node node = attribs.getNamedItem(name);
        return node.getNodeValue();
    }

    boolean getBoolAttrib(final String name, final NamedNodeMap attribs) {
        final Node node = attribs.getNamedItem(name);
        return (Integer.parseInt(node.getNodeValue()) == 1);
    }

    int[] getIntArrayAttrib(final String name, final NamedNodeMap attribs) {
        final Node node = attribs.getNamedItem(name);
        final String str = node.getNodeValue();
        final StringTokenizer strtok = new StringTokenizer(str, ",");
        final int sz = strtok.countTokens();
        final int[] retVal = new int[sz];
        for (int i = 0; i < sz; i++) {
            retVal[i] = Integer.parseInt(strtok.nextToken());
        }
        return retVal;
    }

    // == support structs ==================================
    @SavableFactory(factoryMethod = "create")
    public static class Info implements Savable {
        public String face;
        public int size;
        public boolean bold;
        public boolean italic;
        public String charset;
        public boolean unicode;
        public int stretchH;
        public boolean smooth;
        public boolean aa;
        public int[] padding;
        public int[] spacing;
        public int outline;

        public static Info create() {
            return new Info();
        }

        @Override
        public void write(final OutputCapsule capsule) throws IOException {
            capsule.write(face, "face", null);
            capsule.write(size, "size", 0);
            capsule.write(bold, "bold", false);
            capsule.write(italic, "italic", false);
            capsule.write(charset, "charset", null);
            capsule.write(unicode, "unicode", false);
            capsule.write(stretchH, "stretchH", 0);
            capsule.write(smooth, "smooth", false);
            capsule.write(aa, "aa", false);
            capsule.write(padding, "padding", null);
            capsule.write(spacing, "spacing", null);
            capsule.write(outline, "outline", 0);
        }

        @Override
        public void read(final InputCapsule capsule) throws IOException {
            face = capsule.readString("face", null);
            size = capsule.readInt("size", 0);
            bold = capsule.readBoolean("bold", false);
            italic = capsule.readBoolean("italic", false);
            charset = capsule.readString("charset", null);
            unicode = capsule.readBoolean("unicode", false);
            stretchH = capsule.readInt("stretchH", 0);
            smooth = capsule.readBoolean("smooth", false);
            aa = capsule.readBoolean("aa", false);
            padding = capsule.readIntArray("padding", null);
            spacing = capsule.readIntArray("spacing", null);
            outline = capsule.readInt("outline", 0);
        }

        @Override
        public Class<?> getClassTag() {
            return getClass();
        }
    }

    @SavableFactory(factoryMethod = "create")
    public static class Common implements Savable {
        public int lineHeight;
        public int base;
        public int scaleW;
        public int scaleH;
        public int pages;
        public boolean packed;
        public int alphaChnl;
        public int redChnl;
        public int greenChnl;
        public int blueChnl;

        public static Common create() {
            return new Common();
        }

        @Override
        public void write(final OutputCapsule capsule) throws IOException {
            capsule.write(lineHeight, "lineHeight", 0);
            capsule.write(base, "base", 0);
            capsule.write(scaleW, "scaleW", 1);
            capsule.write(scaleH, "scaleH", 1);
            capsule.write(pages, "pages", 0);
            capsule.write(packed, "packed", false);
            capsule.write(alphaChnl, "alphaChnl", 0);
            capsule.write(redChnl, "redChnl", 0);
            capsule.write(greenChnl, "greenChnl", 0);
            capsule.write(blueChnl, "blueChnl", 0);
        }

        @Override
        public void read(final InputCapsule capsule) throws IOException {
            lineHeight = capsule.readInt("lineHeight", 0);
            base = capsule.readInt("base", 0);
            scaleW = capsule.readInt("scaleW", 0);
            scaleH = capsule.readInt("scaleH", 0);
            pages = capsule.readInt("pages", 0);
            packed = capsule.readBoolean("packed", false);
            alphaChnl = capsule.readInt("alphaChnl", 0);
            redChnl = capsule.readInt("redChnl", 0);
            greenChnl = capsule.readInt("greenChnl", 0);
            blueChnl = capsule.readInt("blueChnl", 0);
        }

        @Override
        public Class<?> getClassTag() {
            return getClass();
        }
    }

    @SavableFactory(factoryMethod = "create")
    public static class Page implements Savable {
        public int id;
        public String file;

        public static Page create() {
            return new Page();
        }

        @Override
        public void write(final OutputCapsule capsule) throws IOException {
            capsule.write(id, "id", 0);
            capsule.write(file, "file", null);
        }

        @Override
        public void read(final InputCapsule capsule) throws IOException {
            id = capsule.readInt("id", 0);
            file = capsule.readString("file", null);

        }

        @Override
        public Class<?> getClassTag() {
            return this.getClass();
        }
    }

    @SavableFactory(factoryMethod = "create")
    public static class Char implements Savable {
        public int id;
        public int x;
        public int y;
        public int width;
        public int height;
        public int xoffset;
        public int yoffset;
        public int xadvance;
        public int page;
        public int chnl;

        public static Char create() {
            return new Char();
        }

        @Override
        public void write(final OutputCapsule capsule) throws IOException {
            capsule.write(id, "id", 0);
            capsule.write(x, "x", 0);
            capsule.write(y, "y", 0);
            capsule.write(width, "width", 0);
            capsule.write(height, "height", 0);
            capsule.write(xoffset, "xoffset", 0);
            capsule.write(yoffset, "yoffset", 0);
            capsule.write(xadvance, "xadvance", 0);
            capsule.write(page, "page", 0);
            capsule.write(chnl, "chnl", 0);
        }

        @Override
        public void read(final InputCapsule capsule) throws IOException {
            id = capsule.readInt("id", 0);
            x = capsule.readInt("x", 0);
            y = capsule.readInt("y", 0);
            width = capsule.readInt("width", 0);
            height = capsule.readInt("height", 0);
            xoffset = capsule.readInt("xoffset", 0);
            yoffset = capsule.readInt("yoffset", 0);
            xadvance = capsule.readInt("xadvance", 0);
            page = capsule.readInt("page", 0);
            chnl = capsule.readInt("chnl", 0);
        }

        @Override
        public Class<?> getClassTag() {
            return getClass();
        }
    }

    @SavableFactory(factoryMethod = "create")
    public static class Kerning implements Savable {
        public int first;
        public int second;
        public int amount;

        public Kerning() {}

        public Kerning(final int first, final int second, final int amount) {
            this.first = first;
            this.second = second;
            this.amount = amount;
        }

        public static Kerning create() {
            return new BMFont.Kerning();
        }

        @Override
        public void write(final OutputCapsule capsule) throws IOException {
            capsule.write(first, "fist", 0);
            capsule.write(second, "second", 0);
            capsule.write(amount, "amount", 0);
        }

        @Override
        public void read(final InputCapsule capsule) throws IOException {
            first = capsule.readInt("first", 0);
            second = capsule.readInt("second", 0);
            amount = capsule.readInt("amount", 0);
        }

        @Override
        public Class<?> getClassTag() {
            return getClass();
        }
    }

    /**
     * utility to set default render states for text
     */
    public class RenderStateSetter {
        public TextureState textureState;
        public BlendState blendState;
        public ZBufferState zBuffState;

        float _blendDisabledTestRef = 0.3f;
        float _blendEnabledTestRef = 0.02f;

        boolean _useBlend;

        RenderStateSetter(final Texture texture, final boolean useBlend) {
            textureState = new TextureState();
            textureState.setTexture(texture);

            blendState = new BlendState();
            blendState.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
            blendState.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
            blendState.setTestEnabled(true);
            blendState.setTestFunction(BlendState.TestFunction.GreaterThan);

            zBuffState = new ZBufferState();
            zBuffState.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);

            setUseBlend(useBlend);
        }

        void applyTo(final Spatial spatial) {
            spatial.setRenderState(textureState);
            spatial.setRenderState(blendState);
            spatial.setRenderState(zBuffState);
            if (_useBlend) {
                spatial.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
            } else {
                spatial.getSceneHints().setRenderBucketType(RenderBucketType.Opaque);
            }
        }

        void setUseBlend(final boolean blend) {
            _useBlend = blend;
            if (!_useBlend) {
                blendState.setBlendEnabled(false);
                blendState.setReference(_blendDisabledTestRef);
                zBuffState.setWritable(true);
            } else {
                blendState.setBlendEnabled(true);
                blendState.setReference(_blendEnabledTestRef);
                zBuffState.setWritable(false);
            }
        }
    }

    @Override
    public void write(final OutputCapsule capsule) throws IOException {
        _pageTexture.setStoreImage(true);
        capsule.write(_useMipMaps, "useMipMaps", false);
        capsule.write(_styleName, "styleName", null);
        capsule.write(_pageTexture, "pageTexture", null);

        // Info
        capsule.write(_info, "info", null);
        // Common
        capsule.write(_common, "common", null);
        // Pages
        capsule.writeSavableList(_pages, "pages", _pages);
        // Chars
        capsule.writeSavableList(new ArrayList<Char>(_charMap.values()), "charMap", null);

        // Kernings
        final List<Kerning> kernings = new ArrayList<Kerning>();
        for (final Iterator<Integer> iterator = _kernMap.keySet().iterator(); iterator.hasNext();) {
            final Integer first = iterator.next();
            final Map<Integer, Integer> amtHash = _kernMap.get(first);
            for (final Iterator<Integer> iterator2 = amtHash.keySet().iterator(); iterator2.hasNext();) {
                final Integer second = iterator2.next();
                final Integer amount = amtHash.get(second);
                kernings.add(new Kerning(first, second, amount));
            }
        }
        capsule.writeSavableList(kernings, "kernings", kernings);
    }

    @Override
    public void read(final InputCapsule capsule) throws IOException {
        _useMipMaps = capsule.readBoolean("useMipMaps", false);
        _styleName = capsule.readString("styleName", null);
        _pageTexture = (Texture) capsule.readSavable("pageTexture", null);
        _pageTexture = TextureManager.loadFromImage(_pageTexture.getImage(), _pageTexture.getMinificationFilter());

        // Info
        _info = (Info) capsule.readSavable("info", _info);
        // Common
        _common = (Common) capsule.readSavable("common", _common);
        // Pages
        _pages.clear();
        final List<Savable> pages = capsule.readSavableList("pages", new ArrayList<Savable>());
        for (final Savable savable : pages) {
            _pages.add((Page) savable);
            if (_pages.size() > 1) {
                logger.warning("multiple pages defined in font description file, but only a single page is supported.");
            }
        }
        // Chars
        _charMap.clear();
        final List<Savable> chars = capsule.readSavableList("charMap", new ArrayList<Savable>());
        for (final Savable savable : chars) {
            final Char c = (Char) savable;
            _charMap.put(c.id, c);
            if (c.xadvance > _maxCharAdv) {
                _maxCharAdv = c.xadvance;
            }
        }
        // Kernings
        _kernMap.clear();
        final List<Savable> kernings = capsule.readSavableList("kernings", new ArrayList<Savable>());
        for (final Savable savable : kernings) {
            final Kerning k = (Kerning) savable;
            Map<Integer, Integer> amtHash;
            amtHash = _kernMap.get(k.first);
            if (amtHash == null) {
                amtHash = Maps.newHashMap();
                _kernMap.put(k.first, amtHash);
            }
            amtHash.put(k.second, k.amount);
            _kernMap.put(k.first, amtHash);
        }
    }

    @Override
    public Class<?> getClassTag() {
        return getClass();
    }
}
TOP

Related Classes of com.ardor3d.ui.text.BMFont$Page

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.