Optimize font loading to happen on startup without blocking UI thread any more than necessary

This commit is contained in:
drdev
2014-05-26 20:31:22 +00:00
parent b0826714d0
commit ae8cb58c33
3 changed files with 74 additions and 58 deletions

View File

@@ -79,7 +79,10 @@ public class Forge implements ApplicationListener {
public void run() {
FModel.initialize(splashScreen.getProgressBar());
splashScreen.getProgressBar().setDescription("Finishing startup...");
splashScreen.getProgressBar().setDescription("Loading fonts");
FSkinFont.preloadAll();
splashScreen.getProgressBar().setDescription("Finishing startup");
Gdx.app.postRunnable(new Runnable() {
@Override

View File

@@ -46,7 +46,6 @@ import forge.sound.IAudioClip;
import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose;
import forge.util.ITriggerEvent;
import forge.util.ThreadUtil;
import forge.util.WaitCallback;
import forge.util.WaitRunnable;
import forge.util.gui.SGuiChoose;
@@ -79,7 +78,7 @@ public class GuiMobile implements IGuiBase {
@Override
public boolean isGuiThread() {
return !ThreadUtil.isGameThread();
return Thread.currentThread().getName().startsWith("LWJGL");
}
@Override

View File

@@ -8,21 +8,22 @@ import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
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.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
import com.badlogic.gdx.utils.Array;
import forge.FThreads;
import forge.util.Utils;
public class FSkinFont {
public static final int MIN_FONT_SIZE = Math.round(8 / Utils.MAX_RATIO);
public static final int MAX_FONT_SIZE = Math.round(72 / Utils.MAX_RATIO);
private static final String TTF_FILE = "font1.ttf";
private static final Map<Integer, FSkinFont> fonts = new HashMap<Integer, FSkinFont>();
private static final int FONT_PAGE_SIZE = 256;
private static final int MAX_FONT_SIZE = 72; //don't generate fonts larger than this, use scaling instead
public static FSkinFont get(final int size0) {
FSkinFont skinFont = fonts.get(size0);
@@ -43,6 +44,13 @@ public class FSkinFont {
}
}
//pre-load all supported font sizes
public static void preloadAll() {
for (int size = MIN_FONT_SIZE; size <= MAX_FONT_SIZE; size++) {
get(size);
}
}
public static void updateAll() {
for (FSkinFont skinFont : fonts.values()) {
skinFont.updateFont();
@@ -66,45 +74,50 @@ public class FSkinFont {
}
private void updateFont() {
float scale = 1;
int fontSize = (int)Utils.scaleMax(size);
try {
if (fontSize > MAX_FONT_SIZE) { //scale if larger than max font size
scale = (float)fontSize / (float)MAX_FONT_SIZE;
fontSize = MAX_FONT_SIZE;
}
String fontName = "f" + fontSize;
FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt");
if (fontFile.exists()) {
font = new BitmapFont(fontFile);
final BitmapFontData data = new BitmapFontData(fontFile, false);
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() { //font must be initialized on UI thread
font = new BitmapFont(data, (TextureRegion)null, true);
}
});
}
else {
FileHandle ttfFile = Gdx.files.absolute(FSkin.getDir() + TTF_FILE);
font = generateFont(ttfFile, fontName, fontSize);
}
}
catch (Exception e) {
e.printStackTrace();
}
if (font == null) {
font = new BitmapFont(); //use scaled default font as fallback
scale = (float)fontSize / 15; //default font has size 15
}
font.setUseIntegerPositions(true); //prevent parts of text getting cut off at times
if (scale != 1) {
font.setScale(scale);
generateFont(ttfFile, fontName, fontSize);
}
}
private BitmapFont generateFont(FileHandle ttfFile, String fontName, int fontSize) {
if (!ttfFile.exists()) { return null; }
private void generateFont(final FileHandle ttfFile, final String fontName, final int fontSize) {
if (!ttfFile.exists()) { return; }
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(ttfFile);
final FreeTypeFontGenerator generator = new FreeTypeFontGenerator(ttfFile);
PixmapPacker packer = new PixmapPacker(FONT_PAGE_SIZE, FONT_PAGE_SIZE, Pixmap.Format.RGBA8888, 2, false);
FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(fontSize, FreeTypeFontGenerator.DEFAULT_CHARS, false, packer);
Array<PixmapPacker.Page> pages = packer.getPages();
TextureRegion[] texRegions = new TextureRegion[pages.size];
//approximate optimal page size
int pageSize;
if (fontSize >= 28) {
pageSize = 256;
}
else {
pageSize = 128;
}
//only generate images for characters that could be used by Forge
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*";
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
final FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(fontSize, chars, false, packer);
final Array<PixmapPacker.Page> pages = packer.getPages();
//finish generating font on UI thread
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
TextureRegion[] textureRegions = new TextureRegion[pages.size];
for (int i = 0; i < pages.size; i++) {
PixmapPacker.Page p = pages.get(i);
Texture texture = new Texture(new PixmapTextureData(p.getPixmap(), p.getPixmap().getFormat(), false, false)) {
@@ -115,10 +128,10 @@ public class FSkinFont {
}
};
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
texRegions[i] = new TextureRegion(texture);
textureRegions[i] = new TextureRegion(texture);
}
BitmapFont font = new BitmapFont(fontData, texRegions, false);
font = new BitmapFont(fontData, textureRegions, true);
//create .fnt and .png files for font
FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt");
@@ -130,6 +143,7 @@ public class FSkinFont {
generator.dispose();
packer.dispose();
return font;
}
});
}
}