/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.tests.extensions; import java.util.EnumMap; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; import com.badlogic.gdx.graphics.g2d.PixmapPacker; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.tests.utils.GdxTest; import com.badlogic.gdx.utils.Array; /** An advanced example of packing many glyphs into a single texture atlas, using FreeTypeFontGenerator. * * This example uses enum ordinals for fast access to a two-dimensional array, which stores BitmapFonts by size and style. A more * flexible solution might be to use an OjectMap and and IntMap instead. * * @author mattdesl AKA davedes */ public class FreeTypePackTest extends GdxTest { // Define font sizes here... static enum FontSize { Tiny(10), Small(12), Medium(16), Large(20), Huge(24), ReallyHuge(28), JustTooBig(64); public final int size; FontSize (int size) { this.size = size; } } // Define font styles here... static enum FontStyle { Regular("data/arial.ttf"), Italic("data/arial-italic.ttf"); public final String path; FontStyle (String path) { this.path = path; } } OrthographicCamera camera; SpriteBatch batch; Array<TextureRegion> regions; String text; FontMap<BitmapFont> fontMap; public static final int FONT_ATLAS_WIDTH = 1024; public static final int FONT_ATLAS_HEIGHT = 512; // whether to use integer coords for BitmapFont... private static final boolean INTEGER = false; // Our demo doesn't need any fancy characters. // Note: the set in FreeTypeFontGenerator.DEFAULT_CHARS is more extensive // Also note that this string must be contained of unique characters; no duplicates! public static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz\n1234567890" + "\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"; @Override public void create () { camera = new OrthographicCamera(); batch = new SpriteBatch(); long start = System.currentTimeMillis(); int glyphCount = createFonts(); long time = System.currentTimeMillis() - start; text = glyphCount + " glyphs packed in " + regions.size + " page(s) in " + time + " ms"; } @Override public void render () { Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); batch.setProjectionMatrix(camera.combined); batch.begin(); float x = 10; float y = Gdx.graphics.getHeight() - 10; int renderCalls = 0; // NOTE: Before production release on mobile, you should cache the array from values() // inside the Enum in order to reduce allocations in the render loop. for (FontStyle style : FontStyle.values()) { for (FontSize size : FontSize.values()) { BitmapFont fnt = getFont(style, size); fnt.draw(batch, style.name() + " " + size.size + "pt: The quick brown fox jumps over the lazy dog", x, y); y -= fnt.getLineHeight() + 10; } y -= 20; } BitmapFont font = getFont(FontStyle.Regular, FontSize.Medium); font.draw(batch, text, 10, font.getCapHeight() + 10); // draw all glyphs in background batch.setColor(1f, 1f, 1f, 0.15f); batch.draw(regions.first(), 0, 0); batch.setColor(1f, 1f, 1f, 1f); batch.end(); } @Override public void dispose () { super.dispose(); for (TextureRegion r : regions) r.getTexture().dispose(); // dispose the texture since we own it batch.dispose(); } // Utility method to grab a font by style/size pair public BitmapFont getFont (FontStyle style, FontSize size) { return fontMap.get(style).get(size); } protected int createFonts () { // ////////////////////////////////////////////////////////////////////////////////////////////////////// // //////Steps to use multiple FreeTypeFontGenerators with a single texture atlas:////////////////////// // 1. Create a new PixmapPacker big enough to fit all your desired glyphs // 2. Create a new FreeTypeFontGenerator for each file (i.e. font styles/families) // 3. Pack the data by specifying the PixmapPacker parameter to generateData // Keep hold of the returned BitmapFontData for later // 4. Repeat for other sizes. // 5. Dispose the generator and repeat for other font styles/families // 6. Get the TextureRegion(s) from the packer using packer.updateTextureRegions() // 7. Dispose the PixmapPacker // 8. Use each BitmapFontData to construct a new BitmapFont, and specify your TextureRegion(s) to the font constructor // 9. Dispose of the Texture upon application exit or when you are done using the font atlas // ////////////////////////////////////////////////////////////////////////////////////////////////////// // create the pixmap packer PixmapPacker packer = new PixmapPacker(FONT_ATLAS_WIDTH, FONT_ATLAS_HEIGHT, Format.RGBA8888, 2, false); // we need to load all the BitmapFontDatas before we can start loading BitmapFonts FontMap<BitmapFontData> dataMap = new FontMap<BitmapFontData>(); // for each style... for (FontStyle style : FontStyle.values()) { // get the file for this style FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal(style.path)); // For each size... for (FontSize size : FontSize.values()) { // pack the glyphs into the atlas using the default chars FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); fontParameter.size = size.size; fontParameter.packer = packer; fontParameter.characters = CHARACTERS; BitmapFontData data = gen.generateData(fontParameter); // store the info for later, when we generate the texture dataMap.get(style).put(size, data); } // dispose of the generator once we're finished with this family gen.dispose(); } // Get regions from our packer regions = new Array<TextureRegion>(); packer.updateTextureRegions(regions, TextureFilter.Nearest, TextureFilter.Nearest, false); // No more need for our CPU-based pixmap packer, as our textures are now on GPU packer.dispose(); // Now we can create our fonts... fontMap = new FontMap<BitmapFont>(); int fontCount = 0; // for each style... for (FontStyle style : FontStyle.values()) { // For each size... for (FontSize size : FontSize.values()) { // get the data for this style/size pair BitmapFontData data = dataMap.get(style).get(size); // create a BitmapFont from the data and shared texture BitmapFont bmFont = new BitmapFont(data, regions, INTEGER); // place the font into our map of loaded fonts fontMap.get(style).put(size, bmFont); fontCount++; } } // for the demo, show how many glyphs we loaded return fontCount * CHARACTERS.length(); } // We use a nested EnumMap for fast access class FontMap<T> extends EnumMap<FontStyle, EnumMap<FontSize, T>> { public FontMap () { super(FontStyle.class); // create the enum map for each FontSize for (FontStyle style : FontStyle.values()) { put(style, new EnumMap<FontSize, T>(FontSize.class)); } } } }