Wyatt Gillette 2 місяців тому
батько
коміт
f7f3dc6fe1
1 змінених файлів з 140 додано та 57 видалено
  1. 140 57
      jme3-core/src/main/java/com/jme3/font/BitmapFont.java

+ 140 - 57
jme3-core/src/main/java/com/jme3/font/BitmapFont.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2021 jMonkeyEngine
+ * Copyright (c) 2009-2025 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,14 +31,22 @@
  */
 package com.jme3.font;
 
-import com.jme3.export.*;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
 import com.jme3.material.Material;
 
 import java.io.IOException;
 
 /**
- * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
+ * Represents a font loaded from a bitmap font definition
+ * (e.g., generated by <a href="https://libgdx.com/wiki/tools/hiero">AngelCode Bitmap Font Generator</a>).
+ * It manages character sets, font pages (textures), and provides utilities for text measurement and rendering.
+ *
  * @author dhdd
+ * @author Yonghoon
  */
 public class BitmapFont implements Savable {
 
@@ -87,33 +95,30 @@ public class BitmapFont implements Savable {
         Bottom
     }
 
+    // The character set containing definitions for each character (glyph) in the font.
     private BitmapCharacterSet charSet;
+    // An array of materials, where each material corresponds to a font page (texture).
     private Material[] pages;
+    // Indicates whether this font is designed for right-to-left (RTL) text rendering.
     private boolean rightToLeft = false;
     // For cursive bitmap fonts in which letter shape is determined by the adjacent glyphs.
     private GlyphParser glyphParser;
 
     /**
-     * @return true, if this is a right-to-left font, otherwise it will return false.
+     * Creates a new instance of `BitmapFont`.
+     * This constructor is primarily used for deserialization.
      */
-    public boolean isRightToLeft() {
-        return rightToLeft;
+    public BitmapFont() {
     }
 
     /**
-     * Specify if this is a right-to-left font. By default it is set to false.
-     * This can be "overwritten" in the BitmapText constructor.
+     * Creates a new {@link BitmapText} instance initialized with this font.
+     * The label's size will be set to the font's rendered size, and its text content
+     * will be set to the provided string.
      *
-     * @param rightToLeft true &rarr; right-to-left, false &rarr; left-to-right
-     *     (default=false)
+     * @param content The initial text content for the label.
+     * @return A new {@link BitmapText} instance.
      */
-    public void setRightToLeft(boolean rightToLeft) {
-        this.rightToLeft = rightToLeft;
-    }
-
-    public BitmapFont() {
-    }
-
     public BitmapText createLabel(String content) {
         BitmapText label = new BitmapText(this);
         label.setSize(getCharSet().getRenderedSize());
@@ -121,27 +126,81 @@ public class BitmapFont implements Savable {
         return label;
     }
 
+    /**
+     * Checks if this font is configured for right-to-left (RTL) text rendering.
+     *
+     * @return true if this is a right-to-left font, otherwise false (default is left-to-right).
+     */
+    public boolean isRightToLeft() {
+        return rightToLeft;
+    }
+
+    /**
+     * Specifies whether this font should be rendered as right-to-left (RTL).
+     * By default, it is set to false (left-to-right).
+     *
+     * @param rightToLeft true to enable right-to-left rendering; false for left-to-right.
+     */
+    public void setRightToLeft(boolean rightToLeft) {
+        this.rightToLeft = rightToLeft;
+    }
+
+    /**
+     * Returns the preferred size of the font, which is typically its rendered size.
+     *
+     * @return The preferred size of the font in font units.
+     */
     public float getPreferredSize() {
         return getCharSet().getRenderedSize();
     }
 
+    /**
+     * Sets the character set for this font. The character set contains
+     * information about individual glyphs, their positions, and kerning data.
+     *
+     * @param charSet The {@link BitmapCharacterSet} to associate with this font.
+     */
     public void setCharSet(BitmapCharacterSet charSet) {
         this.charSet = charSet;
     }
 
+    /**
+     * Sets the array of materials (font pages) for this font. Each material
+     * corresponds to a texture page containing character bitmaps.
+     * The character set's page size is also updated based on the number of pages.
+     *
+     * @param pages An array of {@link Material} objects representing the font pages.
+     */
     public void setPages(Material[] pages) {
         this.pages = pages;
         charSet.setPageSize(pages.length);
     }
 
+    /**
+     * Retrieves a specific font page material by its index.
+     *
+     * @param index The index of the font page to retrieve.
+     * @return The {@link Material} for the specified font page.
+     * @throws IndexOutOfBoundsException if the index is out of bounds.
+     */
     public Material getPage(int index) {
         return pages[index];
     }
 
+    /**
+     * Returns the total number of font pages (materials) associated with this font.
+     *
+     * @return The number of font pages.
+     */
     public int getPageSize() {
         return pages.length;
     }
 
+    /**
+     * Retrieves the character set associated with this font.
+     *
+     * @return The {@link BitmapCharacterSet} of this font.
+     */
     public BitmapCharacterSet getCharSet() {
         return charSet;
     }
@@ -192,26 +251,19 @@ public class BitmapFont implements Savable {
         return c.getKerning(nextChar);
     }
 
-    @Override
-    public void write(JmeExporter ex) throws IOException {
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.write(charSet, "charSet", null);
-        oc.write(pages, "pages", null);
-        oc.write(rightToLeft, "rightToLeft", false);
-        oc.write(glyphParser, "glyphParser", null);
-    }
-
-    @Override
-    public void read(JmeImporter im) throws IOException {
-        InputCapsule ic = im.getCapsule(this);
-        charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
-        Savable[] pagesSavable = ic.readSavableArray("pages", null);
-        pages = new Material[pagesSavable.length];
-        System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
-        rightToLeft = ic.readBoolean("rightToLeft", false);
-        glyphParser = (GlyphParser) ic.readSavable("glyphParser", null);
-    }
-
+    /**
+     * Calculates the width of the given text in font units.
+     * This method accounts for character advances, kerning, and line breaks.
+     * It also attempts to skip custom color tags (e.g., "\#RRGGBB#" or "\#RRGGBBAA#")
+     * based on a specific format.
+     * <p>
+     * Note: This method calculates width in "font units" where the font's
+     * {@link BitmapCharacterSet#getRenderedSize() rendered size} is the base.
+     * Actual pixel scaling for display is typically handled by {@link BitmapText}.
+     *
+     * @param text The text to measure.
+     * @return The maximum line width of the text in font units.
+     */
     public float getLineWidth(CharSequence text) {
         // This method will probably always be a bit of a maintenance
         // nightmare since it bases its calculation on a different
@@ -252,29 +304,36 @@ public class BitmapFont implements Savable {
         boolean firstCharOfLine = true;
 //        float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
         float sizeScale = 1f;
-        CharSequence characters = glyphParser != null ? glyphParser.parse(text) : text;
 
-        for (int i = 0; i < characters.length(); i++) {
-            char theChar = characters.charAt(i);
-            if (theChar == '\n') {
+        // Use GlyphParser if available for complex script shaping (e.g., cursive fonts).
+        CharSequence processedText = glyphParser != null ? glyphParser.parse(text) : text;
+
+        for (int i = 0; i < processedText.length(); i++) {
+            char currChar = processedText.charAt(i);
+            if (currChar == '\n') {
                 maxLineWidth = Math.max(maxLineWidth, lineWidth);
                 lineWidth = 0f;
                 firstCharOfLine = true;
                 continue;
             }
-            BitmapCharacter c = charSet.getCharacter(theChar);
+            BitmapCharacter c = charSet.getCharacter(currChar);
             if (c != null) {
-                if (theChar == '\\' && i < characters.length() - 1 && characters.charAt(i + 1) == '#') {
-                    if (i + 5 < characters.length() && characters.charAt(i + 5) == '#') {
+                // Custom color tag skipping logic:
+                // Assumes tags are of the form `\#RRGGBB#` (9 chars total) or `\#RRGGBBAA#` (12 chars total).
+                if (currChar == '\\' && i < processedText.length() - 1 && processedText.charAt(i + 1) == '#') {
+                    // Check for `\#XXXXX#` (6 chars after '\', including final '#')
+                    if (i + 5 < processedText.length() && processedText.charAt(i + 5) == '#') {
                         i += 5;
                         continue;
-                    } else if (i + 8 < characters.length() && characters.charAt(i + 8) == '#') {
+                    }
+                    // Check for `\#XXXXXXXX#` (9 chars after '\', including final '#')
+                    else if (i + 8 < processedText.length() && processedText.charAt(i + 8) == '#') {
                         i += 8;
                         continue;
                     }
                 }
                 if (!firstCharOfLine) {
-                    lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
+                    lineWidth += findKerningAmount(lastChar, currChar) * sizeScale;
                 } else {
                     if (rightToLeft) {
                         // Ignore offset, so it will be compatible with BitmapText.getLineWidth().
@@ -292,7 +351,7 @@ public class BitmapFont implements Savable {
                 // If this is the last character of a line, then we really should
                 // have only added its width. The advance may include extra spacing
                 // that we don't care about.
-                if (i == characters.length() - 1 || characters.charAt(i + 1) == '\n') {
+                if (i == processedText.length() - 1 || processedText.charAt(i + 1) == '\n') {
                     if (rightToLeft) {
                         // In RTL text we move the letter x0 by its xAdvance, so
                         // we should add it to lineWidth.
@@ -315,30 +374,54 @@ public class BitmapFont implements Savable {
         return Math.max(maxLineWidth, lineWidth);
     }
 
-
     /**
-     * Merge two fonts.
-     * If two font have the same style, merge will fail.
-     * @param newFont Style must be assigned to this.
-     * author: Yonghoon
+     * Merges another {@link BitmapFont} into this one.
+     * This operation combines the character sets and font pages.
+     * If both fonts contain the same style, the merge will fail and throw a RuntimeException.
+     *
+     * @param newFont The {@link BitmapFont} to merge into this one. It must have a style assigned.
      */
     public void merge(BitmapFont newFont) {
         charSet.merge(newFont.charSet);
         final int size1 = this.pages.length;
         final int size2 = newFont.pages.length;
 
-        Material[] tmp = new Material[size1+size2];
+        Material[] tmp = new Material[size1 + size2];
         System.arraycopy(this.pages, 0, tmp, 0, size1);
         System.arraycopy(newFont.pages, 0, tmp, size1, size2);
 
         this.pages = tmp;
-
-//        this.pages = Arrays.copyOf(this.pages, size1+size2);
-//        System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
     }
 
+    /**
+     * Sets the style for the font's character set.
+     * This method is typically used when a font file contains only one style
+     * but needs to be assigned a specific style identifier for merging
+     * with other multi-style fonts.
+     *
+     * @param style The integer style identifier to set.
+     */
     public void setStyle(int style) {
         charSet.setStyle(style);
     }
 
-}
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(charSet, "charSet", null);
+        oc.write(pages, "pages", null);
+        oc.write(rightToLeft, "rightToLeft", false);
+        oc.write(glyphParser, "glyphParser", null);
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = im.getCapsule(this);
+        charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
+        Savable[] pagesSavable = ic.readSavableArray("pages", null);
+        pages = new Material[pagesSavable.length];
+        System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
+        rightToLeft = ic.readBoolean("rightToLeft", false);
+        glyphParser = (GlyphParser) ic.readSavable("glyphParser", null);
+    }
+}