• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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