Skip to content

Commit f7f3dc6

Browse files
authored
Update BitmapFont.java
1 parent fe4a847 commit f7f3dc6

File tree

1 file changed

+140
-57
lines changed

1 file changed

+140
-57
lines changed

jme3-core/src/main/java/com/jme3/font/BitmapFont.java

Lines changed: 140 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -31,14 +31,22 @@
3131
*/
3232
package com.jme3.font;
3333

34-
import com.jme3.export.*;
34+
import com.jme3.export.InputCapsule;
35+
import com.jme3.export.JmeExporter;
36+
import com.jme3.export.JmeImporter;
37+
import com.jme3.export.OutputCapsule;
38+
import com.jme3.export.Savable;
3539
import com.jme3.material.Material;
3640

3741
import java.io.IOException;
3842

3943
/**
40-
* Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
44+
* Represents a font loaded from a bitmap font definition
45+
* (e.g., generated by <a href="https://libgdx.com/wiki/tools/hiero">AngelCode Bitmap Font Generator</a>).
46+
* It manages character sets, font pages (textures), and provides utilities for text measurement and rendering.
47+
*
4148
* @author dhdd
49+
* @author Yonghoon
4250
*/
4351
public class BitmapFont implements Savable {
4452

@@ -87,61 +95,112 @@ public enum VAlign {
8795
Bottom
8896
}
8997

98+
// The character set containing definitions for each character (glyph) in the font.
9099
private BitmapCharacterSet charSet;
100+
// An array of materials, where each material corresponds to a font page (texture).
91101
private Material[] pages;
102+
// Indicates whether this font is designed for right-to-left (RTL) text rendering.
92103
private boolean rightToLeft = false;
93104
// For cursive bitmap fonts in which letter shape is determined by the adjacent glyphs.
94105
private GlyphParser glyphParser;
95106

96107
/**
97-
* @return true, if this is a right-to-left font, otherwise it will return false.
108+
* Creates a new instance of `BitmapFont`.
109+
* This constructor is primarily used for deserialization.
98110
*/
99-
public boolean isRightToLeft() {
100-
return rightToLeft;
111+
public BitmapFont() {
101112
}
102113

103114
/**
104-
* Specify if this is a right-to-left font. By default it is set to false.
105-
* This can be "overwritten" in the BitmapText constructor.
115+
* Creates a new {@link BitmapText} instance initialized with this font.
116+
* The label's size will be set to the font's rendered size, and its text content
117+
* will be set to the provided string.
106118
*
107-
* @param rightToLeft true &rarr; right-to-left, false &rarr; left-to-right
108-
* (default=false)
119+
* @param content The initial text content for the label.
120+
* @return A new {@link BitmapText} instance.
109121
*/
110-
public void setRightToLeft(boolean rightToLeft) {
111-
this.rightToLeft = rightToLeft;
112-
}
113-
114-
public BitmapFont() {
115-
}
116-
117122
public BitmapText createLabel(String content) {
118123
BitmapText label = new BitmapText(this);
119124
label.setSize(getCharSet().getRenderedSize());
120125
label.setText(content);
121126
return label;
122127
}
123128

129+
/**
130+
* Checks if this font is configured for right-to-left (RTL) text rendering.
131+
*
132+
* @return true if this is a right-to-left font, otherwise false (default is left-to-right).
133+
*/
134+
public boolean isRightToLeft() {
135+
return rightToLeft;
136+
}
137+
138+
/**
139+
* Specifies whether this font should be rendered as right-to-left (RTL).
140+
* By default, it is set to false (left-to-right).
141+
*
142+
* @param rightToLeft true to enable right-to-left rendering; false for left-to-right.
143+
*/
144+
public void setRightToLeft(boolean rightToLeft) {
145+
this.rightToLeft = rightToLeft;
146+
}
147+
148+
/**
149+
* Returns the preferred size of the font, which is typically its rendered size.
150+
*
151+
* @return The preferred size of the font in font units.
152+
*/
124153
public float getPreferredSize() {
125154
return getCharSet().getRenderedSize();
126155
}
127156

157+
/**
158+
* Sets the character set for this font. The character set contains
159+
* information about individual glyphs, their positions, and kerning data.
160+
*
161+
* @param charSet The {@link BitmapCharacterSet} to associate with this font.
162+
*/
128163
public void setCharSet(BitmapCharacterSet charSet) {
129164
this.charSet = charSet;
130165
}
131166

167+
/**
168+
* Sets the array of materials (font pages) for this font. Each material
169+
* corresponds to a texture page containing character bitmaps.
170+
* The character set's page size is also updated based on the number of pages.
171+
*
172+
* @param pages An array of {@link Material} objects representing the font pages.
173+
*/
132174
public void setPages(Material[] pages) {
133175
this.pages = pages;
134176
charSet.setPageSize(pages.length);
135177
}
136178

179+
/**
180+
* Retrieves a specific font page material by its index.
181+
*
182+
* @param index The index of the font page to retrieve.
183+
* @return The {@link Material} for the specified font page.
184+
* @throws IndexOutOfBoundsException if the index is out of bounds.
185+
*/
137186
public Material getPage(int index) {
138187
return pages[index];
139188
}
140189

190+
/**
191+
* Returns the total number of font pages (materials) associated with this font.
192+
*
193+
* @return The number of font pages.
194+
*/
141195
public int getPageSize() {
142196
return pages.length;
143197
}
144198

199+
/**
200+
* Retrieves the character set associated with this font.
201+
*
202+
* @return The {@link BitmapCharacterSet} of this font.
203+
*/
145204
public BitmapCharacterSet getCharSet() {
146205
return charSet;
147206
}
@@ -192,26 +251,19 @@ private int findKerningAmount(int newLineLastChar, int nextChar) {
192251
return c.getKerning(nextChar);
193252
}
194253

195-
@Override
196-
public void write(JmeExporter ex) throws IOException {
197-
OutputCapsule oc = ex.getCapsule(this);
198-
oc.write(charSet, "charSet", null);
199-
oc.write(pages, "pages", null);
200-
oc.write(rightToLeft, "rightToLeft", false);
201-
oc.write(glyphParser, "glyphParser", null);
202-
}
203-
204-
@Override
205-
public void read(JmeImporter im) throws IOException {
206-
InputCapsule ic = im.getCapsule(this);
207-
charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
208-
Savable[] pagesSavable = ic.readSavableArray("pages", null);
209-
pages = new Material[pagesSavable.length];
210-
System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
211-
rightToLeft = ic.readBoolean("rightToLeft", false);
212-
glyphParser = (GlyphParser) ic.readSavable("glyphParser", null);
213-
}
214-
254+
/**
255+
* Calculates the width of the given text in font units.
256+
* This method accounts for character advances, kerning, and line breaks.
257+
* It also attempts to skip custom color tags (e.g., "\#RRGGBB#" or "\#RRGGBBAA#")
258+
* based on a specific format.
259+
* <p>
260+
* Note: This method calculates width in "font units" where the font's
261+
* {@link BitmapCharacterSet#getRenderedSize() rendered size} is the base.
262+
* Actual pixel scaling for display is typically handled by {@link BitmapText}.
263+
*
264+
* @param text The text to measure.
265+
* @return The maximum line width of the text in font units.
266+
*/
215267
public float getLineWidth(CharSequence text) {
216268
// This method will probably always be a bit of a maintenance
217269
// nightmare since it bases its calculation on a different
@@ -252,29 +304,36 @@ public float getLineWidth(CharSequence text) {
252304
boolean firstCharOfLine = true;
253305
// float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
254306
float sizeScale = 1f;
255-
CharSequence characters = glyphParser != null ? glyphParser.parse(text) : text;
256307

257-
for (int i = 0; i < characters.length(); i++) {
258-
char theChar = characters.charAt(i);
259-
if (theChar == '\n') {
308+
// Use GlyphParser if available for complex script shaping (e.g., cursive fonts).
309+
CharSequence processedText = glyphParser != null ? glyphParser.parse(text) : text;
310+
311+
for (int i = 0; i < processedText.length(); i++) {
312+
char currChar = processedText.charAt(i);
313+
if (currChar == '\n') {
260314
maxLineWidth = Math.max(maxLineWidth, lineWidth);
261315
lineWidth = 0f;
262316
firstCharOfLine = true;
263317
continue;
264318
}
265-
BitmapCharacter c = charSet.getCharacter(theChar);
319+
BitmapCharacter c = charSet.getCharacter(currChar);
266320
if (c != null) {
267-
if (theChar == '\\' && i < characters.length() - 1 && characters.charAt(i + 1) == '#') {
268-
if (i + 5 < characters.length() && characters.charAt(i + 5) == '#') {
321+
// Custom color tag skipping logic:
322+
// Assumes tags are of the form `\#RRGGBB#` (9 chars total) or `\#RRGGBBAA#` (12 chars total).
323+
if (currChar == '\\' && i < processedText.length() - 1 && processedText.charAt(i + 1) == '#') {
324+
// Check for `\#XXXXX#` (6 chars after '\', including final '#')
325+
if (i + 5 < processedText.length() && processedText.charAt(i + 5) == '#') {
269326
i += 5;
270327
continue;
271-
} else if (i + 8 < characters.length() && characters.charAt(i + 8) == '#') {
328+
}
329+
// Check for `\#XXXXXXXX#` (9 chars after '\', including final '#')
330+
else if (i + 8 < processedText.length() && processedText.charAt(i + 8) == '#') {
272331
i += 8;
273332
continue;
274333
}
275334
}
276335
if (!firstCharOfLine) {
277-
lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
336+
lineWidth += findKerningAmount(lastChar, currChar) * sizeScale;
278337
} else {
279338
if (rightToLeft) {
280339
// Ignore offset, so it will be compatible with BitmapText.getLineWidth().
@@ -292,7 +351,7 @@ public float getLineWidth(CharSequence text) {
292351
// If this is the last character of a line, then we really should
293352
// have only added its width. The advance may include extra spacing
294353
// that we don't care about.
295-
if (i == characters.length() - 1 || characters.charAt(i + 1) == '\n') {
354+
if (i == processedText.length() - 1 || processedText.charAt(i + 1) == '\n') {
296355
if (rightToLeft) {
297356
// In RTL text we move the letter x0 by its xAdvance, so
298357
// we should add it to lineWidth.
@@ -315,30 +374,54 @@ public float getLineWidth(CharSequence text) {
315374
return Math.max(maxLineWidth, lineWidth);
316375
}
317376

318-
319377
/**
320-
* Merge two fonts.
321-
* If two font have the same style, merge will fail.
322-
* @param newFont Style must be assigned to this.
323-
* author: Yonghoon
378+
* Merges another {@link BitmapFont} into this one.
379+
* This operation combines the character sets and font pages.
380+
* If both fonts contain the same style, the merge will fail and throw a RuntimeException.
381+
*
382+
* @param newFont The {@link BitmapFont} to merge into this one. It must have a style assigned.
324383
*/
325384
public void merge(BitmapFont newFont) {
326385
charSet.merge(newFont.charSet);
327386
final int size1 = this.pages.length;
328387
final int size2 = newFont.pages.length;
329388

330-
Material[] tmp = new Material[size1+size2];
389+
Material[] tmp = new Material[size1 + size2];
331390
System.arraycopy(this.pages, 0, tmp, 0, size1);
332391
System.arraycopy(newFont.pages, 0, tmp, size1, size2);
333392

334393
this.pages = tmp;
335-
336-
// this.pages = Arrays.copyOf(this.pages, size1+size2);
337-
// System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
338394
}
339395

396+
/**
397+
* Sets the style for the font's character set.
398+
* This method is typically used when a font file contains only one style
399+
* but needs to be assigned a specific style identifier for merging
400+
* with other multi-style fonts.
401+
*
402+
* @param style The integer style identifier to set.
403+
*/
340404
public void setStyle(int style) {
341405
charSet.setStyle(style);
342406
}
343407

344-
}
408+
@Override
409+
public void write(JmeExporter ex) throws IOException {
410+
OutputCapsule oc = ex.getCapsule(this);
411+
oc.write(charSet, "charSet", null);
412+
oc.write(pages, "pages", null);
413+
oc.write(rightToLeft, "rightToLeft", false);
414+
oc.write(glyphParser, "glyphParser", null);
415+
}
416+
417+
@Override
418+
public void read(JmeImporter im) throws IOException {
419+
InputCapsule ic = im.getCapsule(this);
420+
charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
421+
Savable[] pagesSavable = ic.readSavableArray("pages", null);
422+
pages = new Material[pagesSavable.length];
423+
System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
424+
rightToLeft = ic.readBoolean("rightToLeft", false);
425+
glyphParser = (GlyphParser) ic.readSavable("glyphParser", null);
426+
}
427+
}

0 commit comments

Comments
 (0)