1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.tests.extensions; 18 19 import com.badlogic.gdx.Gdx; 20 import com.badlogic.gdx.graphics.GL20; 21 import com.badlogic.gdx.graphics.OrthographicCamera; 22 import com.badlogic.gdx.graphics.Pixmap.Format; 23 import com.badlogic.gdx.graphics.Texture; 24 import com.badlogic.gdx.graphics.g2d.BitmapFont; 25 import com.badlogic.gdx.graphics.g2d.PixmapPacker; 26 import com.badlogic.gdx.graphics.g2d.SpriteBatch; 27 import com.badlogic.gdx.graphics.g2d.TextureAtlas; 28 import com.badlogic.gdx.graphics.g2d.TextureRegion; 29 import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; 30 import com.badlogic.gdx.tests.utils.GdxTest; 31 import com.badlogic.gdx.utils.Array; 32 33 import java.util.EnumMap; 34 35 /** 36 * An example of packing many glyphs into a single texture atlas, using FreeTypeFontGenerator. 37 * <p/> 38 * This example uses enum ordinals for fast access to a two-dimensional array, which stores BitmapFonts by size and style. A more 39 * flexible solution might be to use an ObjectMap and and IntMap instead. 40 * <p/> 41 * This test uses a less efficient but more convenient way to pack multiple generated fonts into a single texture atlas 42 * compared with FreeTypePackTest - each texture page will be generated and refreshed once. Refreshing textures is slow 43 * on mobile devices. 44 */ 45 public class FreeTypeAtlasTest extends GdxTest { 46 47 // Define font sizes here... 48 static enum FontSize { 49 Tiny(10), Small(12), Medium(16), Large(20), Huge(24), ReallyHuge(28), JustTooBig(64); 50 51 public final int size; 52 FontSize(int size)53 FontSize(int size) { 54 this.size = size; 55 } 56 } 57 58 // Define font styles here... 59 static enum FontStyle { 60 Regular("data/arial.ttf"), Italic("data/arial-italic.ttf"); 61 62 public final String path; 63 FontStyle(String path)64 FontStyle(String path) { 65 this.path = path; 66 } 67 } 68 69 OrthographicCamera camera; 70 SpriteBatch batch; 71 String text; 72 PixmapPacker packer; 73 FontMap<BitmapFont> fontMap; 74 75 public static final int FONT_ATLAS_WIDTH = 1024; 76 public static final int FONT_ATLAS_HEIGHT = 512; 77 78 // whether to use integer coords for BitmapFont... 79 private static final boolean INTEGER = false; 80 81 // Our demo doesn't need any fancy characters. 82 // Note: the set in FreeTypeFontGenerator.DEFAULT_CHARS is more extensive 83 // Also note that this string must be contained of unique characters; no duplicates! 84 public static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz\n1234567890" 85 + "\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"; 86 87 @Override create()88 public void create() { 89 camera = new OrthographicCamera(); 90 batch = new SpriteBatch(); 91 92 long start = System.currentTimeMillis(); 93 int glyphCount = createFonts(); 94 long time = System.currentTimeMillis() - start; 95 text = glyphCount + " glyphs packed in " + packer.getPages().size + " page(s) in " + time + " ms"; 96 97 } 98 99 @Override render()100 public void render() { 101 Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1); 102 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 103 104 camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 105 batch.setProjectionMatrix(camera.combined); 106 batch.begin(); 107 108 float x = 10; 109 float y = Gdx.graphics.getHeight() - 10; 110 111 int renderCalls = 0; 112 113 // NOTE: Before production release on mobile, you should cache the array from values() 114 // inside the Enum in order to reduce allocations in the render loop. 115 for (FontStyle style : FontStyle.values()) { 116 for (FontSize size : FontSize.values()) { 117 BitmapFont fnt = getFont(style, size); 118 119 fnt.draw(batch, style.name() + " " + size.size + "pt: The quick brown fox jumps over the lazy dog", x, y); 120 y -= fnt.getLineHeight() + 10; 121 } 122 y -= 20; 123 } 124 125 BitmapFont font = getFont(FontStyle.Regular, FontSize.Medium); 126 font.draw(batch, text, 10, font.getCapHeight() + 10); 127 128 // draw all glyphs in background 129 batch.setColor(1f, 1f, 1f, 0.15f); 130 batch.draw(packer.getPages().first().getTexture(), 0, 0); 131 batch.setColor(1f, 1f, 1f, 1f); 132 batch.end(); 133 } 134 135 @Override dispose()136 public void dispose() { 137 super.dispose(); 138 packer.dispose(); 139 batch.dispose(); 140 } 141 142 // Utility method to grab a font by style/size pair getFont(FontStyle style, FontSize size)143 public BitmapFont getFont(FontStyle style, FontSize size) { 144 return fontMap.get(style).get(size); 145 } 146 createFonts()147 protected int createFonts() { 148 // This test uses a less efficient but more convenient way to pack multiple generated fonts into a single 149 // texture atlas. 150 // 151 // 1. Create a new PixmapPacker big enough to fit all your desired glyphs 152 // 2. Create a new FreeTypeFontGenerator for each TTF file (i.e. font styles/families) 153 // 3. For each size and style, call generator.generateFont() with the packer set on the parameter 154 // 4. Generate the texture atlas using packer.generateTextureAtlas or packer.updateTextureAtlas. 155 // 5. Dispose of the atlas upon application exit or when you are done using the fonts 156 // ////////////////////////////////////////////////////////////////////////////////////////////////////// 157 158 // create the pixmap packer 159 packer = new PixmapPacker(FONT_ATLAS_WIDTH, FONT_ATLAS_HEIGHT, Format.RGBA8888, 2, false); 160 161 fontMap = new FontMap<BitmapFont>(); 162 int fontCount = 0; 163 164 // for each style... 165 for (FontStyle style : FontStyle.values()) { 166 // get the file for this style 167 FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal(style.path)); 168 169 // For each size... 170 for (FontSize size : FontSize.values()) { 171 // pack the glyphs into the atlas using the default chars 172 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); 173 fontParameter.size = size.size; 174 fontParameter.packer = packer; 175 fontParameter.characters = CHARACTERS; 176 BitmapFont bmFont = gen.generateFont(fontParameter); 177 178 fontMap.get(style).put(size, bmFont); 179 fontCount++; 180 } 181 182 // dispose of the generator once we're finished with this family 183 gen.dispose(); 184 } 185 186 // for the demo, show how many glyphs we loaded 187 return fontCount * CHARACTERS.length(); 188 } 189 190 // We use a nested EnumMap for fast access 191 class FontMap<T> extends EnumMap<FontStyle, EnumMap<FontSize, T>> { 192 FontMap()193 public FontMap() { 194 super(FontStyle.class); 195 196 // create the enum map for each FontSize 197 for (FontStyle style : FontStyle.values()) { 198 put(style, new EnumMap<FontSize, T>(FontSize.class)); 199 } 200 } 201 } 202 } 203