v0.7.5b: first major portion of rewriting text rendering

some rough edges still need smoothing out
This commit is contained in:
Evan Debenham 2019-10-10 22:20:23 -04:00
parent bbee1a1372
commit b985ed3bf5
14 changed files with 393 additions and 214 deletions

View File

@ -18,4 +18,5 @@ dependencies {
api "com.badlogicgames.gdx:gdx:$gdxVersion"
api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-controllers:$gdxVersion"
}
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
}

View File

@ -60,6 +60,10 @@ public class Texture {
}
}
public static void clear(){
bound_id = 0;
}
public void filter( int minMode, int maxMode ) {
bind();
Gdx.gl.glTexParameterf( Gdx.gl.GL_TEXTURE_2D, Gdx.gl.GL_TEXTURE_MIN_FILTER, minMode );

View File

@ -105,7 +105,6 @@ public class Game implements ApplicationListener {
//refreshes texture and vertex data stored on the gpu
TextureCache.reload();
RenderedText.reloadCache();
Vertexbuffer.refreshAllBuffers();
}

View File

@ -21,239 +21,212 @@
package com.watabou.noosa;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.graphics.Pixmap;
import com.watabou.gltextures.SmartTexture;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Affine2;
import com.badlogic.gdx.math.Matrix4;
import com.watabou.glwrap.Matrix;
import com.watabou.glwrap.Texture;
import com.watabou.utils.RectF;
import com.watabou.glwrap.Quad;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.nio.FloatBuffer;
import java.util.HashMap;
public class RenderedText extends Image {
private static Canvas canvas = new Canvas();
private static Paint painter = new Paint();
private static Typeface font;
//this is basically a LRU cache. capacity is determined by character count, not entry count.
//FIXME: Caching based on words is very inefficient for every language but chinese.
private static LinkedHashMap<String, CachedText> textCache = new LinkedHashMap<>(700, 0.75f, true);
private static int cachedChars = 0;
private static final int GC_TRIGGER = 1250;
private static final int GC_TARGET = 1000;
private static void runGC(){
Iterator<Map.Entry<String, CachedText>> it = textCache.entrySet().iterator();
while (cachedChars > GC_TARGET && it.hasNext()){
CachedText cached = it.next().getValue();
if (cached.activeTexts.isEmpty()) {
cachedChars -= cached.length;
cached.texture.delete();
it.remove();
}
}
}
private BitmapFont font = null;
private int size;
private String text;
private CachedText cache;
private boolean needsRender = false;
public RenderedText( ){
public RenderedText( ) {
text = null;
}
public RenderedText( int size ){
text = null;
this.size = size;
}
public RenderedText(String text, int size){
this.text = text;
this.size = size;
needsRender = true;
measure(this);
measure();
}
public void text( String text ){
this.text = text;
needsRender = true;
measure(this);
measure();
}
public String text(){
return text;
}
public void size( int size ){
this.size = size;
needsRender = true;
measure(this);
measure();
}
public float baseLine(){
return size * scale.y;
}
private static synchronized void measure(RenderedText r){
if ( r.text == null || r.text.equals("") ) {
r.text = "";
r.width=r.height=0;
r.visible = false;
private synchronized void measure(){
if (Thread.currentThread().getName().equals("SHPD Actor Thread")){
throw new RuntimeException("Text measured from the actor thread!");
}
if ( text == null || text.equals("") ) {
text = "";
width=height=0;
visible = false;
return;
} else {
r.visible = true;
visible = true;
}
painter.setTextSize(r.size);
painter.setAntiAlias(true);
if (font != null) {
painter.setTypeface(font);
} else {
painter.setTypeface(Typeface.DEFAULT);
}
//paint outer strokes
painter.setARGB(0xff, 0, 0, 0);
painter.setStyle(Paint.Style.STROKE);
painter.setStrokeWidth(r.size / 5f);
r.width = (painter.measureText(r.text)+ (r.size/5f));
r.height = (-painter.ascent() + painter.descent()+ (r.size/5f));
font = Game.platform.getFont(size, text);
GlyphLayout l = new GlyphLayout( font, text);
width = l.width;
//TODO this is almost the same as old height, but old height was clearly a bit off
height = size*1.375f;
//height = l.height - fonts.get(fontGenerator).get(size).getDescent() + size/5f;
}
private static synchronized void render(RenderedText r){
r.needsRender = false;
if (r.cache != null)
r.cache.activeTexts.remove(r);
String key = "text:" + r.size + " " + r.text;
if (textCache.containsKey(key)){
r.cache = textCache.get(key);
r.texture = r.cache.texture;
r.frame(r.cache.rect);
r.cache.activeTexts.add(r);
} else {
measure(r);
if (r.width == 0 || r.height == 0)
return;
//bitmap has to be in a power of 2 for some devices (as we're using openGL methods to render to texture)
Bitmap bitmap = Bitmap.createBitmap(Integer.highestOneBit((int)r.width)*2, Integer.highestOneBit((int)r.height)*2, Bitmap.Config.ARGB_4444);
bitmap.eraseColor(0x00000000);
canvas.setBitmap(bitmap);
canvas.drawText(r.text, (r.size/10f), r.size, painter);
//paint inner text
painter.setARGB(0xff, 0xff, 0xff, 0xff);
painter.setStyle(Paint.Style.FILL);
canvas.drawText(r.text, (r.size/10f), r.size, painter);
//FIXME really ugly and slow conversion between android bitmap and gdx pixmap
int[] pixels = new int[bitmap.getWidth()*bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// Convert from ARGB to RGBA
for (int i = 0; i< pixels.length; i++) {
int pixel = pixels[i];
pixels[i] = (pixel << 8) | ((pixel >> 24) & 0xFF);
}
Pixmap pixmap = new Pixmap(bitmap.getWidth(), bitmap.getHeight(), Pixmap.Format.RGBA8888);
pixmap.getPixels().asIntBuffer().put(pixels);
r.texture = new SmartTexture(pixmap, Texture.NEAREST, Texture.CLAMP, true);
bitmap.recycle();
RectF rect = r.texture.uvRect(0, 0, r.width, r.height);
r.frame(rect);
r.cache = new CachedText();
r.cache.rect = rect;
r.cache.texture = r.texture;
r.cache.length = r.text.length();
r.cache.activeTexts = new HashSet<>();
r.cache.activeTexts.add(r);
cachedChars += r.cache.length;
textCache.put("text:" + r.size + " " + r.text, r.cache);
if (cachedChars >= GC_TRIGGER){
runGC();
}
}
}
@Override
protected void updateMatrix() {
super.updateMatrix();
//the y value is set at the top of the character, not at the top of accents.
Matrix.translate( matrix, 0, -Math.round((baseLine()*0.15f)/scale.y) );
//FIXME this doesn't work for .otf fonts on android 6.0
Matrix.translate( matrix, 0, Math.round((baseLine()*0.1f)/scale.y) );
}
private static TextRenderBatch textRenderer = new TextRenderBatch();
@Override
public void draw() {
if (needsRender)
render(this);
if (texture != null)
super.draw();
public synchronized void draw() {
updateMatrix();
TextRenderBatch.textBeingRendered = this;
font.draw(textRenderer, text, 0, 0);
}
@Override
public void destroy() {
if (cache != null)
cache.activeTexts.remove(this);
super.destroy();
}
public static void clearCache(){
for (CachedText cached : textCache.values()){
cached.texture.delete();
//implements regular PD rendering within a LibGDX batch so that our rendering logic
//can interface with the freetype font generator
private static class TextRenderBatch implements Batch {
//this isn't as good as only updating once, like with bitmaptext
// but it skips almost all allocations, which is almost as good
private static RenderedText textBeingRendered = null;
private static float[] vertices = new float[16];
private static HashMap<Integer, FloatBuffer> buffers = new HashMap<>();
@Override
public void draw(Texture texture, float[] spriteVertices, int offset, int count) {
Visual v = textBeingRendered;
FloatBuffer toOpenGL;
if (buffers.containsKey(count/20)){
toOpenGL = buffers.get(count/20);
toOpenGL.position(0);
} else {
toOpenGL = Quad.createSet(count / 20);
buffers.put(count/20, toOpenGL);
}
for (int i = 0; i < count; i += 20){
vertices[0] = spriteVertices[i+0];
vertices[1] = spriteVertices[i+1];
vertices[2] = spriteVertices[i+3];
vertices[3] = spriteVertices[i+4];
vertices[4] = spriteVertices[i+5];
vertices[5] = spriteVertices[i+6];
vertices[6] = spriteVertices[i+8];
vertices[7] = spriteVertices[i+9];
vertices[8] = spriteVertices[i+10];
vertices[9] = spriteVertices[i+11];
vertices[10] = spriteVertices[i+13];
vertices[11] = spriteVertices[i+14];
vertices[12] = spriteVertices[i+15];
vertices[13] = spriteVertices[i+16];
vertices[14] = spriteVertices[i+18];
vertices[15] = spriteVertices[i+19];
toOpenGL.put(vertices);
}
toOpenGL.position(0);
NoosaScript script = NoosaScript.get();
texture.bind();
com.watabou.glwrap.Texture.clear();
script.camera( v.camera() );
script.uModel.valueM4( v.matrix );
script.lighting(
v.rm, v.gm, v.bm, v.am,
v.ra, v.ga, v.ba, v.aa );
script.drawQuadSet( toOpenGL, count/20 );
}
cachedChars = 0;
textCache.clear();
}
public static void reloadCache(){
for (CachedText txt : textCache.values()){
txt.texture.reload();
}
}
public static void setFont(String asset){
if (asset == null) font = null;
else font = Typeface.createFromAsset(((AndroidApplication)Gdx.app).getAssets(), asset);
clearCache();
}
public static Typeface getFont(){
return font;
}
private static class CachedText{
public SmartTexture texture;
public RectF rect;
public int length;
public HashSet<RenderedText> activeTexts;
//none of these functions are needed, so they are stubbed
@Override
public void begin() { }
public void end() { }
public void setColor(Color tint) { }
public void setColor(float r, float g, float b, float a) { }
public Color getColor() { return null; }
public void setPackedColor(float packedColor) { }
public float getPackedColor() { return 0; }
public void draw(Texture texture, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY) { }
public void draw(Texture texture, float x, float y, float width, float height, int srcX, int srcY, int srcWidth, int srcHeight, boolean flipX, boolean flipY) { }
public void draw(Texture texture, float x, float y, int srcX, int srcY, int srcWidth, int srcHeight) { }
public void draw(Texture texture, float x, float y, float width, float height, float u, float v, float u2, float v2) { }
public void draw(Texture texture, float x, float y) { }
public void draw(Texture texture, float x, float y, float width, float height) { }
public void draw(TextureRegion region, float x, float y) { }
public void draw(TextureRegion region, float x, float y, float width, float height) { }
public void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation) { }
public void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation, boolean clockwise) { }
public void draw(TextureRegion region, float width, float height, Affine2 transform) { }
public void flush() { }
public void disableBlending() { }
public void enableBlending() { }
public void setBlendFunction(int srcFunc, int dstFunc) { }
public void setBlendFunctionSeparate(int srcFuncColor, int dstFuncColor, int srcFuncAlpha, int dstFuncAlpha) { }
public int getBlendSrcFunc() { return 0; }
public int getBlendDstFunc() { return 0; }
public int getBlendSrcFuncAlpha() { return 0; }
public int getBlendDstFuncAlpha() { return 0; }
public Matrix4 getProjectionMatrix() { return null; }
public Matrix4 getTransformMatrix() { return null; }
public void setProjectionMatrix(Matrix4 projection) { }
public void setTransformMatrix(Matrix4 transform) { }
public void setShader(ShaderProgram shader) { }
public ShaderProgram getShader() { return null; }
public boolean isBlendingEnabled() { return false; }
public boolean isDrawing() { return false; }
public void dispose() { }
}
}

View File

@ -21,6 +21,8 @@
package com.watabou.utils;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
public abstract class PlatformSupport {
public abstract void updateDisplaySize();
@ -35,5 +37,13 @@ public abstract class PlatformSupport {
public static abstract class TextCallback {
public abstract void onSelect( boolean positive, String text );
}
//TODO should consider spinning this into its own class, rather than platform support getting ever bigger
public abstract void setupFontGenerators(int pageSize, boolean systemFont );
public abstract void resetGenerators();
public abstract BitmapFont getFont(int size, String text);
}

3
android/.gitignore vendored
View File

@ -1,2 +1,3 @@
#LibGDX native dependancies
libgdx.so
libgdx.so
libgdx-freetype.so

View File

@ -69,6 +69,7 @@ dependencies {
implementation project(':core')
implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
@ -76,6 +77,11 @@ dependencies {
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
implementation "com.badlogicgames.gdx:gdx-controllers:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-controllers-android:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"
}
// called every time gradle gets executed, takes the native dependencies of

View File

@ -27,12 +27,20 @@ import android.os.Build;
import android.view.View;
import android.view.WindowManager;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.PixmapPacker;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.android.windows.WndAndroidTextInput;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.watabou.noosa.Game;
import com.watabou.utils.PlatformSupport;
import java.util.HashMap;
import java.util.regex.Pattern;
public class AndroidPlatformSupport extends PlatformSupport {
public void updateDisplaySize(){
@ -140,4 +148,147 @@ public class AndroidPlatformSupport extends PlatformSupport {
}
});
}
/* FONT SUPPORT */
private int pageSize;
private PixmapPacker packer;
private boolean systemfont;
//custom ttf or droid sans, for use with Latin and Cyrillic languages
private static FreeTypeFontGenerator latinAndCryllicFontGenerator;
private static HashMap<Integer, BitmapFont> pixelFonts = new HashMap<>();
//droid sans, for use with hangul languages (Korean)
private static FreeTypeFontGenerator hangulFontGenerator;
private static HashMap<Integer, BitmapFont> hangulFonts = new HashMap<>();
//droid sans, for use with han languages (Chinese, Japanese)
private static FreeTypeFontGenerator hanFontGenerator;
private static HashMap<Integer, BitmapFont> hanFonts = new HashMap<>();
private static HashMap<FreeTypeFontGenerator, HashMap<Integer, BitmapFont>> fonts;
public static Pattern hanMatcher = Pattern.compile("\\p{InHiragana}|\\p{InKatakana}|\\p{InCJK_Unified_Ideographs}|\\p{InCJK_Symbols_and_Punctuation}");
public static Pattern hangulMatcher = Pattern.compile("\\p{InHangul_Syllables}");
@Override
public void setupFontGenerators(int pageSize, boolean systemfont) {
//don't bother doing anything if nothing has changed
if (fonts != null && this.pageSize == pageSize && this.systemfont == systemfont){
return;
}
this.pageSize = pageSize;
this.systemfont = systemfont;
if (fonts != null){
for (FreeTypeFontGenerator generator : fonts.keySet()){
for (BitmapFont f : fonts.get(generator).values()){
f.dispose();
}
fonts.get(generator).clear();
generator.dispose();
}
fonts.clear();
if (packer != null){
for (PixmapPacker.Page p : packer.getPages()){
p.getTexture().dispose();
}
packer.dispose();
}
}
fonts = new HashMap<>();
if (systemfont){
latinAndCryllicFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/DroidSans.ttf"));
} else {
//FIXME need to add currency symbols
latinAndCryllicFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("pixelfont.ttf"));
}
//android 7.0+. Finally back to normalcy, everything nicely in one .ttc
if (Gdx.files.absolute("/system/fonts/NotoSansCJK-Regular.ttc").exists()) {
//TODO why typeface #2 here? It seems to match DroidSansFallback.ttf the best, but why?
//might be that different languages prefer a different face, see about tweaking this?
hangulFontGenerator = hanFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/NotoSansCJK-Regular.ttc"), 2);
//android 6.0. Fonts are split over multiple .otf files, very awkward
} else if (Gdx.files.absolute("/system/fonts/NotoSansKR-Regular.otf").exists()) {
//FIXME all fonts are messed up here currently, need to fix this
hangulFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/NotoSansKR-Regular.otf"));
hanFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/NotoSansSC-Regular.otf"));
//android 4.4-5.1. Korean no longer broken with the addition of NanumGothic.
} else if (Gdx.files.absolute("/system/fonts/NanumGothic.ttf").exists()){
hangulFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/NanumGothic.ttf"));
hanFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/DroidSansFallback.ttf"));
//android 4.3-. Note that korean isn't in DroidSandFallback and is therefore unfixably broken on 4.2 and 4.3
} else if (Gdx.files.absolute("/system/fonts/DroidSansFallback.ttf").exists()) {
hangulFontGenerator = hanFontGenerator = new FreeTypeFontGenerator(Gdx.files.absolute("/system/fonts/DroidSansFallback.ttf"));
//shouldn't ever trigger, but just incase
} else {
hangulFontGenerator = hanFontGenerator = latinAndCryllicFontGenerator;
}
fonts.put(latinAndCryllicFontGenerator, pixelFonts);
fonts.put(hangulFontGenerator, hangulFonts);
fonts.put(hanFontGenerator, hanFonts);
//use RGBA4444 to save memory. Extra precision isn't needed here.
packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA4444, 1, false);
}
@Override
public void resetGenerators() {
for (FreeTypeFontGenerator generator : fonts.keySet()){
for (BitmapFont f : fonts.get(generator).values()){
f.dispose();
}
fonts.get(generator).clear();
generator.dispose();
}
fonts.clear();
if (packer != null){
for (PixmapPacker.Page p : packer.getPages()){
p.getTexture().dispose();
}
packer.dispose();
}
fonts = null;
setupFontGenerators(pageSize, systemfont);
}
private static FreeTypeFontGenerator getGeneratorForString( String input ){
if (hanMatcher.matcher(input).find()){
return hanFontGenerator;
} else if (hangulMatcher.matcher(input).find()){
return hangulFontGenerator;
} else {
return latinAndCryllicFontGenerator;
}
}
@Override
public BitmapFont getFont(int size, String text) {
FreeTypeFontGenerator generator = getGeneratorForString(text);
if (!fonts.get(generator).containsKey(size)) {
FreeTypeFontGenerator.FreeTypeFontParameter parameters = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameters.size = size;
parameters.flip = true;
parameters.borderWidth = parameters.size / 10f;
parameters.renderCount = 3;
parameters.hinting = FreeTypeFontGenerator.Hinting.None;
parameters.spaceX = -(int) parameters.borderWidth;
parameters.incremental = true;
parameters.characters = "";
parameters.packer = packer;
fonts.get(generator).put(size, generator.generateFont(parameters));
}
return fonts.get(generator).get(size);
}
}

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.android.windows;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.InputFilter;
import android.text.InputType;
import android.util.TypedValue;
@ -39,12 +40,12 @@ import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidGraphics;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.android.AndroidLauncher;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.noosa.Game;
import com.watabou.noosa.RenderedText;
//This class makes use of the android EditText component to handle text input
//FIXME this window is currently android-specific, should generalize it
@ -96,7 +97,9 @@ public class WndAndroidTextInput extends Window {
textInput = new EditText((AndroidApplication)Gdx.app);
textInput.setText( initialValue );
textInput.setTypeface( RenderedText.getFont() );
if (!SPDSettings.systemFont()){
textInput.setTypeface( Typeface.createFromAsset(AndroidLauncher.instance.getAssets(), "pixelfont.ttf") );
}
textInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
textInput.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES );

View File

@ -251,16 +251,12 @@ public class SPDSettings extends GameSettings {
public static void systemFont(boolean value){
put(KEY_SYSTEMFONT, value);
if (!value) {
RenderedText.setFont("pixelfont.ttf");
} else {
RenderedText.setFont( null );
}
ShatteredPixelDungeon.seamlessResetScene();
}
public static boolean systemFont(){
return getBoolean(KEY_SYSTEMFONT,
(language() == Languages.KOREAN || language() == Languages.CHINESE));
(language() == Languages.KOREAN || language() == Languages.CHINESE || language() == Languages.JAPANESE));
}
}

View File

@ -173,11 +173,6 @@ public class ShatteredPixelDungeon extends Game {
Assets.SND_DEGRADE,
Assets.SND_MIMIC );
if (!SPDSettings.systemFont()) {
RenderedText.setFont("pixelfont.ttf");
} else {
RenderedText.setFont( null );
}
}

View File

@ -25,6 +25,8 @@ import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.effects.BadgeBanner;
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.watabou.glwrap.Blending;
@ -133,6 +135,24 @@ public class PixelScene extends Scene {
font2x.tracking = -4;
font2x.texture.filter(Texture.LINEAR, Texture.NEAREST);*/
}
//set up the texture size which rendered text will use for any new glyphs.
int renderedTextPageSize;
if (defaultZoom <= 3){
renderedTextPageSize = 256;
} else if (defaultZoom <= 8){
renderedTextPageSize = 512;
} else {
renderedTextPageSize = 1024;
}
//asian languages have many more unique characters, so increase texture size to anticipate that
if (Messages.lang() == Languages.KOREAN ||
Messages.lang() == Languages.CHINESE ||
Messages.lang() == Languages.JAPANESE){
renderedTextPageSize *= 2;
}
Game.platform.setupFontGenerators(renderedTextPageSize, SPDSettings.systemFont());
}
//FIXME this system currently only works for a subset of windows

View File

@ -29,13 +29,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
//TODO gdx-freetype can manage multi-line layouts for us, and is more efficient. Should consider migrating to that.
public class RenderedTextMultiline extends Component {
private int maxWidth = Integer.MAX_VALUE;
public int nLines;
private String text;
private List<String> tokens;
private String[] tokens;
private ArrayList<RenderedText> words = new ArrayList<>();
private int size;
@ -66,9 +67,9 @@ public class RenderedTextMultiline extends Component {
chinese = text.replaceAll("\\p{Han}", "").length() != text.length();
if (chinese){
tokens = Arrays.asList(text.split(""));
tokens = text.split("");
} else {
tokens = Arrays.asList(text.split("(?<= )|(?= )|(?<=\n)|(?=\n)"));
tokens = text.split("(?<= )|(?= )|(?<=\n)|(?=\n)");
}
build();
}
@ -82,6 +83,10 @@ public class RenderedTextMultiline extends Component {
public String text(){
return text;
}
public float baseLine(){
return size * zoom;
}
public void maxWidth(int maxWidth){
if (this.maxWidth != maxWidth){
@ -189,8 +194,9 @@ public class RenderedTextMultiline extends Component {
word.y = y;
PixelScene.align(word);
x += word.width();
if (!chinese) x ++;
else x--;
else x -= 0.5f;
if ((x - this.x) > width) width = (x - this.x);

View File

@ -73,7 +73,6 @@ public class WndLangs extends Window {
@Override
public void beforeCreate() {
SPDSettings.language(langs.get(langIndex));
RenderedText.clearCache();
}
@Override
public void afterCreate() {
@ -237,5 +236,20 @@ public class WndLangs extends Window {
}
}
@Override
public void hide() {
super.hide();
//resets generators because there's no need to retain chars for languages not selected
ShatteredPixelDungeon.seamlessResetScene(new Game.SceneChangeCallback() {
@Override
public void beforeCreate() {
Game.platform.resetGenerators();
}
@Override
public void afterCreate() {
//do nothing
}
});
}
}