Skip to content

Commit

Permalink
BitmapFontData#getGlyphs now takes last glyph in a previous run for b…
Browse files Browse the repository at this point in the history
…etter glyph layout.

closes #4425
closes #4192
#3455
  • Loading branch information
NathanSweet committed May 31, 2018
1 parent fbabb46 commit 9a7dfdf
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- API Change: BitmapFont#getSpaceWidth changed to BitmapFont#getSpaceXadvance.
- Many GlyphLayout fixes.
- API Addition: Added FileHandle#map(), can be used to memory map a file
- API Change: BitmapFontData#getGlyphs changed for better glyph layout. See https://github.com/libgdx/libgdx/commit/a2467728d52d18f3eb29b00e6175d5fa47813425

[1.9.8]
- Add iPhoneX images
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,9 @@ public Glyph getGlyph (char ch) {
return glyph;
}

public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, boolean tightBounds) {
public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, Glyph lastGlyph) {
if (packer != null) packer.setPackToTexture(true); // All glyphs added after this are packed directly to the texture.
super.getGlyphs(run, str, start, end, tightBounds);
super.getGlyphs(run, str, start, end, lastGlyph);
if (dirty) {
dirty = false;
packer.updateTextureRegions(regions, parameter.minFilter, parameter.magFilter, parameter.genMipMaps);
Expand Down
17 changes: 7 additions & 10 deletions gdx/src/com/badlogic/gdx/graphics/g2d/BitmapFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,8 @@ public boolean hasGlyph (char ch) {
}

/** Returns the glyph for the specified character, or null if no such glyph exists. Note that
* {@link #getGlyphs(GlyphRun, CharSequence, int, int, boolean)} should be be used to shape a string of characters into a
* list of glyphs. */
* {@link #getGlyphs(GlyphRun, CharSequence, int, int, Glyph)} should be be used to shape a string of characters into a list
* of glyphs. */
public Glyph getGlyph (char ch) {
Glyph[] page = glyphs[ch / PAGE_SIZE];
if (page != null) return page[ch & PAGE_SIZE - 1];
Expand All @@ -773,10 +773,8 @@ public Glyph getGlyph (char ch) {
/** Using the specified string, populates the glyphs and positions of the specified glyph run.
* @param str Characters to convert to glyphs. Will not contain newline or color tags. May contain "[[" for an escaped left
* square bracket.
* @param tightBounds If true, the first {@link GlyphRun#xAdvances} entry is offset to prevent the first glyph from being
* drawn left of 0 and the last entry is offset to prevent the last glyph from being drawn right of the run
* width. */
public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, boolean tightBounds) {
* @param lastGlyph The glyph immediately before this run, or null if this is run is the first on a line of text. */
public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, Glyph lastGlyph) {
boolean markupEnabled = this.markupEnabled;
float scaleX = this.scaleX;
Glyph missingGlyph = this.missingGlyph;
Expand All @@ -787,7 +785,6 @@ public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, boole
glyphs.ensureCapacity(end - start);
xAdvances.ensureCapacity(end - start + 1);

Glyph lastGlyph = null;
while (start < end) {
char ch = str.charAt(start++);
Glyph glyph = getGlyph(ch);
Expand All @@ -798,8 +795,8 @@ public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, boole

glyphs.add(glyph);

if (lastGlyph == null) // First glyph.
xAdvances.add((!tightBounds || glyph.fixedWidth) ? 0 : -glyph.xoffset * scaleX - padLeft);
if (lastGlyph == null) // First glyph on line, adjust the position so it isn't drawn left of 0.
xAdvances.add(glyph.fixedWidth ? 0 : -glyph.xoffset * scaleX - padLeft);
else
xAdvances.add((lastGlyph.xadvance + lastGlyph.getKerning(ch)) * scaleX);
lastGlyph = glyph;
Expand All @@ -808,7 +805,7 @@ public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, boole
if (markupEnabled && ch == '[' && start < end && str.charAt(start) == '[') start++;
}
if (lastGlyph != null) {
float lastGlyphWidth = (!tightBounds || lastGlyph.fixedWidth) ? lastGlyph.xadvance * scaleX
float lastGlyphWidth = lastGlyph.fixedWidth ? lastGlyph.xadvance * scaleX
: (lastGlyph.width + lastGlyph.xoffset) * scaleX - padRight;
xAdvances.add(lastGlyphWidth);
}
Expand Down
32 changes: 24 additions & 8 deletions gdx/src/com/badlogic/gdx/graphics/g2d/GlyphLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //

float x = 0, y = 0, width = 0;
int lines = 0, blankLines = 0;
Glyph lastGlyph = null;

Array<Color> colorStack = this.colorStack;
Color nextColor = color;
Expand All @@ -107,7 +108,7 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
while (true) {
// Each run is delimited by newline or left square bracket.
int runEnd = -1;
boolean newline = false, colorRun = false;
boolean newline = false;
if (start == end) {
if (runStart == end) break; // End of string with no run to process, we're done.
runEnd = end; // End of string, process last run.
Expand All @@ -126,7 +127,6 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
runEnd = start - 1;
start += length + 1;
nextColor = colorStack.peek();
colorRun = true;
} else if (length == -2) {
start++; // Skip first of "[[" escape sequence.
continue outer;
Expand All @@ -141,14 +141,20 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
if (runEnd != runStart) { // Eg, when a color tag is at text start or a line is "\n".
// Store the run that has ended.
GlyphRun run = glyphRunPool.obtain();
fontData.getGlyphs(run, str, runStart, runEnd, colorRun || runs.size == 0);
fontData.getGlyphs(run, str, runStart, runEnd, lastGlyph);
if (run.glyphs.size == 0) {
glyphRunPool.free(run);
break runEnded;
}
run.color.set(color);
if (lastGlyph != null) {
float lastGlyphWidth = lastGlyph.fixedWidth ? lastGlyph.xadvance * fontData.scaleX
: (lastGlyph.width + lastGlyph.xoffset) * fontData.scaleX - fontData.padRight;
x -= lastGlyphWidth;
}
run.x = x;
run.y = y;
if (newline || runEnd == end) adjustLastGlyph(fontData, run);
runs.add(run);

float[] xAdvances = run.xAdvances.items;
Expand All @@ -159,14 +165,18 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
runWidth += xAdvances[i];
x += runWidth;
run.width = runWidth;
lastGlyph = run.glyphs.peek();
break runEnded;
}

// Wrap or truncate.
int n = run.xAdvances.size;
x += xAdvances[0];
run.width = xAdvances[0];
if (n < 1) break runEnded;
if (n < 1) {
lastGlyph = run.glyphs.peek();
break runEnded;
}
x += xAdvances[1];
run.width += xAdvances[1];
for (int i = 2; i < n; i++) {
Expand Down Expand Up @@ -227,6 +237,7 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
next.y = y;
i = 1;
run = next;
lastGlyph = null;
}
}

Expand All @@ -241,6 +252,7 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
} else
lines++;
y += down;
lastGlyph = null;
}

runStart = start;
Expand Down Expand Up @@ -268,7 +280,7 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
runs.get(lineStart++).x += shift;
lineWidth = 0;
}
lineWidth += run.width;
lineWidth = Math.max(lineWidth, run.x + run.width);
}
float shift = targetWidth - lineWidth;
if (center) shift /= 2;
Expand All @@ -280,15 +292,19 @@ else if (targetWidth <= fontData.spaceXadvance * 3) //
this.height = fontData.capHeight - lines * fontData.down - blankLines * fontData.down * fontData.blankLineScale;
}

/** @param truncate May be empty string. */
private void truncate (BitmapFontData fontData, GlyphRun run, float targetWidth, String truncate, int widthIndex,
Pool<GlyphRun> glyphRunPool) {

// Determine truncate string size.
GlyphRun truncateRun = glyphRunPool.obtain();
fontData.getGlyphs(truncateRun, truncate, 0, truncate.length(), true);
fontData.getGlyphs(truncateRun, truncate, 0, truncate.length(), null);
float truncateWidth = 0;
for (int i = 1, n = truncateRun.xAdvances.size; i < n; i++)
truncateWidth += truncateRun.xAdvances.get(i);
if (truncateRun.xAdvances.size > 0) {
adjustLastGlyph(fontData, truncateRun);
for (int i = 1, n = truncateRun.xAdvances.size; i < n; i++) // Skip first for tight bounds.
truncateWidth += truncateRun.xAdvances.get(i);
}
targetWidth -= truncateWidth;

// Determine visible glyphs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static void main (String[] argv) {
// new SharedLibraryLoader("../../extensions/gdx-controllers/gdx-controllers-desktop/libs/gdx-controllers-desktop-natives.jar").load("gdx-controllers-desktop");
// new SharedLibraryLoader("../../gdx/libs/gdx-natives.jar").load("gdx");

GdxTest test = new GroupTest();
GdxTest test = new LabelTest();
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.r = config.g = config.b = config.a = 8;
// config.width = 320;
Expand Down
10 changes: 5 additions & 5 deletions tests/gdx-tests/src/com/badlogic/gdx/tests/BitmapFontTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

// Test wrapping or truncation with the font directly.
if (!true) {
if (true) {
// BitmapFont font = label.getStyle().font;
BitmapFont font = this.font;
font.getRegion().getTexture().setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
Expand All @@ -108,9 +108,9 @@ public void render () {

spriteBatch.begin();
String text = "your new";
text = "How quickly [RED]daft jumping zebras vex.";
// text = "Another font wrap is-sue, this time with multiple whitespace characters.";
text = "test with AGWlWi AGWlWi issue";
// text = "How quickly da[RED]ft jumping zebras vex.";
text = "Another font wrap is-sue, this time with multiple whitespace characters.";
// text = "test with AGWlWi AGWlWi issue";
if (true) { // Test wrap.
layout.setText(font, text, 0, text.length(), font.getColor(), w, Align.center, true, null);
} else { // Test truncation.
Expand Down Expand Up @@ -138,7 +138,7 @@ public void render () {
label.setStyle(label.getStyle());
label.setText("How quickly [RED]daft[] jumping zebras vex.");
label.setWrap(true);
// label.setEllipsis(true);
// label.setEllipsis(true);
label.setAlignment(Align.center, Align.right);
label.setWidth(Gdx.input.getX() - label.getX());
label.setHeight(label.getPrefHeight());
Expand Down
14 changes: 14 additions & 0 deletions tests/gdx-tests/src/com/badlogic/gdx/tests/LabelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public void create () {
stage = new Stage(new ScreenViewport());
Gdx.input.setInputProcessor(stage);

// skin.getFont("default-font").getData().getGlyph('T').xoffset = -20;
skin.getFont("default-font").getData().getGlyph('B').setKerning('B', -5);

Table table = new Table();
stage.addActor(table);
table.setPosition(200, 65);
Expand All @@ -75,6 +78,17 @@ public void create () {
label5.setWrap(true);
label5.setAlignment(Align.bottom | Align.right);
table.add(label5).minWidth(200 * scale).minHeight(110 * scale).fill();
table.row();

// The color markup text should match the uncolored text exactly.
Label label6 = new Label("AAA BBB CCC DDD EEE", skin);
table.add(label6).align(Align.left);
table.row();
Label label7 = new Label("AAA B[RED]B[]B CCC DDD EEE", skin);
table.add(label7).align(Align.left);
table.row();
Label label8 = new Label("[RED]AAA [BLUE]BBB [RED]CCC [BLUE]DDD [RED]EEE", skin);
table.add(label8).align(Align.left);

table.pack();
}
Expand Down

0 comments on commit 9a7dfdf

Please sign in to comment.