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() { public void run() {
FModel.initialize(splashScreen.getProgressBar()); 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() { Gdx.app.postRunnable(new Runnable() {
@Override @Override

View File

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

View File

@@ -8,21 +8,22 @@ import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont; 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.PixmapPacker;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData; import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import forge.FThreads;
import forge.util.Utils; import forge.util.Utils;
public class FSkinFont { public class FSkinFont {
public static final int MIN_FONT_SIZE = Math.round(8 / Utils.MAX_RATIO); 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 String TTF_FILE = "font1.ttf";
private static final Map<Integer, FSkinFont> fonts = new HashMap<Integer, FSkinFont>(); 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) { public static FSkinFont get(final int size0) {
FSkinFont skinFont = fonts.get(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() { public static void updateAll() {
for (FSkinFont skinFont : fonts.values()) { for (FSkinFont skinFont : fonts.values()) {
skinFont.updateFont(); skinFont.updateFont();
@@ -66,70 +74,76 @@ public class FSkinFont {
} }
private void updateFont() { private void updateFont() {
float scale = 1;
int fontSize = (int)Utils.scaleMax(size); int fontSize = (int)Utils.scaleMax(size);
try { String fontName = "f" + fontSize;
if (fontSize > MAX_FONT_SIZE) { //scale if larger than max font size FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt");
scale = (float)fontSize / (float)MAX_FONT_SIZE; if (fontFile.exists()) {
fontSize = MAX_FONT_SIZE; final BitmapFontData data = new BitmapFontData(fontFile, false);
} FThreads.invokeInEdtNowOrLater(new Runnable() {
String fontName = "f" + fontSize; @Override
FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt"); public void run() { //font must be initialized on UI thread
if (fontFile.exists()) { font = new BitmapFont(data, (TextureRegion)null, true);
font = new BitmapFont(fontFile); }
} });
else {
FileHandle ttfFile = Gdx.files.absolute(FSkin.getDir() + TTF_FILE);
font = generateFont(ttfFile, fontName, fontSize);
}
} }
catch (Exception e) { else {
e.printStackTrace(); FileHandle ttfFile = Gdx.files.absolute(FSkin.getDir() + TTF_FILE);
} generateFont(ttfFile, fontName, fontSize);
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);
} }
} }
private BitmapFont generateFont(FileHandle ttfFile, String fontName, int fontSize) { private void generateFont(final FileHandle ttfFile, final String fontName, final int fontSize) {
if (!ttfFile.exists()) { return null; } 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); //approximate optimal page size
FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(fontSize, FreeTypeFontGenerator.DEFAULT_CHARS, false, packer); int pageSize;
Array<PixmapPacker.Page> pages = packer.getPages(); if (fontSize >= 28) {
TextureRegion[] texRegions = new TextureRegion[pages.size]; pageSize = 256;
for (int i=0; i<pages.size; i++) { }
PixmapPacker.Page p = pages.get(i); else {
Texture texture = new Texture(new PixmapTextureData(p.getPixmap(), p.getPixmap().getFormat(), false, false)) { pageSize = 128;
@Override
public void dispose() {
super.dispose();
getTextureData().consumePixmap().dispose();
}
};
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
texRegions[i] = new TextureRegion(texture);
} }
BitmapFont font = new BitmapFont(fontData, texRegions, false); //only generate images for characters that could be used by Forge
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!?'.,;:()[]{}<>|/@\\^$-%+=#_&*";
//create .fnt and .png files for font final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt"); final FreeTypeFontGenerator.FreeTypeBitmapFontData fontData = generator.generateData(fontSize, chars, false, packer);
FileHandle pixmapDir = Gdx.files.absolute(FSkin.getFontDir()); final Array<PixmapPacker.Page> pages = packer.getPages();
BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text);
String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, fontName); //finish generating font on UI thread
BitmapFontWriter.writeFont(font.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo(fontName, fontSize), 1, 1); 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)) {
@Override
public void dispose() {
super.dispose();
getTextureData().consumePixmap().dispose();
}
};
texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
textureRegions[i] = new TextureRegion(texture);
}
generator.dispose(); font = new BitmapFont(fontData, textureRegions, true);
packer.dispose();
return font; //create .fnt and .png files for font
FileHandle fontFile = Gdx.files.absolute(FSkin.getFontDir() + fontName + ".fnt");
FileHandle pixmapDir = Gdx.files.absolute(FSkin.getFontDir());
BitmapFontWriter.setOutputFormat(BitmapFontWriter.OutputFormat.Text);
String[] pageRefs = BitmapFontWriter.writePixmaps(packer.getPages(), pixmapDir, fontName);
BitmapFontWriter.writeFont(font.getData(), pageRefs, fontFile, new BitmapFontWriter.FontInfo(fontName, fontSize), 1, 1);
generator.dispose();
packer.dispose();
}
});
} }
} }