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 java.util.EnumMap; 20 21 import com.badlogic.gdx.Gdx; 22 import com.badlogic.gdx.graphics.GL20; 23 import com.badlogic.gdx.graphics.OrthographicCamera; 24 import com.badlogic.gdx.graphics.Pixmap.Format; 25 import com.badlogic.gdx.graphics.Texture.TextureFilter; 26 import com.badlogic.gdx.graphics.g2d.BitmapFont; 27 import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; 28 import com.badlogic.gdx.graphics.g2d.PixmapPacker; 29 import com.badlogic.gdx.graphics.g2d.SpriteBatch; 30 import com.badlogic.gdx.graphics.g2d.TextureRegion; 31 import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; 32 import com.badlogic.gdx.tests.utils.GdxTest; 33 import com.badlogic.gdx.utils.Array; 34 35 /** An advanced example of packing many glyphs into a single texture atlas, using FreeTypeFontGenerator. 36 * 37 * This example uses enum ordinals for fast access to a two-dimensional array, which stores BitmapFonts by size and style. A more 38 * flexible solution might be to use an OjectMap and and IntMap instead. 39 * 40 * @author mattdesl AKA davedes */ 41 public class FreeTypePackTest extends GdxTest { 42 43 // Define font sizes here... 44 static enum FontSize { 45 Tiny(10), Small(12), Medium(16), Large(20), Huge(24), ReallyHuge(28), JustTooBig(64); 46 47 public final int size; 48 FontSize(int size)49 FontSize (int size) { 50 this.size = size; 51 } 52 } 53 54 // Define font styles here... 55 static enum FontStyle { 56 Regular("data/arial.ttf"), Italic("data/arial-italic.ttf"); 57 58 public final String path; 59 FontStyle(String path)60 FontStyle (String path) { 61 this.path = path; 62 } 63 } 64 65 OrthographicCamera camera; 66 SpriteBatch batch; 67 Array<TextureRegion> regions; 68 String text; 69 70 FontMap<BitmapFont> fontMap; 71 72 public static final int FONT_ATLAS_WIDTH = 1024; 73 public static final int FONT_ATLAS_HEIGHT = 512; 74 75 // whether to use integer coords for BitmapFont... 76 private static final boolean INTEGER = false; 77 78 // Our demo doesn't need any fancy characters. 79 // Note: the set in FreeTypeFontGenerator.DEFAULT_CHARS is more extensive 80 // Also note that this string must be contained of unique characters; no duplicates! 81 public static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz\n1234567890" 82 + "\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"; 83 84 @Override create()85 public void create () { 86 camera = new OrthographicCamera(); 87 batch = new SpriteBatch(); 88 89 long start = System.currentTimeMillis(); 90 int glyphCount = createFonts(); 91 long time = System.currentTimeMillis() - start; 92 text = glyphCount + " glyphs packed in " + regions.size + " page(s) in " + time + " ms"; 93 94 } 95 96 @Override render()97 public void render () { 98 Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1); 99 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 100 101 camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 102 batch.setProjectionMatrix(camera.combined); 103 batch.begin(); 104 105 float x = 10; 106 float y = Gdx.graphics.getHeight() - 10; 107 108 int renderCalls = 0; 109 110 // NOTE: Before production release on mobile, you should cache the array from values() 111 // inside the Enum in order to reduce allocations in the render loop. 112 for (FontStyle style : FontStyle.values()) { 113 for (FontSize size : FontSize.values()) { 114 BitmapFont fnt = getFont(style, size); 115 116 fnt.draw(batch, style.name() + " " + size.size + "pt: The quick brown fox jumps over the lazy dog", x, y); 117 y -= fnt.getLineHeight() + 10; 118 } 119 y -= 20; 120 } 121 122 BitmapFont font = getFont(FontStyle.Regular, FontSize.Medium); 123 font.draw(batch, text, 10, font.getCapHeight() + 10); 124 125 // draw all glyphs in background 126 batch.setColor(1f, 1f, 1f, 0.15f); 127 batch.draw(regions.first(), 0, 0); 128 batch.setColor(1f, 1f, 1f, 1f); 129 batch.end(); 130 } 131 132 @Override dispose()133 public void dispose () { 134 super.dispose(); 135 for (TextureRegion r : regions) 136 r.getTexture().dispose(); // dispose the texture since we own it 137 batch.dispose(); 138 } 139 140 // Utility method to grab a font by style/size pair getFont(FontStyle style, FontSize size)141 public BitmapFont getFont (FontStyle style, FontSize size) { 142 return fontMap.get(style).get(size); 143 } 144 createFonts()145 protected int createFonts () { 146 // ////////////////////////////////////////////////////////////////////////////////////////////////////// 147 // //////Steps to use multiple FreeTypeFontGenerators with a single texture atlas:////////////////////// 148 // 1. Create a new PixmapPacker big enough to fit all your desired glyphs 149 // 2. Create a new FreeTypeFontGenerator for each file (i.e. font styles/families) 150 // 3. Pack the data by specifying the PixmapPacker parameter to generateData 151 // Keep hold of the returned BitmapFontData for later 152 // 4. Repeat for other sizes. 153 // 5. Dispose the generator and repeat for other font styles/families 154 // 6. Get the TextureRegion(s) from the packer using packer.updateTextureRegions() 155 // 7. Dispose the PixmapPacker 156 // 8. Use each BitmapFontData to construct a new BitmapFont, and specify your TextureRegion(s) to the font constructor 157 // 9. Dispose of the Texture upon application exit or when you are done using the font atlas 158 // ////////////////////////////////////////////////////////////////////////////////////////////////////// 159 160 // create the pixmap packer 161 PixmapPacker packer = new PixmapPacker(FONT_ATLAS_WIDTH, FONT_ATLAS_HEIGHT, Format.RGBA8888, 2, false); 162 163 // we need to load all the BitmapFontDatas before we can start loading BitmapFonts 164 FontMap<BitmapFontData> dataMap = new FontMap<BitmapFontData>(); 165 166 // for each style... 167 for (FontStyle style : FontStyle.values()) { 168 // get the file for this style 169 FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal(style.path)); 170 171 // For each size... 172 for (FontSize size : FontSize.values()) { 173 // pack the glyphs into the atlas using the default chars 174 FreeTypeFontGenerator.FreeTypeFontParameter fontParameter = new FreeTypeFontGenerator.FreeTypeFontParameter(); 175 fontParameter.size = size.size; 176 fontParameter.packer = packer; 177 fontParameter.characters = CHARACTERS; 178 BitmapFontData data = gen.generateData(fontParameter); 179 180 // store the info for later, when we generate the texture 181 dataMap.get(style).put(size, data); 182 } 183 184 // dispose of the generator once we're finished with this family 185 gen.dispose(); 186 } 187 188 // Get regions from our packer 189 regions = new Array<TextureRegion>(); 190 packer.updateTextureRegions(regions, TextureFilter.Nearest, TextureFilter.Nearest, false); 191 192 // No more need for our CPU-based pixmap packer, as our textures are now on GPU 193 packer.dispose(); 194 195 // Now we can create our fonts... 196 fontMap = new FontMap<BitmapFont>(); 197 198 int fontCount = 0; 199 200 // for each style... 201 for (FontStyle style : FontStyle.values()) { 202 // For each size... 203 for (FontSize size : FontSize.values()) { 204 // get the data for this style/size pair 205 BitmapFontData data = dataMap.get(style).get(size); 206 207 // create a BitmapFont from the data and shared texture 208 BitmapFont bmFont = new BitmapFont(data, regions, INTEGER); 209 210 // place the font into our map of loaded fonts 211 fontMap.get(style).put(size, bmFont); 212 213 fontCount++; 214 } 215 } 216 217 // for the demo, show how many glyphs we loaded 218 return fontCount * CHARACTERS.length(); 219 } 220 221 // We use a nested EnumMap for fast access 222 class FontMap<T> extends EnumMap<FontStyle, EnumMap<FontSize, T>> { 223 FontMap()224 public FontMap () { 225 super(FontStyle.class); 226 227 // create the enum map for each FontSize 228 for (FontStyle style : FontStyle.values()) { 229 put(style, new EnumMap<FontSize, T>(FontSize.class)); 230 } 231 } 232 } 233 } 234