/******************************************************************************* * 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 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; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.PixmapPacker; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; 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; import java.util.EnumMap; /** * An example of packing many glyphs into a single texture atlas, using FreeTypeFontGenerator. * <p/> * 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 ObjectMap and and IntMap instead. * <p/> * This test uses a less efficient but more convenient way to pack multiple generated fonts into a single texture atlas * compared with FreeTypePackTest - each texture page will be generated and refreshed once. Refreshing textures is slow * on mobile devices. */ public class FreeTypeAtlasTest 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; String text; PixmapPacker packer; 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 " + packer.getPages().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(packer.getPages().first().getTexture(), 0, 0); batch.setColor(1f, 1f, 1f, 1f); batch.end(); } @Override public void dispose() { super.dispose(); packer.dispose(); 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() { // This test uses a less efficient but more convenient way to pack multiple generated fonts into a single // texture atlas. // // 1. Create a new PixmapPacker big enough to fit all your desired glyphs // 2. Create a new FreeTypeFontGenerator for each TTF file (i.e. font styles/families) // 3. For each size and style, call generator.generateFont() with the packer set on the parameter // 4. Generate the texture atlas using packer.generateTextureAtlas or packer.updateTextureAtlas. // 5. Dispose of the atlas upon application exit or when you are done using the fonts // ////////////////////////////////////////////////////////////////////////////////////////////////////// // create the pixmap packer packer = new PixmapPacker(FONT_ATLAS_WIDTH, FONT_ATLAS_HEIGHT, Format.RGBA8888, 2, false); fontMap = new FontMap<BitmapFont>(); int fontCount = 0; // 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; BitmapFont bmFont = gen.generateFont(fontParameter); fontMap.get(style).put(size, bmFont); fontCount++; } // dispose of the generator once we're finished with this family gen.dispose(); } // 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)); } } } }