Source: scene/text-bitmap/BitmapText.ts

import { AbstractText, ensureOptions } from '../text/AbstractText';
import { TextStyle } from '../text/TextStyle';
import { BitmapFontManager } from './BitmapFontManager';

import type { View } from '../../rendering/renderers/shared/view/View';
import type { TextOptions, TextString } from '../text/AbstractText';
import type { TextStyleOptions } from '../text/TextStyle';

/**
 * A BitmapText Object will create a line or multiple lines of text.
 *
 * To split a line you can use '\n' in your text string, or, on the `style` object,
 * change its `wordWrap` property to true and and give the `wordWrapWidth` property a value.
 *
 * The text is created using a bitmap font (a sprite sheet of characters).
 *
 * The primary advantage of this render mode over `text` is that all of your textures are pre-generated and loaded,
 * meaning that rendering is fast, and changing text is much faster than Text.
 *
 * The primary disadvantage is that supporting character sets other than latin, such as CJK languages,
 * may be impractical due to the number of characters.
 *
 * <b>Pre-loaded BitmapFonts:</b>
 *
 *
 * PixiJS enables the loading of BitmapFonts through its Asset Manager, supporting both XML and FNT formats.
 * Additionally, PixiJS is compatible with MSDF (Multi-channel Signed Distance Field) and SDF (Signed Distance Field) fonts.
 * These advanced font types allow for scaling without quality degradation and must be created with specific tools,
 * such as the one available at https://msdf-bmfont.donmccurdy.com/.
 *
 * <b>Dynamically Generated BitmapFonts:</b>
 *
 *
 * PixiJS also offers the capability to generate BitmapFonts dynamically. This means that fonts are created in real-time
 * based on specified styles, eliminating the need for pre-loading. This process is initiated simply by assigning a style
 * to a BitmapText object, which then automatically generates the required font.
 *
 * However, dynamically generating a large number of fonts may lead to significant memory use. To prevent this,
 * PixiJS smartly attempts to reuse fonts that closely match the desired style parameters. For instance, if a text style
 * requires a font size of 80 but a similar font of size 100 has already been generated, PixiJS will scale the existing
 * font to fit the new requirement, rather than creating a new font from scratch.
 *
 * For those who prefer to manage BitmapFonts manually, PixiJS provides the BitmapFont.install method. This method
 * allows for the pre-generation and preparation of fonts, making them readily available for use by specifying the
 * fontFamily in your text styling.
 *
 * This approach ensures efficient font management within PixiJS, balancing between dynamic generation for flexibility
 * and manual management for optimized performance.
 * @example
 * import { BitmapText, BitmapFont } from 'pixi.js';
 *
 * // generate a dynamic font behind the scenes:
 * const text = new BitmapText({
 *     text: 'Hello Pixi!',
 *     style: {
 *         fontFamily: 'Arial',
 *         fontSize: 24,
 *         fill: 0xff1010,
 *         align: 'center',
 *     }
 * });
 *
 * // pre install
 * BitmapFont.install({
 *    name: 'myFont',
 *    style:{
 *        fontFamily: 'Arial',
 *    }
 * })
 *
 * // new bitmap text with preinstalled font
 * const text = new BitmapText({
 *     text: 'Hello Pixi!',
 *     style: {
 *        fontFamily: 'myFont',
 *        fontSize: 24,
 *        fill: 0xff1010,
 *        align: 'center',
 *     }
 * }
 *
 * // load a font from an xml file
 * const font = await Assets.load('path/to/myLoadedFont.fnt');
 *
 * // new bitmap text with loaded font
 * const text = new BitmapText({
 *     text: 'Hello Pixi!',
 *     style: {
 *        fontFamily: 'myLoadedFont', // the name of the font in the fnt file
 *        fontSize: 24,
 *        fill: 0xff1010,
 *        align: 'center',
 *     }
 * }
 * @memberof scene
 */
export class BitmapText extends AbstractText<TextStyle, TextStyleOptions> implements View
{
    public readonly renderPipeId: string = 'bitmapText';

    /**
     * **Note:** Our docs parser struggles to properly understand the constructor signature.
     * This is the correct signature.
     * ```ts
     * new BitmapText(options?: TextOptions);
     * ```
     * @param { text.TextOptions } options - The options of the bitmap text.
     */
    constructor(options?: TextOptions);
    /** @deprecated since 8.0.0 */
    constructor(text?: TextString, options?: Partial<TextStyle>);
    constructor(...args: [TextOptions?] | [TextString, Partial<TextStyle>])
    {
        const options = ensureOptions(args, 'BitmapText');

        options.style ??= options.style || {};
        options.style.fill ??= 0xffffff;

        super(options, TextStyle);
    }

    protected _updateBounds()
    {
        const bounds = this._bounds;
        const padding = this._style.padding;
        const anchor = this._anchor;

        const bitmapMeasurement = BitmapFontManager.measureText(this.text, this._style);
        const scale = bitmapMeasurement.scale;
        const offset = bitmapMeasurement.offsetY * scale;

        let width = bitmapMeasurement.width * scale;
        let height = bitmapMeasurement.height * scale;

        const stroke = this._style._stroke;

        if (stroke)
        {
            width += stroke.width;
            height += stroke.width;
        }

        bounds.minX = (-anchor._x * width) - padding;
        bounds.maxX = bounds.minX + width;
        bounds.minY = (-anchor._y * (height + offset)) - padding;
        bounds.maxY = bounds.minY + height;
    }
}