This commit is contained in:
Akko
2025-08-04 18:57:35 +02:00
parent 8cf6e78a79
commit 9495868c2e
5030 changed files with 518594 additions and 17609 deletions

View File

@@ -0,0 +1,132 @@
import EventEmitter from 'eventemitter3';
import type { Texture } from '../../rendering/renderers/shared/texture/Texture';
import type { FontMetrics } from '../text/canvas/CanvasTextMetrics';
/** @memberof text */
export interface CharData {
/** Unique id of character */
id: number;
/** x-offset to apply when rendering character */
xOffset: number;
/** y-offset to apply when rendering character. */
yOffset: number;
/** Advancement to apply to next character. */
xAdvance: number;
/** The kerning values for this character. */
kerning: Record<string, number>;
/** The texture of the character. */
texture?: Texture;
}
/**
* The raw data of a character in a bitmap font.
* @memberof text
*/
export interface RawCharData extends Omit<CharData, 'texture'> {
/** The page of the font texture that the character is on. */
page: number;
/** The x position of the character in the page. */
x: number;
/** The y position of the character in the page. */
y: number;
/** The width of the character in the page. */
width: number;
/** The height of the character in the page. */
height: number;
/** The letter of the character. */
letter: string;
}
/**
* The raw data of a bitmap font.
* @memberof text
*/
export interface BitmapFontData {
/** The offset of the font face from the baseline. */
baseLineOffset: number;
/** The map of characters by character code. */
chars: Record<string, RawCharData>;
/** The map of base page textures (i.e., sheets of glyphs). */
pages: {
/** Unique id for bitmap texture */
id: number;
/** File name */
file: string;
}[];
/** The line-height of the font face in pixels. */
lineHeight: number;
/** The size of the font face in pixels. */
fontSize: number;
/** The name of the font face. */
fontFamily: string;
/** The range and type of the distance field for this font. */
distanceField?: {
/** Type of distance field */
type: 'sdf' | 'msdf' | 'none';
/** Range of the distance field in pixels */
range: number;
};
}
interface BitmapFontEvents<Type> {
destroy: [Type];
}
/**
* An abstract representation of a bitmap font.
* @memberof text
*/
export declare abstract class AbstractBitmapFont<FontType> extends EventEmitter<BitmapFontEvents<FontType>> implements Omit<BitmapFontData, 'chars' | 'pages' | 'fontSize'> {
/** The map of characters by character code. */
readonly chars: Record<string, CharData>;
/**
* The line-height of the font face in pixels.
* @type {number}
*/
readonly lineHeight: BitmapFontData['lineHeight'];
/**
* The name of the font face
* @type {string}
*/
readonly fontFamily: BitmapFontData['fontFamily'];
/** The metrics of the font face. */
readonly fontMetrics: FontMetrics;
/**
* The offset of the font face from the baseline.
* @type {number}
*/
readonly baseLineOffset: BitmapFontData['baseLineOffset'];
/** The range and type of the distance field for this font. */
readonly distanceField: BitmapFontData['distanceField'];
/** The map of base page textures (i.e., sheets of glyphs). */
readonly pages: {
texture: Texture;
}[];
/** should the fill for this font be applied as a tint to the text. */
applyFillAsTint: boolean;
/** The size of the font face in pixels. */
readonly baseMeasurementFontSize: number;
protected baseRenderedFontSize: number;
/**
* The name of the font face.
* @deprecated since 8.0.0 Use `fontFamily` instead.
*/
get font(): BitmapFontData['fontFamily'];
/**
* The map of base page textures (i.e., sheets of glyphs).
* @deprecated since 8.0.0 Use `pages` instead.
*/
get pageTextures(): AbstractBitmapFont<FontType>['pages'];
/**
* The size of the font face in pixels.
* @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead.
*/
get size(): BitmapFontData['fontSize'];
/**
* The kind of distance field for this font or "none".
* @deprecated since 8.0.0 Use `distanceField.type` instead.
*/
get distanceFieldRange(): NonNullable<BitmapFontData['distanceField']>['range'];
/**
* The range of the distance field in pixels.
* @deprecated since 8.0.0 Use `distanceField.range` instead.
*/
get distanceFieldType(): NonNullable<BitmapFontData['distanceField']>['type'];
destroy(destroyTextures?: boolean): void;
}
export {};

View File

@@ -0,0 +1,94 @@
'use strict';
var EventEmitter = require('eventemitter3');
var deprecation = require('../../utils/logging/deprecation.js');
"use strict";
class AbstractBitmapFont extends EventEmitter {
constructor() {
super(...arguments);
/** The map of characters by character code. */
this.chars = /* @__PURE__ */ Object.create(null);
/**
* The line-height of the font face in pixels.
* @type {number}
*/
this.lineHeight = 0;
/**
* The name of the font face
* @type {string}
*/
this.fontFamily = "";
/** The metrics of the font face. */
this.fontMetrics = { fontSize: 0, ascent: 0, descent: 0 };
/**
* The offset of the font face from the baseline.
* @type {number}
*/
this.baseLineOffset = 0;
/** The range and type of the distance field for this font. */
this.distanceField = { type: "none", range: 0 };
/** The map of base page textures (i.e., sheets of glyphs). */
this.pages = [];
/** should the fill for this font be applied as a tint to the text. */
this.applyFillAsTint = true;
/** The size of the font face in pixels. */
this.baseMeasurementFontSize = 100;
this.baseRenderedFontSize = 100;
}
/**
* The name of the font face.
* @deprecated since 8.0.0 Use `fontFamily` instead.
*/
get font() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead.");
return this.fontFamily;
}
/**
* The map of base page textures (i.e., sheets of glyphs).
* @deprecated since 8.0.0 Use `pages` instead.
*/
get pageTextures() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
return this.pages;
}
/**
* The size of the font face in pixels.
* @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead.
*/
get size() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead.");
return this.fontMetrics.fontSize;
}
/**
* The kind of distance field for this font or "none".
* @deprecated since 8.0.0 Use `distanceField.type` instead.
*/
get distanceFieldRange() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead.");
return this.distanceField.range;
}
/**
* The range of the distance field in pixels.
* @deprecated since 8.0.0 Use `distanceField.range` instead.
*/
get distanceFieldType() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead.");
return this.distanceField.type;
}
destroy(destroyTextures = false) {
this.emit("destroy", this);
this.removeAllListeners();
for (const i in this.chars) {
this.chars[i].texture?.destroy();
}
this.chars = null;
if (destroyTextures) {
this.pages.forEach((page) => page.texture.destroy(true));
this.pages = null;
}
}
}
exports.AbstractBitmapFont = AbstractBitmapFont;
//# sourceMappingURL=AbstractBitmapFont.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,92 @@
import EventEmitter from 'eventemitter3';
import { deprecation, v8_0_0 } from '../../utils/logging/deprecation.mjs';
"use strict";
class AbstractBitmapFont extends EventEmitter {
constructor() {
super(...arguments);
/** The map of characters by character code. */
this.chars = /* @__PURE__ */ Object.create(null);
/**
* The line-height of the font face in pixels.
* @type {number}
*/
this.lineHeight = 0;
/**
* The name of the font face
* @type {string}
*/
this.fontFamily = "";
/** The metrics of the font face. */
this.fontMetrics = { fontSize: 0, ascent: 0, descent: 0 };
/**
* The offset of the font face from the baseline.
* @type {number}
*/
this.baseLineOffset = 0;
/** The range and type of the distance field for this font. */
this.distanceField = { type: "none", range: 0 };
/** The map of base page textures (i.e., sheets of glyphs). */
this.pages = [];
/** should the fill for this font be applied as a tint to the text. */
this.applyFillAsTint = true;
/** The size of the font face in pixels. */
this.baseMeasurementFontSize = 100;
this.baseRenderedFontSize = 100;
}
/**
* The name of the font face.
* @deprecated since 8.0.0 Use `fontFamily` instead.
*/
get font() {
deprecation(v8_0_0, "BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead.");
return this.fontFamily;
}
/**
* The map of base page textures (i.e., sheets of glyphs).
* @deprecated since 8.0.0 Use `pages` instead.
*/
get pageTextures() {
deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
return this.pages;
}
/**
* The size of the font face in pixels.
* @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead.
*/
get size() {
deprecation(v8_0_0, "BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead.");
return this.fontMetrics.fontSize;
}
/**
* The kind of distance field for this font or "none".
* @deprecated since 8.0.0 Use `distanceField.type` instead.
*/
get distanceFieldRange() {
deprecation(v8_0_0, "BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead.");
return this.distanceField.range;
}
/**
* The range of the distance field in pixels.
* @deprecated since 8.0.0 Use `distanceField.range` instead.
*/
get distanceFieldType() {
deprecation(v8_0_0, "BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead.");
return this.distanceField.type;
}
destroy(destroyTextures = false) {
this.emit("destroy", this);
this.removeAllListeners();
for (const i in this.chars) {
this.chars[i].texture?.destroy();
}
this.chars = null;
if (destroyTextures) {
this.pages.forEach((page) => page.texture.destroy(true));
this.pages = null;
}
}
}
export { AbstractBitmapFont };
//# sourceMappingURL=AbstractBitmapFont.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
import { Texture } from '../../rendering/renderers/shared/texture/Texture';
import { AbstractBitmapFont } from './AbstractBitmapFont';
import type { BitmapFontData } from './AbstractBitmapFont';
import type { BitmapFontInstallOptions } from './BitmapFontManager';
/**
* Options for creating a BitmapFont.
* @memberof text
*/
export interface BitmapFontOptions {
data: BitmapFontData;
textures: Texture[];
}
/**
* A BitmapFont object represents a particular font face, size, and style.
* @memberof text
*/
export declare class BitmapFont extends AbstractBitmapFont<BitmapFont> {
/** the url of the font */
url?: string;
constructor(options: BitmapFontOptions, url?: string);
/** Destroys the BitmapFont object. */
destroy(): void;
/**
* Generates a bitmap-font for the given style and character set
* @param options - Setup options for font generation.
* @returns Font generated by style options.
* @example
* import { BitmapFont, BitmapText } from 'pixi.js';
*
* BitmapFont.install('TitleFont', {
* fontFamily: 'Arial',
* fontSize: 12,
* strokeThickness: 2,
* fill: 'purple',
* });
*
* const title = new BitmapText({ text: 'This is the title', fontFamily: 'TitleFont' });
*/
static install(options: BitmapFontInstallOptions): void;
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
static uninstall(name: string): void;
}

View File

@@ -0,0 +1,97 @@
'use strict';
var Rectangle = require('../../maths/shapes/Rectangle.js');
var Texture = require('../../rendering/renderers/shared/texture/Texture.js');
var AbstractBitmapFont = require('./AbstractBitmapFont.js');
var BitmapFontManager = require('./BitmapFontManager.js');
"use strict";
class BitmapFont extends AbstractBitmapFont.AbstractBitmapFont {
constructor(options, url) {
super();
const { textures, data } = options;
Object.keys(data.pages).forEach((key) => {
const pageData = data.pages[parseInt(key, 10)];
const texture = textures[pageData.id];
this.pages.push({ texture });
});
Object.keys(data.chars).forEach((key) => {
const charData = data.chars[key];
const {
frame: textureFrame,
source: textureSource
} = textures[charData.page];
const frameReal = new Rectangle.Rectangle(
charData.x + textureFrame.x,
charData.y + textureFrame.y,
charData.width,
charData.height
);
const texture = new Texture.Texture({
source: textureSource,
frame: frameReal
});
this.chars[key] = {
id: key.codePointAt(0),
xOffset: charData.xOffset,
yOffset: charData.yOffset,
xAdvance: charData.xAdvance,
kerning: charData.kerning ?? {},
texture
};
});
this.baseRenderedFontSize = data.fontSize;
this.baseMeasurementFontSize = data.fontSize;
this.fontMetrics = {
ascent: 0,
descent: 0,
fontSize: data.fontSize
};
this.baseLineOffset = data.baseLineOffset;
this.lineHeight = data.lineHeight;
this.fontFamily = data.fontFamily;
this.distanceField = data.distanceField ?? {
type: "none",
range: 0
};
this.url = url;
}
/** Destroys the BitmapFont object. */
destroy() {
super.destroy();
for (let i = 0; i < this.pages.length; i++) {
const { texture } = this.pages[i];
texture.destroy(true);
}
this.pages = null;
}
/**
* Generates a bitmap-font for the given style and character set
* @param options - Setup options for font generation.
* @returns Font generated by style options.
* @example
* import { BitmapFont, BitmapText } from 'pixi.js';
*
* BitmapFont.install('TitleFont', {
* fontFamily: 'Arial',
* fontSize: 12,
* strokeThickness: 2,
* fill: 'purple',
* });
*
* const title = new BitmapText({ text: 'This is the title', fontFamily: 'TitleFont' });
*/
static install(options) {
BitmapFontManager.BitmapFontManager.install(options);
}
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
static uninstall(name) {
BitmapFontManager.BitmapFontManager.uninstall(name);
}
}
exports.BitmapFont = BitmapFont;
//# sourceMappingURL=BitmapFont.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,95 @@
import { Rectangle } from '../../maths/shapes/Rectangle.mjs';
import { Texture } from '../../rendering/renderers/shared/texture/Texture.mjs';
import { AbstractBitmapFont } from './AbstractBitmapFont.mjs';
import { BitmapFontManager } from './BitmapFontManager.mjs';
"use strict";
class BitmapFont extends AbstractBitmapFont {
constructor(options, url) {
super();
const { textures, data } = options;
Object.keys(data.pages).forEach((key) => {
const pageData = data.pages[parseInt(key, 10)];
const texture = textures[pageData.id];
this.pages.push({ texture });
});
Object.keys(data.chars).forEach((key) => {
const charData = data.chars[key];
const {
frame: textureFrame,
source: textureSource
} = textures[charData.page];
const frameReal = new Rectangle(
charData.x + textureFrame.x,
charData.y + textureFrame.y,
charData.width,
charData.height
);
const texture = new Texture({
source: textureSource,
frame: frameReal
});
this.chars[key] = {
id: key.codePointAt(0),
xOffset: charData.xOffset,
yOffset: charData.yOffset,
xAdvance: charData.xAdvance,
kerning: charData.kerning ?? {},
texture
};
});
this.baseRenderedFontSize = data.fontSize;
this.baseMeasurementFontSize = data.fontSize;
this.fontMetrics = {
ascent: 0,
descent: 0,
fontSize: data.fontSize
};
this.baseLineOffset = data.baseLineOffset;
this.lineHeight = data.lineHeight;
this.fontFamily = data.fontFamily;
this.distanceField = data.distanceField ?? {
type: "none",
range: 0
};
this.url = url;
}
/** Destroys the BitmapFont object. */
destroy() {
super.destroy();
for (let i = 0; i < this.pages.length; i++) {
const { texture } = this.pages[i];
texture.destroy(true);
}
this.pages = null;
}
/**
* Generates a bitmap-font for the given style and character set
* @param options - Setup options for font generation.
* @returns Font generated by style options.
* @example
* import { BitmapFont, BitmapText } from 'pixi.js';
*
* BitmapFont.install('TitleFont', {
* fontFamily: 'Arial',
* fontSize: 12,
* strokeThickness: 2,
* fill: 'purple',
* });
*
* const title = new BitmapText({ text: 'This is the title', fontFamily: 'TitleFont' });
*/
static install(options) {
BitmapFontManager.install(options);
}
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
static uninstall(name) {
BitmapFontManager.uninstall(name);
}
}
export { BitmapFont };
//# sourceMappingURL=BitmapFont.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,138 @@
import { TextStyle } from '../text/TextStyle';
import type { TextStyleOptions } from '../text/TextStyle';
import type { BitmapFont } from './BitmapFont';
import type { BitmapTextLayoutData } from './utils/getBitmapTextLayout';
/**
*
* The options for installing a new BitmapFont. Once installed the font will be available for use in the BitmapText.
* It can be accessed by the `fontFamily` property of the TextStyle.
*
* Install a new BitmapFont will create the characters provided for the font and store them in the cache.
* But don't worry, if a character is requested that hasn't been generated yet, it will be created on the fly.
* @memberof text
*/
export interface BitmapFontInstallOptions {
/** the name of the font, this will be the name you use in the fontFamily of text style to access this font */
name?: string;
/**
* Characters included in the font set. You can also use ranges.
* For example, `[['a', 'z'], ['A', 'Z'], "!@#$%^&*()~{}[] "]`.
* Don't forget to include spaces ' ' in your character set!
* @default BitmapFont.ALPHANUMERIC
*/
chars?: string | (string | string[])[];
/**
* Render resolution for glyphs.
* @default 1
*/
resolution?: number;
/**
* Padding between glyphs on texture atlas. Lower values could mean more visual artifacts
* and bleeding from other glyphs, larger values increase the space required on the texture.
* @default 4
*/
padding?: number;
/**
* Skip generation of kerning information for the BitmapFont.
* If true, this could potentially increase the performance, but may impact the rendered text appearance.
* @default false
*/
skipKerning?: boolean;
/** Style options to render with BitmapFont. */
style?: TextStyle | TextStyleOptions;
}
/**
* The BitmapFontManager is a helper that exists to install and uninstall fonts
* into the cache for BitmapText objects.
* @memberof text
* @name BitmapFontManager
* @example
* import { BitmapFontManager, BitmapText } from 'pixi.js';
*
* BitmapFontManager.install({
* name: 'TitleFont',
* style: {}
* });
*
* const title = new BitmapText({ text: 'This is the title', style: { fontFamily: 'TitleFont' }});
*/
declare class BitmapFontManagerClass {
/**
* This character set includes all the letters in the alphabet (both lower- and upper- case).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.ALPHA })
*/
readonly ALPHA: (string | string[])[];
/**
* This character set includes all decimal digits (from 0 to 9).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.NUMERIC })
*/
readonly NUMERIC: string[][];
/**
* This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`.
* @type {string[][]}
*/
readonly ALPHANUMERIC: (string | string[])[];
/**
* This character set consists of all the ASCII table.
* @member {string[][]}
* @see http://www.asciitable.com/
*/
readonly ASCII: string[][];
/** Default options for installing a new BitmapFont. */
defaultOptions: Omit<BitmapFontInstallOptions, 'style'>;
/**
* Get a font for the specified text and style.
* @param text - The text to get the font for
* @param style - The style to use
*/
getFont(text: string, style: TextStyle): BitmapFont;
/**
* Get the layout of a text for the specified style.
* @param text - The text to get the layout for
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
getLayout(text: string, style: TextStyle, trimEnd?: boolean): BitmapTextLayoutData;
/**
* Measure the text using the specified style.
* @param text - The text to measure
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
measureText(text: string, style: TextStyle, trimEnd?: boolean): {
width: number;
height: number;
scale: number;
offsetY: number;
};
/**
* Generates a bitmap-font for the given style and character set
* @param options - Setup options for font generation.
* @returns Font generated by style options.
* @example
* import { BitmapFontManager, BitmapText } from 'pixi.js';
*
* BitmapFontManager.install('TitleFont', {
* fontFamily: 'Arial',
* fontSize: 12,
* strokeThickness: 2,
* fill: 'purple',
* });
*
* const title = new BitmapText({ text: 'This is the title', fontFamily: 'TitleFont' });
*/
install(options: BitmapFontInstallOptions): BitmapFont;
/** @deprecated since 7.0.0 */
install(name: string, style?: TextStyle | TextStyleOptions, options?: BitmapFontInstallOptions): BitmapFont;
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
uninstall(name: string): void;
}
export declare const BitmapFontManager: BitmapFontManagerClass;
export {};

View File

@@ -0,0 +1,160 @@
'use strict';
var Cache = require('../../assets/cache/Cache.js');
var deprecation = require('../../utils/logging/deprecation.js');
var warn = require('../../utils/logging/warn.js');
var TextStyle = require('../text/TextStyle.js');
var DynamicBitmapFont = require('./DynamicBitmapFont.js');
var getBitmapTextLayout = require('./utils/getBitmapTextLayout.js');
var resolveCharacters = require('./utils/resolveCharacters.js');
"use strict";
let fontCount = 0;
class BitmapFontManagerClass {
constructor() {
/**
* This character set includes all the letters in the alphabet (both lower- and upper- case).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.ALPHA })
*/
this.ALPHA = [["a", "z"], ["A", "Z"], " "];
/**
* This character set includes all decimal digits (from 0 to 9).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.NUMERIC })
*/
this.NUMERIC = [["0", "9"]];
/**
* This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`.
* @type {string[][]}
*/
this.ALPHANUMERIC = [["a", "z"], ["A", "Z"], ["0", "9"], " "];
/**
* This character set consists of all the ASCII table.
* @member {string[][]}
* @see http://www.asciitable.com/
*/
this.ASCII = [[" ", "~"]];
/** Default options for installing a new BitmapFont. */
this.defaultOptions = {
chars: this.ALPHANUMERIC,
resolution: 1,
padding: 4,
skipKerning: false
};
}
/**
* Get a font for the specified text and style.
* @param text - The text to get the font for
* @param style - The style to use
*/
getFont(text, style) {
let fontFamilyKey = `${style.fontFamily}-bitmap`;
let overrideFill = true;
if (style._fill.fill && !style._stroke) {
fontFamilyKey += style._fill.fill.styleKey;
overrideFill = false;
} else if (style._stroke || style.dropShadow) {
let key = style.styleKey;
key = key.substring(0, key.lastIndexOf("-"));
fontFamilyKey = `${key}-bitmap`;
overrideFill = false;
}
if (!Cache.Cache.has(fontFamilyKey)) {
const fnt = new DynamicBitmapFont.DynamicBitmapFont({
style,
overrideFill,
overrideSize: true,
...this.defaultOptions
});
fontCount++;
if (fontCount > 50) {
warn.warn("BitmapText", `You have dynamically created ${fontCount} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``);
}
fnt.once("destroy", () => {
fontCount--;
Cache.Cache.remove(fontFamilyKey);
});
Cache.Cache.set(
fontFamilyKey,
fnt
);
}
const dynamicFont = Cache.Cache.get(fontFamilyKey);
dynamicFont.ensureCharacters?.(text);
return dynamicFont;
}
/**
* Get the layout of a text for the specified style.
* @param text - The text to get the layout for
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
getLayout(text, style, trimEnd = true) {
const bitmapFont = this.getFont(text, style);
return getBitmapTextLayout.getBitmapTextLayout([...text], style, bitmapFont, trimEnd);
}
/**
* Measure the text using the specified style.
* @param text - The text to measure
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
measureText(text, style, trimEnd = true) {
return this.getLayout(text, style, trimEnd);
}
// eslint-disable-next-line max-len
install(...args) {
let options = args[0];
if (typeof options === "string") {
options = {
name: options,
style: args[1],
chars: args[2]?.chars,
resolution: args[2]?.resolution,
padding: args[2]?.padding,
skipKerning: args[2]?.skipKerning
};
deprecation.deprecation(deprecation.v8_0_0, "BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})");
}
const name = options?.name;
if (!name) {
throw new Error("[BitmapFontManager] Property `name` is required.");
}
options = { ...this.defaultOptions, ...options };
const textStyle = options.style;
const style = textStyle instanceof TextStyle.TextStyle ? textStyle : new TextStyle.TextStyle(textStyle);
const overrideFill = style._fill.fill !== null && style._fill.fill !== void 0;
const font = new DynamicBitmapFont.DynamicBitmapFont({
style,
overrideFill,
skipKerning: options.skipKerning,
padding: options.padding,
resolution: options.resolution,
overrideSize: false
});
const flatChars = resolveCharacters.resolveCharacters(options.chars);
font.ensureCharacters(flatChars.join(""));
Cache.Cache.set(`${name}-bitmap`, font);
font.once("destroy", () => Cache.Cache.remove(`${name}-bitmap`));
return font;
}
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
uninstall(name) {
const cacheKey = `${name}-bitmap`;
const font = Cache.Cache.get(cacheKey);
if (font) {
Cache.Cache.remove(cacheKey);
font.destroy();
}
}
}
const BitmapFontManager = new BitmapFontManagerClass();
exports.BitmapFontManager = BitmapFontManager;
//# sourceMappingURL=BitmapFontManager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,158 @@
import { Cache } from '../../assets/cache/Cache.mjs';
import { deprecation, v8_0_0 } from '../../utils/logging/deprecation.mjs';
import { warn } from '../../utils/logging/warn.mjs';
import { TextStyle } from '../text/TextStyle.mjs';
import { DynamicBitmapFont } from './DynamicBitmapFont.mjs';
import { getBitmapTextLayout } from './utils/getBitmapTextLayout.mjs';
import { resolveCharacters } from './utils/resolveCharacters.mjs';
"use strict";
let fontCount = 0;
class BitmapFontManagerClass {
constructor() {
/**
* This character set includes all the letters in the alphabet (both lower- and upper- case).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.ALPHA })
*/
this.ALPHA = [["a", "z"], ["A", "Z"], " "];
/**
* This character set includes all decimal digits (from 0 to 9).
* @type {string[][]}
* @example
* BitmapFont.from('ExampleFont', style, { chars: BitmapFont.NUMERIC })
*/
this.NUMERIC = [["0", "9"]];
/**
* This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`.
* @type {string[][]}
*/
this.ALPHANUMERIC = [["a", "z"], ["A", "Z"], ["0", "9"], " "];
/**
* This character set consists of all the ASCII table.
* @member {string[][]}
* @see http://www.asciitable.com/
*/
this.ASCII = [[" ", "~"]];
/** Default options for installing a new BitmapFont. */
this.defaultOptions = {
chars: this.ALPHANUMERIC,
resolution: 1,
padding: 4,
skipKerning: false
};
}
/**
* Get a font for the specified text and style.
* @param text - The text to get the font for
* @param style - The style to use
*/
getFont(text, style) {
let fontFamilyKey = `${style.fontFamily}-bitmap`;
let overrideFill = true;
if (style._fill.fill && !style._stroke) {
fontFamilyKey += style._fill.fill.styleKey;
overrideFill = false;
} else if (style._stroke || style.dropShadow) {
let key = style.styleKey;
key = key.substring(0, key.lastIndexOf("-"));
fontFamilyKey = `${key}-bitmap`;
overrideFill = false;
}
if (!Cache.has(fontFamilyKey)) {
const fnt = new DynamicBitmapFont({
style,
overrideFill,
overrideSize: true,
...this.defaultOptions
});
fontCount++;
if (fontCount > 50) {
warn("BitmapText", `You have dynamically created ${fontCount} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``);
}
fnt.once("destroy", () => {
fontCount--;
Cache.remove(fontFamilyKey);
});
Cache.set(
fontFamilyKey,
fnt
);
}
const dynamicFont = Cache.get(fontFamilyKey);
dynamicFont.ensureCharacters?.(text);
return dynamicFont;
}
/**
* Get the layout of a text for the specified style.
* @param text - The text to get the layout for
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
getLayout(text, style, trimEnd = true) {
const bitmapFont = this.getFont(text, style);
return getBitmapTextLayout([...text], style, bitmapFont, trimEnd);
}
/**
* Measure the text using the specified style.
* @param text - The text to measure
* @param style - The style to use
* @param trimEnd - Whether to ignore whitespaces at the end of each line
*/
measureText(text, style, trimEnd = true) {
return this.getLayout(text, style, trimEnd);
}
// eslint-disable-next-line max-len
install(...args) {
let options = args[0];
if (typeof options === "string") {
options = {
name: options,
style: args[1],
chars: args[2]?.chars,
resolution: args[2]?.resolution,
padding: args[2]?.padding,
skipKerning: args[2]?.skipKerning
};
deprecation(v8_0_0, "BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})");
}
const name = options?.name;
if (!name) {
throw new Error("[BitmapFontManager] Property `name` is required.");
}
options = { ...this.defaultOptions, ...options };
const textStyle = options.style;
const style = textStyle instanceof TextStyle ? textStyle : new TextStyle(textStyle);
const overrideFill = style._fill.fill !== null && style._fill.fill !== void 0;
const font = new DynamicBitmapFont({
style,
overrideFill,
skipKerning: options.skipKerning,
padding: options.padding,
resolution: options.resolution,
overrideSize: false
});
const flatChars = resolveCharacters(options.chars);
font.ensureCharacters(flatChars.join(""));
Cache.set(`${name}-bitmap`, font);
font.once("destroy", () => Cache.remove(`${name}-bitmap`));
return font;
}
/**
* Uninstalls a bitmap font from the cache.
* @param {string} name - The name of the bitmap font to uninstall.
*/
uninstall(name) {
const cacheKey = `${name}-bitmap`;
const font = Cache.get(cacheKey);
if (font) {
Cache.remove(cacheKey);
font.destroy();
}
}
}
const BitmapFontManager = new BitmapFontManagerClass();
export { BitmapFontManager };
//# sourceMappingURL=BitmapFontManager.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,108 @@
import { AbstractText } from '../text/AbstractText';
import { TextStyle } from '../text/TextStyle';
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 declare class BitmapText extends AbstractText<TextStyle, TextStyleOptions> implements View {
readonly renderPipeId: string;
/**
* **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>);
protected _updateBounds(): void;
}

View File

@@ -0,0 +1,38 @@
'use strict';
var AbstractText = require('../text/AbstractText.js');
var TextStyle = require('../text/TextStyle.js');
var BitmapFontManager = require('./BitmapFontManager.js');
"use strict";
class BitmapText extends AbstractText.AbstractText {
constructor(...args) {
var _a;
const options = AbstractText.ensureOptions(args, "BitmapText");
options.style ?? (options.style = options.style || {});
(_a = options.style).fill ?? (_a.fill = 16777215);
super(options, TextStyle.TextStyle);
this.renderPipeId = "bitmapText";
}
_updateBounds() {
const bounds = this._bounds;
const anchor = this._anchor;
const bitmapMeasurement = BitmapFontManager.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;
bounds.maxX = bounds.minX + width;
bounds.minY = -anchor._y * (height + offset);
bounds.maxY = bounds.minY + height;
}
}
exports.BitmapText = BitmapText;
//# sourceMappingURL=BitmapText.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
import { AbstractText, ensureOptions } from '../text/AbstractText.mjs';
import { TextStyle } from '../text/TextStyle.mjs';
import { BitmapFontManager } from './BitmapFontManager.mjs';
"use strict";
class BitmapText extends AbstractText {
constructor(...args) {
var _a;
const options = ensureOptions(args, "BitmapText");
options.style ?? (options.style = options.style || {});
(_a = options.style).fill ?? (_a.fill = 16777215);
super(options, TextStyle);
this.renderPipeId = "bitmapText";
}
_updateBounds() {
const bounds = this._bounds;
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;
bounds.maxX = bounds.minX + width;
bounds.minY = -anchor._y * (height + offset);
bounds.maxY = bounds.minY + height;
}
}
export { BitmapText };
//# sourceMappingURL=BitmapText.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
import { ExtensionType } from '../../extensions/Extensions';
import { Graphics } from '../graphics/shared/Graphics';
import type { InstructionSet } from '../../rendering/renderers/shared/instructions/InstructionSet';
import type { RenderPipe } from '../../rendering/renderers/shared/instructions/RenderPipe';
import type { Renderer } from '../../rendering/renderers/types';
import type { BitmapText } from './BitmapText';
export declare class BitmapTextPipe implements RenderPipe<BitmapText> {
/** @ignore */
static extension: {
readonly type: readonly [ExtensionType.WebGLPipes, ExtensionType.WebGPUPipes, ExtensionType.CanvasPipes];
readonly name: "bitmapText";
};
private _renderer;
private _gpuBitmapText;
private readonly _destroyRenderableBound;
constructor(renderer: Renderer);
validateRenderable(bitmapText: BitmapText): boolean;
addRenderable(bitmapText: BitmapText, instructionSet: InstructionSet): void;
destroyRenderable(bitmapText: BitmapText): void;
private _destroyRenderableByUid;
updateRenderable(bitmapText: BitmapText): void;
private _updateContext;
private _getGpuBitmapText;
initGpuText(bitmapText: BitmapText): Graphics;
private _updateDistanceField;
destroy(): void;
}

View File

@@ -0,0 +1,152 @@
'use strict';
var Cache = require('../../assets/cache/Cache.js');
var Extensions = require('../../extensions/Extensions.js');
var PoolGroup = require('../../utils/pool/PoolGroup.js');
var Graphics = require('../graphics/shared/Graphics.js');
var SdfShader = require('../text/sdfShader/SdfShader.js');
var BitmapFontManager = require('./BitmapFontManager.js');
var getBitmapTextLayout = require('./utils/getBitmapTextLayout.js');
"use strict";
class BitmapTextPipe {
constructor(renderer) {
this._gpuBitmapText = {};
this._destroyRenderableBound = this.destroyRenderable.bind(this);
this._renderer = renderer;
}
validateRenderable(bitmapText) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
if (bitmapText._didTextUpdate) {
bitmapText._didTextUpdate = false;
this._updateContext(bitmapText, graphicsRenderable);
}
return this._renderer.renderPipes.graphics.validateRenderable(graphicsRenderable);
}
addRenderable(bitmapText, instructionSet) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
syncWithProxy(bitmapText, graphicsRenderable);
if (bitmapText._didTextUpdate) {
bitmapText._didTextUpdate = false;
this._updateContext(bitmapText, graphicsRenderable);
}
this._renderer.renderPipes.graphics.addRenderable(graphicsRenderable, instructionSet);
if (graphicsRenderable.context.customShader) {
this._updateDistanceField(bitmapText);
}
}
destroyRenderable(bitmapText) {
bitmapText.off("destroyed", this._destroyRenderableBound);
this._destroyRenderableByUid(bitmapText.uid);
}
_destroyRenderableByUid(renderableUid) {
const context = this._gpuBitmapText[renderableUid].context;
if (context.customShader) {
PoolGroup.BigPool.return(context.customShader);
context.customShader = null;
}
PoolGroup.BigPool.return(this._gpuBitmapText[renderableUid]);
this._gpuBitmapText[renderableUid] = null;
}
updateRenderable(bitmapText) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
syncWithProxy(bitmapText, graphicsRenderable);
this._renderer.renderPipes.graphics.updateRenderable(graphicsRenderable);
if (graphicsRenderable.context.customShader) {
this._updateDistanceField(bitmapText);
}
}
_updateContext(bitmapText, proxyGraphics) {
const { context } = proxyGraphics;
const bitmapFont = BitmapFontManager.BitmapFontManager.getFont(bitmapText.text, bitmapText._style);
context.clear();
if (bitmapFont.distanceField.type !== "none") {
if (!context.customShader) {
context.customShader = PoolGroup.BigPool.get(SdfShader.SdfShader);
}
}
const chars = Array.from(bitmapText.text);
const style = bitmapText._style;
let currentY = bitmapFont.baseLineOffset;
const bitmapTextLayout = getBitmapTextLayout.getBitmapTextLayout(chars, style, bitmapFont, true);
let index = 0;
const padding = style.padding;
const scale = bitmapTextLayout.scale;
let tx = bitmapTextLayout.width;
let ty = bitmapTextLayout.height + bitmapTextLayout.offsetY;
if (style._stroke) {
tx += style._stroke.width / scale;
ty += style._stroke.width / scale;
}
context.translate(-bitmapText._anchor._x * tx - padding, -bitmapText._anchor._y * ty - padding).scale(scale, scale);
const tint = bitmapFont.applyFillAsTint ? style._fill.color : 16777215;
for (let i = 0; i < bitmapTextLayout.lines.length; i++) {
const line = bitmapTextLayout.lines[i];
for (let j = 0; j < line.charPositions.length; j++) {
const char = chars[index++];
const charData = bitmapFont.chars[char];
if (charData?.texture) {
context.texture(
charData.texture,
tint ? tint : "black",
Math.round(line.charPositions[j] + charData.xOffset),
Math.round(currentY + charData.yOffset)
);
}
}
currentY += bitmapFont.lineHeight;
}
}
_getGpuBitmapText(bitmapText) {
return this._gpuBitmapText[bitmapText.uid] || this.initGpuText(bitmapText);
}
initGpuText(bitmapText) {
const proxyRenderable = PoolGroup.BigPool.get(Graphics.Graphics);
this._gpuBitmapText[bitmapText.uid] = proxyRenderable;
this._updateContext(bitmapText, proxyRenderable);
bitmapText.on("destroyed", this._destroyRenderableBound);
return this._gpuBitmapText[bitmapText.uid];
}
_updateDistanceField(bitmapText) {
const context = this._getGpuBitmapText(bitmapText).context;
const fontFamily = bitmapText._style.fontFamily;
const dynamicFont = Cache.Cache.get(`${fontFamily}-bitmap`);
const { a, b, c, d } = bitmapText.groupTransform;
const dx = Math.sqrt(a * a + b * b);
const dy = Math.sqrt(c * c + d * d);
const worldScale = (Math.abs(dx) + Math.abs(dy)) / 2;
const fontScale = dynamicFont.baseRenderedFontSize / bitmapText._style.fontSize;
const distance = worldScale * dynamicFont.distanceField.range * (1 / fontScale);
context.customShader.resources.localUniforms.uniforms.uDistance = distance;
}
destroy() {
for (const uid in this._gpuBitmapText) {
this._destroyRenderableByUid(uid);
}
this._gpuBitmapText = null;
this._renderer = null;
}
}
/** @ignore */
BitmapTextPipe.extension = {
type: [
Extensions.ExtensionType.WebGLPipes,
Extensions.ExtensionType.WebGPUPipes,
Extensions.ExtensionType.CanvasPipes
],
name: "bitmapText"
};
function syncWithProxy(container, proxy) {
proxy.groupTransform = container.groupTransform;
proxy.groupColorAlpha = container.groupColorAlpha;
proxy.groupColor = container.groupColor;
proxy.groupBlendMode = container.groupBlendMode;
proxy.globalDisplayStatus = container.globalDisplayStatus;
proxy.groupTransform = container.groupTransform;
proxy.localDisplayStatus = container.localDisplayStatus;
proxy.groupAlpha = container.groupAlpha;
proxy._roundPixels = container._roundPixels;
}
exports.BitmapTextPipe = BitmapTextPipe;
//# sourceMappingURL=BitmapTextPipe.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
import { Cache } from '../../assets/cache/Cache.mjs';
import { ExtensionType } from '../../extensions/Extensions.mjs';
import { BigPool } from '../../utils/pool/PoolGroup.mjs';
import { Graphics } from '../graphics/shared/Graphics.mjs';
import { SdfShader } from '../text/sdfShader/SdfShader.mjs';
import { BitmapFontManager } from './BitmapFontManager.mjs';
import { getBitmapTextLayout } from './utils/getBitmapTextLayout.mjs';
"use strict";
class BitmapTextPipe {
constructor(renderer) {
this._gpuBitmapText = {};
this._destroyRenderableBound = this.destroyRenderable.bind(this);
this._renderer = renderer;
}
validateRenderable(bitmapText) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
if (bitmapText._didTextUpdate) {
bitmapText._didTextUpdate = false;
this._updateContext(bitmapText, graphicsRenderable);
}
return this._renderer.renderPipes.graphics.validateRenderable(graphicsRenderable);
}
addRenderable(bitmapText, instructionSet) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
syncWithProxy(bitmapText, graphicsRenderable);
if (bitmapText._didTextUpdate) {
bitmapText._didTextUpdate = false;
this._updateContext(bitmapText, graphicsRenderable);
}
this._renderer.renderPipes.graphics.addRenderable(graphicsRenderable, instructionSet);
if (graphicsRenderable.context.customShader) {
this._updateDistanceField(bitmapText);
}
}
destroyRenderable(bitmapText) {
bitmapText.off("destroyed", this._destroyRenderableBound);
this._destroyRenderableByUid(bitmapText.uid);
}
_destroyRenderableByUid(renderableUid) {
const context = this._gpuBitmapText[renderableUid].context;
if (context.customShader) {
BigPool.return(context.customShader);
context.customShader = null;
}
BigPool.return(this._gpuBitmapText[renderableUid]);
this._gpuBitmapText[renderableUid] = null;
}
updateRenderable(bitmapText) {
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
syncWithProxy(bitmapText, graphicsRenderable);
this._renderer.renderPipes.graphics.updateRenderable(graphicsRenderable);
if (graphicsRenderable.context.customShader) {
this._updateDistanceField(bitmapText);
}
}
_updateContext(bitmapText, proxyGraphics) {
const { context } = proxyGraphics;
const bitmapFont = BitmapFontManager.getFont(bitmapText.text, bitmapText._style);
context.clear();
if (bitmapFont.distanceField.type !== "none") {
if (!context.customShader) {
context.customShader = BigPool.get(SdfShader);
}
}
const chars = Array.from(bitmapText.text);
const style = bitmapText._style;
let currentY = bitmapFont.baseLineOffset;
const bitmapTextLayout = getBitmapTextLayout(chars, style, bitmapFont, true);
let index = 0;
const padding = style.padding;
const scale = bitmapTextLayout.scale;
let tx = bitmapTextLayout.width;
let ty = bitmapTextLayout.height + bitmapTextLayout.offsetY;
if (style._stroke) {
tx += style._stroke.width / scale;
ty += style._stroke.width / scale;
}
context.translate(-bitmapText._anchor._x * tx - padding, -bitmapText._anchor._y * ty - padding).scale(scale, scale);
const tint = bitmapFont.applyFillAsTint ? style._fill.color : 16777215;
for (let i = 0; i < bitmapTextLayout.lines.length; i++) {
const line = bitmapTextLayout.lines[i];
for (let j = 0; j < line.charPositions.length; j++) {
const char = chars[index++];
const charData = bitmapFont.chars[char];
if (charData?.texture) {
context.texture(
charData.texture,
tint ? tint : "black",
Math.round(line.charPositions[j] + charData.xOffset),
Math.round(currentY + charData.yOffset)
);
}
}
currentY += bitmapFont.lineHeight;
}
}
_getGpuBitmapText(bitmapText) {
return this._gpuBitmapText[bitmapText.uid] || this.initGpuText(bitmapText);
}
initGpuText(bitmapText) {
const proxyRenderable = BigPool.get(Graphics);
this._gpuBitmapText[bitmapText.uid] = proxyRenderable;
this._updateContext(bitmapText, proxyRenderable);
bitmapText.on("destroyed", this._destroyRenderableBound);
return this._gpuBitmapText[bitmapText.uid];
}
_updateDistanceField(bitmapText) {
const context = this._getGpuBitmapText(bitmapText).context;
const fontFamily = bitmapText._style.fontFamily;
const dynamicFont = Cache.get(`${fontFamily}-bitmap`);
const { a, b, c, d } = bitmapText.groupTransform;
const dx = Math.sqrt(a * a + b * b);
const dy = Math.sqrt(c * c + d * d);
const worldScale = (Math.abs(dx) + Math.abs(dy)) / 2;
const fontScale = dynamicFont.baseRenderedFontSize / bitmapText._style.fontSize;
const distance = worldScale * dynamicFont.distanceField.range * (1 / fontScale);
context.customShader.resources.localUniforms.uniforms.uDistance = distance;
}
destroy() {
for (const uid in this._gpuBitmapText) {
this._destroyRenderableByUid(uid);
}
this._gpuBitmapText = null;
this._renderer = null;
}
}
/** @ignore */
BitmapTextPipe.extension = {
type: [
ExtensionType.WebGLPipes,
ExtensionType.WebGPUPipes,
ExtensionType.CanvasPipes
],
name: "bitmapText"
};
function syncWithProxy(container, proxy) {
proxy.groupTransform = container.groupTransform;
proxy.groupColorAlpha = container.groupColorAlpha;
proxy.groupColor = container.groupColor;
proxy.groupBlendMode = container.groupBlendMode;
proxy.globalDisplayStatus = container.globalDisplayStatus;
proxy.groupTransform = container.groupTransform;
proxy.localDisplayStatus = container.localDisplayStatus;
proxy.groupAlpha = container.groupAlpha;
proxy._roundPixels = container._roundPixels;
}
export { BitmapTextPipe };
//# sourceMappingURL=BitmapTextPipe.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,57 @@
import { Texture } from '../../rendering/renderers/shared/texture/Texture';
import { TextStyle } from '../text/TextStyle';
import { AbstractBitmapFont } from './AbstractBitmapFont';
import type { CanvasAndContext } from '../../rendering/renderers/shared/texture/CanvasPool';
export interface DynamicBitmapFontOptions {
style: TextStyle;
skipKerning?: boolean;
resolution?: number;
padding?: number;
overrideFill?: boolean;
overrideSize?: boolean;
textureSize?: number;
mipmap?: boolean;
}
/**
* A BitmapFont that generates its glyphs dynamically.
* @memberof text
* @ignore
*/
export declare class DynamicBitmapFont extends AbstractBitmapFont<DynamicBitmapFont> {
static defaultOptions: DynamicBitmapFontOptions;
/**
* this is a resolution modifier for the font size..
* texture resolution will also be used to scale texture according to its font size also
*/
resolution: number;
/** The pages of the font. */
readonly pages: {
canvasAndContext?: CanvasAndContext;
texture: Texture;
}[];
private readonly _padding;
private readonly _measureCache;
private _currentChars;
private _currentX;
private _currentY;
private _currentPageIndex;
private readonly _style;
private readonly _skipKerning;
private readonly _textureSize;
private readonly _mipmap;
/**
* @param options - The options for the dynamic bitmap font.
*/
constructor(options: DynamicBitmapFontOptions);
ensureCharacters(chars: string): void;
/**
* @deprecated since 8.0.0
* The map of base page textures (i.e., sheets of glyphs).
*/
get pageTextures(): DynamicBitmapFont['pages'];
private _applyKerning;
private _nextPage;
private _setupContext;
private _drawGlyph;
destroy(): void;
}

View File

@@ -0,0 +1,276 @@
'use strict';
var Color = require('../../color/Color.js');
var Rectangle = require('../../maths/shapes/Rectangle.js');
var CanvasPool = require('../../rendering/renderers/shared/texture/CanvasPool.js');
var ImageSource = require('../../rendering/renderers/shared/texture/sources/ImageSource.js');
var Texture = require('../../rendering/renderers/shared/texture/Texture.js');
var deprecation = require('../../utils/logging/deprecation.js');
var CanvasTextMetrics = require('../text/canvas/CanvasTextMetrics.js');
var fontStringFromTextStyle = require('../text/canvas/utils/fontStringFromTextStyle.js');
var getCanvasFillStyle = require('../text/canvas/utils/getCanvasFillStyle.js');
var TextStyle = require('../text/TextStyle.js');
var AbstractBitmapFont = require('./AbstractBitmapFont.js');
var resolveCharacters = require('./utils/resolveCharacters.js');
"use strict";
const _DynamicBitmapFont = class _DynamicBitmapFont extends AbstractBitmapFont.AbstractBitmapFont {
/**
* @param options - The options for the dynamic bitmap font.
*/
constructor(options) {
super();
/**
* this is a resolution modifier for the font size..
* texture resolution will also be used to scale texture according to its font size also
*/
this.resolution = 1;
/** The pages of the font. */
this.pages = [];
this._padding = 0;
this._measureCache = /* @__PURE__ */ Object.create(null);
this._currentChars = [];
this._currentX = 0;
this._currentY = 0;
this._currentPageIndex = -1;
this._skipKerning = false;
const dynamicOptions = { ..._DynamicBitmapFont.defaultOptions, ...options };
this._textureSize = dynamicOptions.textureSize;
this._mipmap = dynamicOptions.mipmap;
const style = dynamicOptions.style.clone();
if (dynamicOptions.overrideFill) {
style._fill.color = 16777215;
style._fill.alpha = 1;
style._fill.texture = Texture.Texture.WHITE;
style._fill.fill = null;
}
this.applyFillAsTint = dynamicOptions.overrideFill;
const requestedFontSize = style.fontSize;
style.fontSize = this.baseMeasurementFontSize;
const font = fontStringFromTextStyle.fontStringFromTextStyle(style);
if (dynamicOptions.overrideSize) {
if (style._stroke) {
style._stroke.width *= this.baseRenderedFontSize / requestedFontSize;
}
} else {
style.fontSize = this.baseRenderedFontSize = requestedFontSize;
}
this._style = style;
this._skipKerning = dynamicOptions.skipKerning ?? false;
this.resolution = dynamicOptions.resolution ?? 1;
this._padding = dynamicOptions.padding ?? 4;
this.fontMetrics = CanvasTextMetrics.CanvasTextMetrics.measureFont(font);
this.lineHeight = style.lineHeight || this.fontMetrics.fontSize || style.fontSize;
}
ensureCharacters(chars) {
const charList = resolveCharacters.resolveCharacters(chars).filter((char) => !this._currentChars.includes(char)).filter((char, index, self) => self.indexOf(char) === index);
if (!charList.length)
return;
this._currentChars = [...this._currentChars, ...charList];
let pageData;
if (this._currentPageIndex === -1) {
pageData = this._nextPage();
} else {
pageData = this.pages[this._currentPageIndex];
}
let { canvas, context } = pageData.canvasAndContext;
let textureSource = pageData.texture.source;
const style = this._style;
let currentX = this._currentX;
let currentY = this._currentY;
const fontScale = this.baseRenderedFontSize / this.baseMeasurementFontSize;
const padding = this._padding * fontScale;
const widthScale = style.fontStyle === "italic" ? 2 : 1;
let maxCharHeight = 0;
let skipTexture = false;
for (let i = 0; i < charList.length; i++) {
const char = charList[i];
const metrics = CanvasTextMetrics.CanvasTextMetrics.measureText(char, style, canvas, false);
metrics.lineHeight = metrics.height;
const width = widthScale * metrics.width * fontScale;
const height = metrics.height * fontScale;
const paddedWidth = width + padding * 2;
const paddedHeight = height + padding * 2;
skipTexture = false;
if (char !== "\n" && char !== "\r" && char !== " " && char !== " ") {
skipTexture = true;
maxCharHeight = Math.ceil(Math.max(paddedHeight, maxCharHeight));
}
if (currentX + paddedWidth > this._textureSize) {
currentY += maxCharHeight;
maxCharHeight = paddedHeight;
currentX = 0;
if (currentY + maxCharHeight > this._textureSize) {
textureSource.update();
const pageData2 = this._nextPage();
canvas = pageData2.canvasAndContext.canvas;
context = pageData2.canvasAndContext.context;
textureSource = pageData2.texture.source;
currentY = 0;
}
}
const xAdvance = width / fontScale - (style.dropShadow?.distance ?? 0) - (style._stroke?.width ?? 0);
this.chars[char] = {
id: char.codePointAt(0),
xOffset: -this._padding,
yOffset: -this._padding,
xAdvance,
kerning: {}
};
if (skipTexture) {
this._drawGlyph(
context,
metrics,
currentX + padding,
currentY + padding,
fontScale,
style
);
const px = textureSource.width * fontScale;
const py = textureSource.height * fontScale;
const frame = new Rectangle.Rectangle(
currentX / px * textureSource.width,
currentY / py * textureSource.height,
paddedWidth / px * textureSource.width,
paddedHeight / py * textureSource.height
);
this.chars[char].texture = new Texture.Texture({
source: textureSource,
frame
});
currentX += Math.ceil(paddedWidth);
}
}
textureSource.update();
this._currentX = currentX;
this._currentY = currentY;
this._skipKerning && this._applyKerning(charList, context);
}
/**
* @deprecated since 8.0.0
* The map of base page textures (i.e., sheets of glyphs).
*/
get pageTextures() {
deprecation.deprecation(deprecation.v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
return this.pages;
}
_applyKerning(newChars, context) {
const measureCache = this._measureCache;
for (let i = 0; i < newChars.length; i++) {
const first = newChars[i];
for (let j = 0; j < this._currentChars.length; j++) {
const second = this._currentChars[j];
let c1 = measureCache[first];
if (!c1)
c1 = measureCache[first] = context.measureText(first).width;
let c2 = measureCache[second];
if (!c2)
c2 = measureCache[second] = context.measureText(second).width;
let total = context.measureText(first + second).width;
let amount = total - (c1 + c2);
if (amount) {
this.chars[first].kerning[second] = amount;
}
total = context.measureText(first + second).width;
amount = total - (c1 + c2);
if (amount) {
this.chars[second].kerning[first] = amount;
}
}
}
}
_nextPage() {
this._currentPageIndex++;
const textureResolution = this.resolution;
const canvasAndContext = CanvasPool.CanvasPool.getOptimalCanvasAndContext(
this._textureSize,
this._textureSize,
textureResolution
);
this._setupContext(canvasAndContext.context, this._style, textureResolution);
const resolution = textureResolution * (this.baseRenderedFontSize / this.baseMeasurementFontSize);
const texture = new Texture.Texture({
source: new ImageSource.ImageSource({
resource: canvasAndContext.canvas,
resolution,
alphaMode: "premultiply-alpha-on-upload",
autoGenerateMipmaps: this._mipmap
})
});
const pageData = {
canvasAndContext,
texture
};
this.pages[this._currentPageIndex] = pageData;
return pageData;
}
// canvas style!
_setupContext(context, style, resolution) {
style.fontSize = this.baseRenderedFontSize;
context.scale(resolution, resolution);
context.font = fontStringFromTextStyle.fontStringFromTextStyle(style);
style.fontSize = this.baseMeasurementFontSize;
context.textBaseline = style.textBaseline;
const stroke = style._stroke;
const strokeThickness = stroke?.width ?? 0;
if (stroke) {
context.lineWidth = strokeThickness;
context.lineJoin = stroke.join;
context.miterLimit = stroke.miterLimit;
context.strokeStyle = getCanvasFillStyle.getCanvasFillStyle(stroke, context);
}
if (style._fill) {
context.fillStyle = getCanvasFillStyle.getCanvasFillStyle(style._fill, context);
}
if (style.dropShadow) {
const shadowOptions = style.dropShadow;
const rgb = Color.Color.shared.setValue(shadowOptions.color).toArray();
const dropShadowBlur = shadowOptions.blur * resolution;
const dropShadowDistance = shadowOptions.distance * resolution;
context.shadowColor = `rgba(${rgb[0] * 255},${rgb[1] * 255},${rgb[2] * 255},${shadowOptions.alpha})`;
context.shadowBlur = dropShadowBlur;
context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance;
context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance;
} else {
context.shadowColor = "black";
context.shadowBlur = 0;
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
}
}
_drawGlyph(context, metrics, x, y, fontScale, style) {
const char = metrics.text;
const fontProperties = metrics.fontProperties;
const stroke = style._stroke;
const strokeThickness = (stroke?.width ?? 0) * fontScale;
const tx = x + strokeThickness / 2;
const ty = y - strokeThickness / 2;
const descent = fontProperties.descent * fontScale;
const lineHeight = metrics.lineHeight * fontScale;
if (style.stroke && strokeThickness) {
context.strokeText(char, tx, ty + lineHeight - descent);
}
if (style._fill) {
context.fillText(char, tx, ty + lineHeight - descent);
}
}
destroy() {
super.destroy();
for (let i = 0; i < this.pages.length; i++) {
const { canvasAndContext, texture } = this.pages[i];
canvasAndContext.canvas.width = canvasAndContext.canvas.width;
CanvasPool.CanvasPool.returnCanvasAndContext(canvasAndContext);
texture.destroy(true);
}
this.pages = null;
}
};
_DynamicBitmapFont.defaultOptions = {
textureSize: 512,
style: new TextStyle.TextStyle(),
mipmap: true
};
let DynamicBitmapFont = _DynamicBitmapFont;
exports.DynamicBitmapFont = DynamicBitmapFont;
//# sourceMappingURL=DynamicBitmapFont.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,274 @@
import { Color } from '../../color/Color.mjs';
import { Rectangle } from '../../maths/shapes/Rectangle.mjs';
import { CanvasPool } from '../../rendering/renderers/shared/texture/CanvasPool.mjs';
import { ImageSource } from '../../rendering/renderers/shared/texture/sources/ImageSource.mjs';
import { Texture } from '../../rendering/renderers/shared/texture/Texture.mjs';
import { deprecation, v8_0_0 } from '../../utils/logging/deprecation.mjs';
import { CanvasTextMetrics } from '../text/canvas/CanvasTextMetrics.mjs';
import { fontStringFromTextStyle } from '../text/canvas/utils/fontStringFromTextStyle.mjs';
import { getCanvasFillStyle } from '../text/canvas/utils/getCanvasFillStyle.mjs';
import { TextStyle } from '../text/TextStyle.mjs';
import { AbstractBitmapFont } from './AbstractBitmapFont.mjs';
import { resolveCharacters } from './utils/resolveCharacters.mjs';
"use strict";
const _DynamicBitmapFont = class _DynamicBitmapFont extends AbstractBitmapFont {
/**
* @param options - The options for the dynamic bitmap font.
*/
constructor(options) {
super();
/**
* this is a resolution modifier for the font size..
* texture resolution will also be used to scale texture according to its font size also
*/
this.resolution = 1;
/** The pages of the font. */
this.pages = [];
this._padding = 0;
this._measureCache = /* @__PURE__ */ Object.create(null);
this._currentChars = [];
this._currentX = 0;
this._currentY = 0;
this._currentPageIndex = -1;
this._skipKerning = false;
const dynamicOptions = { ..._DynamicBitmapFont.defaultOptions, ...options };
this._textureSize = dynamicOptions.textureSize;
this._mipmap = dynamicOptions.mipmap;
const style = dynamicOptions.style.clone();
if (dynamicOptions.overrideFill) {
style._fill.color = 16777215;
style._fill.alpha = 1;
style._fill.texture = Texture.WHITE;
style._fill.fill = null;
}
this.applyFillAsTint = dynamicOptions.overrideFill;
const requestedFontSize = style.fontSize;
style.fontSize = this.baseMeasurementFontSize;
const font = fontStringFromTextStyle(style);
if (dynamicOptions.overrideSize) {
if (style._stroke) {
style._stroke.width *= this.baseRenderedFontSize / requestedFontSize;
}
} else {
style.fontSize = this.baseRenderedFontSize = requestedFontSize;
}
this._style = style;
this._skipKerning = dynamicOptions.skipKerning ?? false;
this.resolution = dynamicOptions.resolution ?? 1;
this._padding = dynamicOptions.padding ?? 4;
this.fontMetrics = CanvasTextMetrics.measureFont(font);
this.lineHeight = style.lineHeight || this.fontMetrics.fontSize || style.fontSize;
}
ensureCharacters(chars) {
const charList = resolveCharacters(chars).filter((char) => !this._currentChars.includes(char)).filter((char, index, self) => self.indexOf(char) === index);
if (!charList.length)
return;
this._currentChars = [...this._currentChars, ...charList];
let pageData;
if (this._currentPageIndex === -1) {
pageData = this._nextPage();
} else {
pageData = this.pages[this._currentPageIndex];
}
let { canvas, context } = pageData.canvasAndContext;
let textureSource = pageData.texture.source;
const style = this._style;
let currentX = this._currentX;
let currentY = this._currentY;
const fontScale = this.baseRenderedFontSize / this.baseMeasurementFontSize;
const padding = this._padding * fontScale;
const widthScale = style.fontStyle === "italic" ? 2 : 1;
let maxCharHeight = 0;
let skipTexture = false;
for (let i = 0; i < charList.length; i++) {
const char = charList[i];
const metrics = CanvasTextMetrics.measureText(char, style, canvas, false);
metrics.lineHeight = metrics.height;
const width = widthScale * metrics.width * fontScale;
const height = metrics.height * fontScale;
const paddedWidth = width + padding * 2;
const paddedHeight = height + padding * 2;
skipTexture = false;
if (char !== "\n" && char !== "\r" && char !== " " && char !== " ") {
skipTexture = true;
maxCharHeight = Math.ceil(Math.max(paddedHeight, maxCharHeight));
}
if (currentX + paddedWidth > this._textureSize) {
currentY += maxCharHeight;
maxCharHeight = paddedHeight;
currentX = 0;
if (currentY + maxCharHeight > this._textureSize) {
textureSource.update();
const pageData2 = this._nextPage();
canvas = pageData2.canvasAndContext.canvas;
context = pageData2.canvasAndContext.context;
textureSource = pageData2.texture.source;
currentY = 0;
}
}
const xAdvance = width / fontScale - (style.dropShadow?.distance ?? 0) - (style._stroke?.width ?? 0);
this.chars[char] = {
id: char.codePointAt(0),
xOffset: -this._padding,
yOffset: -this._padding,
xAdvance,
kerning: {}
};
if (skipTexture) {
this._drawGlyph(
context,
metrics,
currentX + padding,
currentY + padding,
fontScale,
style
);
const px = textureSource.width * fontScale;
const py = textureSource.height * fontScale;
const frame = new Rectangle(
currentX / px * textureSource.width,
currentY / py * textureSource.height,
paddedWidth / px * textureSource.width,
paddedHeight / py * textureSource.height
);
this.chars[char].texture = new Texture({
source: textureSource,
frame
});
currentX += Math.ceil(paddedWidth);
}
}
textureSource.update();
this._currentX = currentX;
this._currentY = currentY;
this._skipKerning && this._applyKerning(charList, context);
}
/**
* @deprecated since 8.0.0
* The map of base page textures (i.e., sheets of glyphs).
*/
get pageTextures() {
deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead.");
return this.pages;
}
_applyKerning(newChars, context) {
const measureCache = this._measureCache;
for (let i = 0; i < newChars.length; i++) {
const first = newChars[i];
for (let j = 0; j < this._currentChars.length; j++) {
const second = this._currentChars[j];
let c1 = measureCache[first];
if (!c1)
c1 = measureCache[first] = context.measureText(first).width;
let c2 = measureCache[second];
if (!c2)
c2 = measureCache[second] = context.measureText(second).width;
let total = context.measureText(first + second).width;
let amount = total - (c1 + c2);
if (amount) {
this.chars[first].kerning[second] = amount;
}
total = context.measureText(first + second).width;
amount = total - (c1 + c2);
if (amount) {
this.chars[second].kerning[first] = amount;
}
}
}
}
_nextPage() {
this._currentPageIndex++;
const textureResolution = this.resolution;
const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(
this._textureSize,
this._textureSize,
textureResolution
);
this._setupContext(canvasAndContext.context, this._style, textureResolution);
const resolution = textureResolution * (this.baseRenderedFontSize / this.baseMeasurementFontSize);
const texture = new Texture({
source: new ImageSource({
resource: canvasAndContext.canvas,
resolution,
alphaMode: "premultiply-alpha-on-upload",
autoGenerateMipmaps: this._mipmap
})
});
const pageData = {
canvasAndContext,
texture
};
this.pages[this._currentPageIndex] = pageData;
return pageData;
}
// canvas style!
_setupContext(context, style, resolution) {
style.fontSize = this.baseRenderedFontSize;
context.scale(resolution, resolution);
context.font = fontStringFromTextStyle(style);
style.fontSize = this.baseMeasurementFontSize;
context.textBaseline = style.textBaseline;
const stroke = style._stroke;
const strokeThickness = stroke?.width ?? 0;
if (stroke) {
context.lineWidth = strokeThickness;
context.lineJoin = stroke.join;
context.miterLimit = stroke.miterLimit;
context.strokeStyle = getCanvasFillStyle(stroke, context);
}
if (style._fill) {
context.fillStyle = getCanvasFillStyle(style._fill, context);
}
if (style.dropShadow) {
const shadowOptions = style.dropShadow;
const rgb = Color.shared.setValue(shadowOptions.color).toArray();
const dropShadowBlur = shadowOptions.blur * resolution;
const dropShadowDistance = shadowOptions.distance * resolution;
context.shadowColor = `rgba(${rgb[0] * 255},${rgb[1] * 255},${rgb[2] * 255},${shadowOptions.alpha})`;
context.shadowBlur = dropShadowBlur;
context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance;
context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance;
} else {
context.shadowColor = "black";
context.shadowBlur = 0;
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
}
}
_drawGlyph(context, metrics, x, y, fontScale, style) {
const char = metrics.text;
const fontProperties = metrics.fontProperties;
const stroke = style._stroke;
const strokeThickness = (stroke?.width ?? 0) * fontScale;
const tx = x + strokeThickness / 2;
const ty = y - strokeThickness / 2;
const descent = fontProperties.descent * fontScale;
const lineHeight = metrics.lineHeight * fontScale;
if (style.stroke && strokeThickness) {
context.strokeText(char, tx, ty + lineHeight - descent);
}
if (style._fill) {
context.fillText(char, tx, ty + lineHeight - descent);
}
}
destroy() {
super.destroy();
for (let i = 0; i < this.pages.length; i++) {
const { canvasAndContext, texture } = this.pages[i];
canvasAndContext.canvas.width = canvasAndContext.canvas.width;
CanvasPool.returnCanvasAndContext(canvasAndContext);
texture.destroy(true);
}
this.pages = null;
}
};
_DynamicBitmapFont.defaultOptions = {
textureSize: 512,
style: new TextStyle(),
mipmap: true
};
let DynamicBitmapFont = _DynamicBitmapFont;
export { DynamicBitmapFont };
//# sourceMappingURL=DynamicBitmapFont.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
declare global
{
namespace PixiMixins
{
interface RendererPipes
{
bitmapText: import('./BitmapTextPipe').BitmapTextPipe;
}
}
}
export {};

View File

@@ -0,0 +1,51 @@
import type { BitmapFontData } from '../AbstractBitmapFont';
/**
* Internal data format used to convert to BitmapFontData.
* @private
*/
export interface BitmapFontRawData {
info: {
face: string;
size: string;
}[];
common: {
lineHeight: string;
base: string;
}[];
page: {
id: string;
file: string;
}[];
chars: {
count: number;
}[];
char: {
id: string;
page: string;
xoffset: string;
yoffset: string;
xadvance: string;
x: string;
y: string;
width: string;
height: string;
letter?: string;
char?: string;
}[];
kernings?: {
count: number;
}[];
kerning?: {
first: string;
second: string;
amount: string;
}[];
distanceField?: {
fieldType: 'sdf' | 'msdf' | 'none';
distanceRange: string;
}[];
}
export declare const bitmapFontTextParser: {
test(data: string | XMLDocument | BitmapFontData): boolean;
parse(txt: string): BitmapFontData;
};

View File

@@ -0,0 +1,98 @@
'use strict';
"use strict";
const bitmapFontTextParser = {
test(data) {
return typeof data === "string" && data.startsWith("info face=");
},
parse(txt) {
const items = txt.match(/^[a-z]+\s+.+$/gm);
const rawData = {
info: [],
common: [],
page: [],
char: [],
chars: [],
kerning: [],
kernings: [],
distanceField: []
};
for (const i in items) {
const name = items[i].match(/^[a-z]+/gm)[0];
const attributeList = items[i].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm);
const itemData = {};
for (const i2 in attributeList) {
const split = attributeList[i2].split("=");
const key = split[0];
const strValue = split[1].replace(/"/gm, "");
const floatValue = parseFloat(strValue);
const value = isNaN(floatValue) ? strValue : floatValue;
itemData[key] = value;
}
rawData[name].push(itemData);
}
const font = {
chars: {},
pages: [],
lineHeight: 0,
fontSize: 0,
fontFamily: "",
distanceField: null,
baseLineOffset: 0
};
const [info] = rawData.info;
const [common] = rawData.common;
const [distanceField] = rawData.distanceField ?? [];
if (distanceField) {
font.distanceField = {
range: parseInt(distanceField.distanceRange, 10),
type: distanceField.fieldType
};
}
font.fontSize = parseInt(info.size, 10);
font.fontFamily = info.face;
font.lineHeight = parseInt(common.lineHeight, 10);
const page = rawData.page;
for (let i = 0; i < page.length; i++) {
font.pages.push({
id: parseInt(page[i].id, 10) || 0,
file: page[i].file
});
}
const map = {};
font.baseLineOffset = font.lineHeight - parseInt(common.base, 10);
const char = rawData.char;
for (let i = 0; i < char.length; i++) {
const charNode = char[i];
const id = parseInt(charNode.id, 10);
let letter = charNode.letter ?? charNode.char ?? String.fromCharCode(id);
if (letter === "space")
letter = " ";
map[id] = letter;
font.chars[letter] = {
id,
// texture deets..
page: parseInt(charNode.page, 10) || 0,
x: parseInt(charNode.x, 10),
y: parseInt(charNode.y, 10),
width: parseInt(charNode.width, 10),
height: parseInt(charNode.height, 10),
xOffset: parseInt(charNode.xoffset, 10),
yOffset: parseInt(charNode.yoffset, 10),
xAdvance: parseInt(charNode.xadvance, 10),
kerning: {}
};
}
const kerning = rawData.kerning || [];
for (let i = 0; i < kerning.length; i++) {
const first = parseInt(kerning[i].first, 10);
const second = parseInt(kerning[i].second, 10);
const amount = parseInt(kerning[i].amount, 10);
font.chars[map[second]].kerning[map[first]] = amount;
}
return font;
}
};
exports.bitmapFontTextParser = bitmapFontTextParser;
//# sourceMappingURL=bitmapFontTextParser.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,96 @@
"use strict";
const bitmapFontTextParser = {
test(data) {
return typeof data === "string" && data.startsWith("info face=");
},
parse(txt) {
const items = txt.match(/^[a-z]+\s+.+$/gm);
const rawData = {
info: [],
common: [],
page: [],
char: [],
chars: [],
kerning: [],
kernings: [],
distanceField: []
};
for (const i in items) {
const name = items[i].match(/^[a-z]+/gm)[0];
const attributeList = items[i].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm);
const itemData = {};
for (const i2 in attributeList) {
const split = attributeList[i2].split("=");
const key = split[0];
const strValue = split[1].replace(/"/gm, "");
const floatValue = parseFloat(strValue);
const value = isNaN(floatValue) ? strValue : floatValue;
itemData[key] = value;
}
rawData[name].push(itemData);
}
const font = {
chars: {},
pages: [],
lineHeight: 0,
fontSize: 0,
fontFamily: "",
distanceField: null,
baseLineOffset: 0
};
const [info] = rawData.info;
const [common] = rawData.common;
const [distanceField] = rawData.distanceField ?? [];
if (distanceField) {
font.distanceField = {
range: parseInt(distanceField.distanceRange, 10),
type: distanceField.fieldType
};
}
font.fontSize = parseInt(info.size, 10);
font.fontFamily = info.face;
font.lineHeight = parseInt(common.lineHeight, 10);
const page = rawData.page;
for (let i = 0; i < page.length; i++) {
font.pages.push({
id: parseInt(page[i].id, 10) || 0,
file: page[i].file
});
}
const map = {};
font.baseLineOffset = font.lineHeight - parseInt(common.base, 10);
const char = rawData.char;
for (let i = 0; i < char.length; i++) {
const charNode = char[i];
const id = parseInt(charNode.id, 10);
let letter = charNode.letter ?? charNode.char ?? String.fromCharCode(id);
if (letter === "space")
letter = " ";
map[id] = letter;
font.chars[letter] = {
id,
// texture deets..
page: parseInt(charNode.page, 10) || 0,
x: parseInt(charNode.x, 10),
y: parseInt(charNode.y, 10),
width: parseInt(charNode.width, 10),
height: parseInt(charNode.height, 10),
xOffset: parseInt(charNode.xoffset, 10),
yOffset: parseInt(charNode.yoffset, 10),
xAdvance: parseInt(charNode.xadvance, 10),
kerning: {}
};
}
const kerning = rawData.kerning || [];
for (let i = 0; i < kerning.length; i++) {
const first = parseInt(kerning[i].first, 10);
const second = parseInt(kerning[i].second, 10);
const amount = parseInt(kerning[i].amount, 10);
font.chars[map[second]].kerning[map[first]] = amount;
}
return font;
}
};
export { bitmapFontTextParser };
//# sourceMappingURL=bitmapFontTextParser.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import type { BitmapFontData } from '../AbstractBitmapFont';
export declare const bitmapFontXMLParser: {
test(data: string | XMLDocument | BitmapFontData): boolean;
parse(xml: Document): BitmapFontData;
};

View File

@@ -0,0 +1,76 @@
'use strict';
"use strict";
const bitmapFontXMLParser = {
test(data) {
const xml = data;
return typeof xml !== "string" && "getElementsByTagName" in xml && xml.getElementsByTagName("page").length && xml.getElementsByTagName("info")[0].getAttribute("face") !== null;
},
parse(xml) {
const data = {
chars: {},
pages: [],
lineHeight: 0,
fontSize: 0,
fontFamily: "",
distanceField: null,
baseLineOffset: 0
};
const info = xml.getElementsByTagName("info")[0];
const common = xml.getElementsByTagName("common")[0];
const distanceField = xml.getElementsByTagName("distanceField")[0];
if (distanceField) {
data.distanceField = {
type: distanceField.getAttribute("fieldType"),
range: parseInt(distanceField.getAttribute("distanceRange"), 10)
};
}
const page = xml.getElementsByTagName("page");
const char = xml.getElementsByTagName("char");
const kerning = xml.getElementsByTagName("kerning");
data.fontSize = parseInt(info.getAttribute("size"), 10);
data.fontFamily = info.getAttribute("face");
data.lineHeight = parseInt(common.getAttribute("lineHeight"), 10);
for (let i = 0; i < page.length; i++) {
data.pages.push({
id: parseInt(page[i].getAttribute("id"), 10) || 0,
file: page[i].getAttribute("file")
});
}
const map = {};
data.baseLineOffset = data.lineHeight - parseInt(common.getAttribute("base"), 10);
for (let i = 0; i < char.length; i++) {
const charNode = char[i];
const id = parseInt(charNode.getAttribute("id"), 10);
let letter = charNode.getAttribute("letter") ?? charNode.getAttribute("char") ?? String.fromCharCode(id);
if (letter === "space")
letter = " ";
map[id] = letter;
data.chars[letter] = {
id,
// texture deets..
page: parseInt(charNode.getAttribute("page"), 10) || 0,
x: parseInt(charNode.getAttribute("x"), 10),
y: parseInt(charNode.getAttribute("y"), 10),
width: parseInt(charNode.getAttribute("width"), 10),
height: parseInt(charNode.getAttribute("height"), 10),
// render deets..
xOffset: parseInt(charNode.getAttribute("xoffset"), 10),
yOffset: parseInt(charNode.getAttribute("yoffset"), 10),
// + baseLineOffset,
xAdvance: parseInt(charNode.getAttribute("xadvance"), 10),
kerning: {}
};
}
for (let i = 0; i < kerning.length; i++) {
const first = parseInt(kerning[i].getAttribute("first"), 10);
const second = parseInt(kerning[i].getAttribute("second"), 10);
const amount = parseInt(kerning[i].getAttribute("amount"), 10);
data.chars[map[second]].kerning[map[first]] = amount;
}
return data;
}
};
exports.bitmapFontXMLParser = bitmapFontXMLParser;
//# sourceMappingURL=bitmapFontXMLParser.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,74 @@
"use strict";
const bitmapFontXMLParser = {
test(data) {
const xml = data;
return typeof xml !== "string" && "getElementsByTagName" in xml && xml.getElementsByTagName("page").length && xml.getElementsByTagName("info")[0].getAttribute("face") !== null;
},
parse(xml) {
const data = {
chars: {},
pages: [],
lineHeight: 0,
fontSize: 0,
fontFamily: "",
distanceField: null,
baseLineOffset: 0
};
const info = xml.getElementsByTagName("info")[0];
const common = xml.getElementsByTagName("common")[0];
const distanceField = xml.getElementsByTagName("distanceField")[0];
if (distanceField) {
data.distanceField = {
type: distanceField.getAttribute("fieldType"),
range: parseInt(distanceField.getAttribute("distanceRange"), 10)
};
}
const page = xml.getElementsByTagName("page");
const char = xml.getElementsByTagName("char");
const kerning = xml.getElementsByTagName("kerning");
data.fontSize = parseInt(info.getAttribute("size"), 10);
data.fontFamily = info.getAttribute("face");
data.lineHeight = parseInt(common.getAttribute("lineHeight"), 10);
for (let i = 0; i < page.length; i++) {
data.pages.push({
id: parseInt(page[i].getAttribute("id"), 10) || 0,
file: page[i].getAttribute("file")
});
}
const map = {};
data.baseLineOffset = data.lineHeight - parseInt(common.getAttribute("base"), 10);
for (let i = 0; i < char.length; i++) {
const charNode = char[i];
const id = parseInt(charNode.getAttribute("id"), 10);
let letter = charNode.getAttribute("letter") ?? charNode.getAttribute("char") ?? String.fromCharCode(id);
if (letter === "space")
letter = " ";
map[id] = letter;
data.chars[letter] = {
id,
// texture deets..
page: parseInt(charNode.getAttribute("page"), 10) || 0,
x: parseInt(charNode.getAttribute("x"), 10),
y: parseInt(charNode.getAttribute("y"), 10),
width: parseInt(charNode.getAttribute("width"), 10),
height: parseInt(charNode.getAttribute("height"), 10),
// render deets..
xOffset: parseInt(charNode.getAttribute("xoffset"), 10),
yOffset: parseInt(charNode.getAttribute("yoffset"), 10),
// + baseLineOffset,
xAdvance: parseInt(charNode.getAttribute("xadvance"), 10),
kerning: {}
};
}
for (let i = 0; i < kerning.length; i++) {
const first = parseInt(kerning[i].getAttribute("first"), 10);
const second = parseInt(kerning[i].getAttribute("second"), 10);
const amount = parseInt(kerning[i].getAttribute("amount"), 10);
data.chars[map[second]].kerning[map[first]] = amount;
}
return data;
}
};
export { bitmapFontXMLParser };
//# sourceMappingURL=bitmapFontXMLParser.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import type { BitmapFontData } from '../AbstractBitmapFont';
export declare const bitmapFontXMLStringParser: {
test(data: string | XMLDocument | BitmapFontData): boolean;
parse(data: string): BitmapFontData;
};

View File

@@ -0,0 +1,20 @@
'use strict';
var adapter = require('../../../environment/adapter.js');
var bitmapFontXMLParser = require('./bitmapFontXMLParser.js');
"use strict";
const bitmapFontXMLStringParser = {
test(data) {
if (typeof data === "string" && data.includes("<font>")) {
return bitmapFontXMLParser.bitmapFontXMLParser.test(adapter.DOMAdapter.get().parseXML(data));
}
return false;
},
parse(data) {
return bitmapFontXMLParser.bitmapFontXMLParser.parse(adapter.DOMAdapter.get().parseXML(data));
}
};
exports.bitmapFontXMLStringParser = bitmapFontXMLStringParser;
//# sourceMappingURL=bitmapFontXMLStringParser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"bitmapFontXMLStringParser.js","sources":["../../../../src/scene/text-bitmap/asset/bitmapFontXMLStringParser.ts"],"sourcesContent":["import { DOMAdapter } from '../../../environment/adapter';\nimport { bitmapFontXMLParser } from './bitmapFontXMLParser';\n\nimport type { BitmapFontData } from '../AbstractBitmapFont';\n\nexport const bitmapFontXMLStringParser = {\n test(data: string | XMLDocument | BitmapFontData): boolean\n {\n if (typeof data === 'string' && data.includes('<font>'))\n {\n return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data));\n }\n\n return false;\n },\n\n parse(data: string): BitmapFontData\n {\n return bitmapFontXMLParser.parse(DOMAdapter.get().parseXML(data));\n }\n};\n"],"names":["bitmapFontXMLParser","DOMAdapter"],"mappings":";;;;;;AAKO,MAAM,yBAA4B,GAAA;AAAA,EACrC,KAAK,IACL,EAAA;AACI,IAAA,IAAI,OAAO,IAAS,KAAA,QAAA,IAAY,IAAK,CAAA,QAAA,CAAS,QAAQ,CACtD,EAAA;AACI,MAAA,OAAOA,wCAAoB,IAAK,CAAAC,kBAAA,CAAW,KAAM,CAAA,QAAA,CAAS,IAAI,CAAC,CAAA,CAAA;AAAA,KACnE;AAEA,IAAO,OAAA,KAAA,CAAA;AAAA,GACX;AAAA,EAEA,MAAM,IACN,EAAA;AACI,IAAA,OAAOD,wCAAoB,KAAM,CAAAC,kBAAA,CAAW,KAAM,CAAA,QAAA,CAAS,IAAI,CAAC,CAAA,CAAA;AAAA,GACpE;AACJ;;;;"}

View File

@@ -0,0 +1,18 @@
import { DOMAdapter } from '../../../environment/adapter.mjs';
import { bitmapFontXMLParser } from './bitmapFontXMLParser.mjs';
"use strict";
const bitmapFontXMLStringParser = {
test(data) {
if (typeof data === "string" && data.includes("<font>")) {
return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data));
}
return false;
},
parse(data) {
return bitmapFontXMLParser.parse(DOMAdapter.get().parseXML(data));
}
};
export { bitmapFontXMLStringParser };
//# sourceMappingURL=bitmapFontXMLStringParser.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"bitmapFontXMLStringParser.mjs","sources":["../../../../src/scene/text-bitmap/asset/bitmapFontXMLStringParser.ts"],"sourcesContent":["import { DOMAdapter } from '../../../environment/adapter';\nimport { bitmapFontXMLParser } from './bitmapFontXMLParser';\n\nimport type { BitmapFontData } from '../AbstractBitmapFont';\n\nexport const bitmapFontXMLStringParser = {\n test(data: string | XMLDocument | BitmapFontData): boolean\n {\n if (typeof data === 'string' && data.includes('<font>'))\n {\n return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data));\n }\n\n return false;\n },\n\n parse(data: string): BitmapFontData\n {\n return bitmapFontXMLParser.parse(DOMAdapter.get().parseXML(data));\n }\n};\n"],"names":[],"mappings":";;;;AAKO,MAAM,yBAA4B,GAAA;AAAA,EACrC,KAAK,IACL,EAAA;AACI,IAAA,IAAI,OAAO,IAAS,KAAA,QAAA,IAAY,IAAK,CAAA,QAAA,CAAS,QAAQ,CACtD,EAAA;AACI,MAAA,OAAO,oBAAoB,IAAK,CAAA,UAAA,CAAW,KAAM,CAAA,QAAA,CAAS,IAAI,CAAC,CAAA,CAAA;AAAA,KACnE;AAEA,IAAO,OAAA,KAAA,CAAA;AAAA,GACX;AAAA,EAEA,MAAM,IACN,EAAA;AACI,IAAA,OAAO,oBAAoB,KAAM,CAAA,UAAA,CAAW,KAAM,CAAA,QAAA,CAAS,IAAI,CAAC,CAAA,CAAA;AAAA,GACpE;AACJ;;;;"}

View File

@@ -0,0 +1,26 @@
import { LoaderParserPriority } from '../../../assets/loader/parsers/LoaderParser';
import { ExtensionType } from '../../../extensions/Extensions';
import { BitmapFont } from '../BitmapFont';
import type { Loader } from '../../../assets/loader/Loader';
import type { ResolvedAsset } from '../../../assets/types';
/** simple loader plugin for loading in bitmap fonts! */
export declare const bitmapFontCachePlugin: {
extension: {
type: ExtensionType.CacheParser;
name: string;
};
test: (asset: BitmapFont) => boolean;
getCacheableAssets(keys: string[], asset: BitmapFont): Record<string, BitmapFont>;
};
export declare const loadBitmapFont: {
extension: {
type: ExtensionType.LoadParser;
priority: LoaderParserPriority;
};
name: string;
test(url: string): boolean;
testParse(data: string): Promise<boolean>;
parse<T>(asset: string, data: ResolvedAsset, loader: Loader): Promise<BitmapFont>;
load<T_1>(url: string, _options: ResolvedAsset): Promise<string>;
unload(bitmapFont: BitmapFont, _resolvedAsset: ResolvedAsset<any>, loader: Loader): Promise<void>;
};

View File

@@ -0,0 +1,82 @@
'use strict';
var LoaderParser = require('../../../assets/loader/parsers/LoaderParser.js');
var copySearchParams = require('../../../assets/utils/copySearchParams.js');
var adapter = require('../../../environment/adapter.js');
var Extensions = require('../../../extensions/Extensions.js');
var path = require('../../../utils/path.js');
var BitmapFont = require('../BitmapFont.js');
var bitmapFontTextParser = require('./bitmapFontTextParser.js');
var bitmapFontXMLStringParser = require('./bitmapFontXMLStringParser.js');
"use strict";
const validExtensions = [".xml", ".fnt"];
const bitmapFontCachePlugin = {
extension: {
type: Extensions.ExtensionType.CacheParser,
name: "cacheBitmapFont"
},
test: (asset) => asset instanceof BitmapFont.BitmapFont,
getCacheableAssets(keys, asset) {
const out = {};
keys.forEach((key) => {
out[key] = asset;
out[`${key}-bitmap`] = asset;
});
out[`${asset.fontFamily}-bitmap`] = asset;
return out;
}
};
const loadBitmapFont = {
extension: {
type: Extensions.ExtensionType.LoadParser,
priority: LoaderParser.LoaderParserPriority.Normal
},
name: "loadBitmapFont",
test(url) {
return validExtensions.includes(path.path.extname(url).toLowerCase());
},
async testParse(data) {
return bitmapFontTextParser.bitmapFontTextParser.test(data) || bitmapFontXMLStringParser.bitmapFontXMLStringParser.test(data);
},
async parse(asset, data, loader) {
const bitmapFontData = bitmapFontTextParser.bitmapFontTextParser.test(asset) ? bitmapFontTextParser.bitmapFontTextParser.parse(asset) : bitmapFontXMLStringParser.bitmapFontXMLStringParser.parse(asset);
const { src } = data;
const { pages } = bitmapFontData;
const textureUrls = [];
const textureOptions = bitmapFontData.distanceField ? {
scaleMode: "linear",
alphaMode: "premultiply-alpha-on-upload",
autoGenerateMipmaps: false,
resolution: 1
} : {};
for (let i = 0; i < pages.length; ++i) {
const pageFile = pages[i].file;
let imagePath = path.path.join(path.path.dirname(src), pageFile);
imagePath = copySearchParams.copySearchParams(imagePath, src);
textureUrls.push({
src: imagePath,
data: textureOptions
});
}
const loadedTextures = await loader.load(textureUrls);
const textures = textureUrls.map((url) => loadedTextures[url.src]);
const bitmapFont = new BitmapFont.BitmapFont({
data: bitmapFontData,
textures
}, src);
return bitmapFont;
},
async load(url, _options) {
const response = await adapter.DOMAdapter.get().fetch(url);
return await response.text();
},
async unload(bitmapFont, _resolvedAsset, loader) {
await Promise.all(bitmapFont.pages.map((page) => loader.unload(page.texture.source._sourceOrigin)));
bitmapFont.destroy();
}
};
exports.bitmapFontCachePlugin = bitmapFontCachePlugin;
exports.loadBitmapFont = loadBitmapFont;
//# sourceMappingURL=loadBitmapFont.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,79 @@
import { LoaderParserPriority } from '../../../assets/loader/parsers/LoaderParser.mjs';
import { copySearchParams } from '../../../assets/utils/copySearchParams.mjs';
import { DOMAdapter } from '../../../environment/adapter.mjs';
import { ExtensionType } from '../../../extensions/Extensions.mjs';
import { path } from '../../../utils/path.mjs';
import { BitmapFont } from '../BitmapFont.mjs';
import { bitmapFontTextParser } from './bitmapFontTextParser.mjs';
import { bitmapFontXMLStringParser } from './bitmapFontXMLStringParser.mjs';
"use strict";
const validExtensions = [".xml", ".fnt"];
const bitmapFontCachePlugin = {
extension: {
type: ExtensionType.CacheParser,
name: "cacheBitmapFont"
},
test: (asset) => asset instanceof BitmapFont,
getCacheableAssets(keys, asset) {
const out = {};
keys.forEach((key) => {
out[key] = asset;
out[`${key}-bitmap`] = asset;
});
out[`${asset.fontFamily}-bitmap`] = asset;
return out;
}
};
const loadBitmapFont = {
extension: {
type: ExtensionType.LoadParser,
priority: LoaderParserPriority.Normal
},
name: "loadBitmapFont",
test(url) {
return validExtensions.includes(path.extname(url).toLowerCase());
},
async testParse(data) {
return bitmapFontTextParser.test(data) || bitmapFontXMLStringParser.test(data);
},
async parse(asset, data, loader) {
const bitmapFontData = bitmapFontTextParser.test(asset) ? bitmapFontTextParser.parse(asset) : bitmapFontXMLStringParser.parse(asset);
const { src } = data;
const { pages } = bitmapFontData;
const textureUrls = [];
const textureOptions = bitmapFontData.distanceField ? {
scaleMode: "linear",
alphaMode: "premultiply-alpha-on-upload",
autoGenerateMipmaps: false,
resolution: 1
} : {};
for (let i = 0; i < pages.length; ++i) {
const pageFile = pages[i].file;
let imagePath = path.join(path.dirname(src), pageFile);
imagePath = copySearchParams(imagePath, src);
textureUrls.push({
src: imagePath,
data: textureOptions
});
}
const loadedTextures = await loader.load(textureUrls);
const textures = textureUrls.map((url) => loadedTextures[url.src]);
const bitmapFont = new BitmapFont({
data: bitmapFontData,
textures
}, src);
return bitmapFont;
},
async load(url, _options) {
const response = await DOMAdapter.get().fetch(url);
return await response.text();
},
async unload(bitmapFont, _resolvedAsset, loader) {
await Promise.all(bitmapFont.pages.map((page) => loader.unload(page.texture.source._sourceOrigin)));
bitmapFont.destroy();
}
};
export { bitmapFontCachePlugin, loadBitmapFont };
//# sourceMappingURL=loadBitmapFont.mjs.map

File diff suppressed because one or more lines are too long

1
node_modules/pixi.js/lib/scene/text-bitmap/init.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

8
node_modules/pixi.js/lib/scene/text-bitmap/init.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
'use strict';
var Extensions = require('../../extensions/Extensions.js');
var BitmapTextPipe = require('./BitmapTextPipe.js');
"use strict";
Extensions.extensions.add(BitmapTextPipe.BitmapTextPipe);
//# sourceMappingURL=init.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"init.js","sources":["../../../src/scene/text-bitmap/init.ts"],"sourcesContent":["import { extensions } from '../../extensions/Extensions';\nimport { BitmapTextPipe } from './BitmapTextPipe';\n\nextensions.add(BitmapTextPipe);\n"],"names":["extensions","BitmapTextPipe"],"mappings":";;;;;;AAGAA,qBAAA,CAAW,IAAIC,6BAAc,CAAA;;"}

6
node_modules/pixi.js/lib/scene/text-bitmap/init.mjs generated vendored Normal file
View File

@@ -0,0 +1,6 @@
import { extensions } from '../../extensions/Extensions.mjs';
import { BitmapTextPipe } from './BitmapTextPipe.mjs';
"use strict";
extensions.add(BitmapTextPipe);
//# sourceMappingURL=init.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"init.mjs","sources":["../../../src/scene/text-bitmap/init.ts"],"sourcesContent":["import { extensions } from '../../extensions/Extensions';\nimport { BitmapTextPipe } from './BitmapTextPipe';\n\nextensions.add(BitmapTextPipe);\n"],"names":[],"mappings":";;;;AAGA,UAAA,CAAW,IAAI,cAAc,CAAA"}

View File

@@ -0,0 +1,16 @@
import type { TextStyle } from '../../text/TextStyle';
import type { AbstractBitmapFont } from '../AbstractBitmapFont';
export interface BitmapTextLayoutData {
width: number;
height: number;
scale: number;
offsetY: number;
lines: {
width: number;
charPositions: number[];
chars: string[];
spaceWidth: number;
spacesIndex: number[];
}[];
}
export declare function getBitmapTextLayout(chars: string[], style: TextStyle, font: AbstractBitmapFont<any>, trimEnd: boolean): BitmapTextLayoutData;

View File

@@ -0,0 +1,161 @@
'use strict';
"use strict";
function getBitmapTextLayout(chars, style, font, trimEnd) {
const layoutData = {
width: 0,
height: 0,
offsetY: 0,
scale: style.fontSize / font.baseMeasurementFontSize,
lines: [{
width: 0,
charPositions: [],
spaceWidth: 0,
spacesIndex: [],
chars: []
}]
};
layoutData.offsetY = font.baseLineOffset;
let currentLine = layoutData.lines[0];
let previousChar = null;
let firstWord = true;
const currentWord = {
spaceWord: false,
width: 0,
start: 0,
index: 0,
// use index to not modify the array as we use it a lot!
positions: [],
chars: []
};
const nextWord = (word) => {
const start = currentLine.width;
for (let j = 0; j < currentWord.index; j++) {
const position = word.positions[j];
currentLine.chars.push(word.chars[j]);
currentLine.charPositions.push(position + start);
}
currentLine.width += word.width;
firstWord = false;
currentWord.width = 0;
currentWord.index = 0;
currentWord.chars.length = 0;
};
const nextLine = () => {
let index = currentLine.chars.length - 1;
if (trimEnd) {
let lastChar = currentLine.chars[index];
while (lastChar === " ") {
currentLine.width -= font.chars[lastChar].xAdvance;
lastChar = currentLine.chars[--index];
}
}
layoutData.width = Math.max(layoutData.width, currentLine.width);
currentLine = {
width: 0,
charPositions: [],
chars: [],
spaceWidth: 0,
spacesIndex: []
};
firstWord = true;
layoutData.lines.push(currentLine);
layoutData.height += font.lineHeight;
};
const scale = font.baseMeasurementFontSize / style.fontSize;
const adjustedLetterSpacing = style.letterSpacing * scale;
const adjustedWordWrapWidth = style.wordWrapWidth * scale;
for (let i = 0; i < chars.length + 1; i++) {
let char;
const isEnd = i === chars.length;
if (!isEnd) {
char = chars[i];
}
const charData = font.chars[char] || font.chars[" "];
const isSpace = /(?:\s)/.test(char);
const isWordBreak = isSpace || char === "\r" || char === "\n" || isEnd;
if (isWordBreak) {
const addWordToNextLine = !firstWord && style.wordWrap && currentLine.width + currentWord.width - adjustedLetterSpacing > adjustedWordWrapWidth;
if (addWordToNextLine) {
nextLine();
nextWord(currentWord);
if (!isEnd) {
currentLine.charPositions.push(0);
}
} else {
currentWord.start = currentLine.width;
nextWord(currentWord);
if (!isEnd) {
currentLine.charPositions.push(0);
}
}
if (char === "\r" || char === "\n") {
if (currentLine.width !== 0) {
nextLine();
}
} else if (!isEnd) {
const spaceWidth = charData.xAdvance + (charData.kerning[previousChar] || 0) + adjustedLetterSpacing;
currentLine.width += spaceWidth;
currentLine.spaceWidth = spaceWidth;
currentLine.spacesIndex.push(currentLine.charPositions.length);
currentLine.chars.push(char);
}
} else {
const kerning = charData.kerning[previousChar] || 0;
const nextCharWidth = charData.xAdvance + kerning + adjustedLetterSpacing;
currentWord.positions[currentWord.index++] = currentWord.width + kerning;
currentWord.chars.push(char);
currentWord.width += nextCharWidth;
}
previousChar = char;
}
nextLine();
if (style.align === "center") {
alignCenter(layoutData);
} else if (style.align === "right") {
alignRight(layoutData);
} else if (style.align === "justify") {
alignJustify(layoutData);
}
return layoutData;
}
function alignCenter(measurementData) {
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
const offset = measurementData.width / 2 - line.width / 2;
for (let j = 0; j < line.charPositions.length; j++) {
line.charPositions[j] += offset;
}
}
}
function alignRight(measurementData) {
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
const offset = measurementData.width - line.width;
for (let j = 0; j < line.charPositions.length; j++) {
line.charPositions[j] += offset;
}
}
}
function alignJustify(measurementData) {
const width = measurementData.width;
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
let indy = 0;
let spaceIndex = line.spacesIndex[indy++];
let offset = 0;
const totalSpaces = line.spacesIndex.length;
const newSpaceWidth = (width - line.width) / totalSpaces;
const spaceWidth = newSpaceWidth;
for (let j = 0; j < line.charPositions.length; j++) {
if (j === spaceIndex) {
spaceIndex = line.spacesIndex[indy++];
offset += spaceWidth;
}
line.charPositions[j] += offset;
}
}
}
exports.getBitmapTextLayout = getBitmapTextLayout;
//# sourceMappingURL=getBitmapTextLayout.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,159 @@
"use strict";
function getBitmapTextLayout(chars, style, font, trimEnd) {
const layoutData = {
width: 0,
height: 0,
offsetY: 0,
scale: style.fontSize / font.baseMeasurementFontSize,
lines: [{
width: 0,
charPositions: [],
spaceWidth: 0,
spacesIndex: [],
chars: []
}]
};
layoutData.offsetY = font.baseLineOffset;
let currentLine = layoutData.lines[0];
let previousChar = null;
let firstWord = true;
const currentWord = {
spaceWord: false,
width: 0,
start: 0,
index: 0,
// use index to not modify the array as we use it a lot!
positions: [],
chars: []
};
const nextWord = (word) => {
const start = currentLine.width;
for (let j = 0; j < currentWord.index; j++) {
const position = word.positions[j];
currentLine.chars.push(word.chars[j]);
currentLine.charPositions.push(position + start);
}
currentLine.width += word.width;
firstWord = false;
currentWord.width = 0;
currentWord.index = 0;
currentWord.chars.length = 0;
};
const nextLine = () => {
let index = currentLine.chars.length - 1;
if (trimEnd) {
let lastChar = currentLine.chars[index];
while (lastChar === " ") {
currentLine.width -= font.chars[lastChar].xAdvance;
lastChar = currentLine.chars[--index];
}
}
layoutData.width = Math.max(layoutData.width, currentLine.width);
currentLine = {
width: 0,
charPositions: [],
chars: [],
spaceWidth: 0,
spacesIndex: []
};
firstWord = true;
layoutData.lines.push(currentLine);
layoutData.height += font.lineHeight;
};
const scale = font.baseMeasurementFontSize / style.fontSize;
const adjustedLetterSpacing = style.letterSpacing * scale;
const adjustedWordWrapWidth = style.wordWrapWidth * scale;
for (let i = 0; i < chars.length + 1; i++) {
let char;
const isEnd = i === chars.length;
if (!isEnd) {
char = chars[i];
}
const charData = font.chars[char] || font.chars[" "];
const isSpace = /(?:\s)/.test(char);
const isWordBreak = isSpace || char === "\r" || char === "\n" || isEnd;
if (isWordBreak) {
const addWordToNextLine = !firstWord && style.wordWrap && currentLine.width + currentWord.width - adjustedLetterSpacing > adjustedWordWrapWidth;
if (addWordToNextLine) {
nextLine();
nextWord(currentWord);
if (!isEnd) {
currentLine.charPositions.push(0);
}
} else {
currentWord.start = currentLine.width;
nextWord(currentWord);
if (!isEnd) {
currentLine.charPositions.push(0);
}
}
if (char === "\r" || char === "\n") {
if (currentLine.width !== 0) {
nextLine();
}
} else if (!isEnd) {
const spaceWidth = charData.xAdvance + (charData.kerning[previousChar] || 0) + adjustedLetterSpacing;
currentLine.width += spaceWidth;
currentLine.spaceWidth = spaceWidth;
currentLine.spacesIndex.push(currentLine.charPositions.length);
currentLine.chars.push(char);
}
} else {
const kerning = charData.kerning[previousChar] || 0;
const nextCharWidth = charData.xAdvance + kerning + adjustedLetterSpacing;
currentWord.positions[currentWord.index++] = currentWord.width + kerning;
currentWord.chars.push(char);
currentWord.width += nextCharWidth;
}
previousChar = char;
}
nextLine();
if (style.align === "center") {
alignCenter(layoutData);
} else if (style.align === "right") {
alignRight(layoutData);
} else if (style.align === "justify") {
alignJustify(layoutData);
}
return layoutData;
}
function alignCenter(measurementData) {
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
const offset = measurementData.width / 2 - line.width / 2;
for (let j = 0; j < line.charPositions.length; j++) {
line.charPositions[j] += offset;
}
}
}
function alignRight(measurementData) {
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
const offset = measurementData.width - line.width;
for (let j = 0; j < line.charPositions.length; j++) {
line.charPositions[j] += offset;
}
}
}
function alignJustify(measurementData) {
const width = measurementData.width;
for (let i = 0; i < measurementData.lines.length; i++) {
const line = measurementData.lines[i];
let indy = 0;
let spaceIndex = line.spacesIndex[indy++];
let offset = 0;
const totalSpaces = line.spacesIndex.length;
const newSpaceWidth = (width - line.width) / totalSpaces;
const spaceWidth = newSpaceWidth;
for (let j = 0; j < line.charPositions.length; j++) {
if (j === spaceIndex) {
spaceIndex = line.spacesIndex[indy++];
offset += spaceWidth;
}
line.charPositions[j] += offset;
}
}
}
export { getBitmapTextLayout };
//# sourceMappingURL=getBitmapTextLayout.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
/**
* Processes the passed character set data and returns a flattened array of all the characters.
*
* Ignored because not directly exposed.
* @ignore
* @param {string | string[] | string[][] } chars
* @returns {string[]} the flattened array of characters
*/
export declare function resolveCharacters(chars: string | (string | string[])[]): string[];

View File

@@ -0,0 +1,40 @@
'use strict';
"use strict";
function resolveCharacters(chars) {
if (chars === "") {
return [];
}
if (typeof chars === "string") {
chars = [chars];
}
const result = [];
for (let i = 0, j = chars.length; i < j; i++) {
const item = chars[i];
if (Array.isArray(item)) {
if (item.length !== 2) {
throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`);
}
if (item[0].length === 0 || item[1].length === 0) {
throw new Error("[BitmapFont]: Invalid character delimiter.");
}
const startCode = item[0].charCodeAt(0);
const endCode = item[1].charCodeAt(0);
if (endCode < startCode) {
throw new Error("[BitmapFont]: Invalid character range.");
}
for (let i2 = startCode, j2 = endCode; i2 <= j2; i2++) {
result.push(String.fromCharCode(i2));
}
} else {
result.push(...Array.from(item));
}
}
if (result.length === 0) {
throw new Error("[BitmapFont]: Empty set when resolving characters.");
}
return result;
}
exports.resolveCharacters = resolveCharacters;
//# sourceMappingURL=resolveCharacters.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"resolveCharacters.js","sources":["../../../../src/scene/text-bitmap/utils/resolveCharacters.ts"],"sourcesContent":["/**\n * Processes the passed character set data and returns a flattened array of all the characters.\n *\n * Ignored because not directly exposed.\n * @ignore\n * @param {string | string[] | string[][] } chars\n * @returns {string[]} the flattened array of characters\n */\n\nexport function resolveCharacters(chars: string | (string | string[])[]): string[]\n{\n // Skip unexpected 'empty set' check at end\n if (chars === '')\n {\n return [];\n }\n\n // Split the chars string into individual characters\n if (typeof chars === 'string')\n {\n chars = [chars];\n }\n\n // Handle an array of characters+ranges\n const result: string[] = [];\n\n for (let i = 0, j = chars.length; i < j; i++)\n {\n const item = chars[i];\n\n // Handle range delimited by start/end chars\n if (Array.isArray(item))\n {\n if (item.length !== 2)\n {\n throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`);\n }\n if (item[0].length === 0 || item[1].length === 0)\n {\n throw new Error('[BitmapFont]: Invalid character delimiter.');\n }\n\n const startCode = item[0].charCodeAt(0);\n const endCode = item[1].charCodeAt(0);\n\n if (endCode < startCode)\n {\n throw new Error('[BitmapFont]: Invalid character range.');\n }\n\n for (let i = startCode, j = endCode; i <= j; i++)\n {\n result.push(String.fromCharCode(i));\n }\n }\n else\n {\n result.push(...Array.from(item));\n }\n }\n\n if (result.length === 0)\n {\n throw new Error('[BitmapFont]: Empty set when resolving characters.');\n }\n\n return result;\n}\n"],"names":["i","j"],"mappings":";;;AASO,SAAS,kBAAkB,KAClC,EAAA;AAEI,EAAA,IAAI,UAAU,EACd,EAAA;AACI,IAAA,OAAO,EAAC,CAAA;AAAA,GACZ;AAGA,EAAI,IAAA,OAAO,UAAU,QACrB,EAAA;AACI,IAAA,KAAA,GAAQ,CAAC,KAAK,CAAA,CAAA;AAAA,GAClB;AAGA,EAAA,MAAM,SAAmB,EAAC,CAAA;AAE1B,EAAA,KAAA,IAAS,IAAI,CAAG,EAAA,CAAA,GAAI,MAAM,MAAQ,EAAA,CAAA,GAAI,GAAG,CACzC,EAAA,EAAA;AACI,IAAM,MAAA,IAAA,GAAO,MAAM,CAAC,CAAA,CAAA;AAGpB,IAAI,IAAA,KAAA,CAAM,OAAQ,CAAA,IAAI,CACtB,EAAA;AACI,MAAI,IAAA,IAAA,CAAK,WAAW,CACpB,EAAA;AACI,QAAA,MAAM,IAAI,KAAA,CAAM,CAAiE,8DAAA,EAAA,IAAA,CAAK,MAAM,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,OACnG;AACA,MAAI,IAAA,IAAA,CAAK,CAAC,CAAE,CAAA,MAAA,KAAW,KAAK,IAAK,CAAA,CAAC,CAAE,CAAA,MAAA,KAAW,CAC/C,EAAA;AACI,QAAM,MAAA,IAAI,MAAM,4CAA4C,CAAA,CAAA;AAAA,OAChE;AAEA,MAAA,MAAM,SAAY,GAAA,IAAA,CAAK,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAA;AACtC,MAAA,MAAM,OAAU,GAAA,IAAA,CAAK,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAA;AAEpC,MAAA,IAAI,UAAU,SACd,EAAA;AACI,QAAM,MAAA,IAAI,MAAM,wCAAwC,CAAA,CAAA;AAAA,OAC5D;AAEA,MAAA,KAAA,IAASA,KAAI,SAAWC,EAAAA,EAAAA,GAAI,OAASD,EAAAA,EAAAA,IAAKC,IAAGD,EAC7C,EAAA,EAAA;AACI,QAAA,MAAA,CAAO,IAAK,CAAA,MAAA,CAAO,YAAaA,CAAAA,EAAC,CAAC,CAAA,CAAA;AAAA,OACtC;AAAA,KAGJ,MAAA;AACI,MAAA,MAAA,CAAO,IAAK,CAAA,GAAG,KAAM,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KACnC;AAAA,GACJ;AAEA,EAAI,IAAA,MAAA,CAAO,WAAW,CACtB,EAAA;AACI,IAAM,MAAA,IAAI,MAAM,oDAAoD,CAAA,CAAA;AAAA,GACxE;AAEA,EAAO,OAAA,MAAA,CAAA;AACX;;;;"}

View File

@@ -0,0 +1,38 @@
"use strict";
function resolveCharacters(chars) {
if (chars === "") {
return [];
}
if (typeof chars === "string") {
chars = [chars];
}
const result = [];
for (let i = 0, j = chars.length; i < j; i++) {
const item = chars[i];
if (Array.isArray(item)) {
if (item.length !== 2) {
throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`);
}
if (item[0].length === 0 || item[1].length === 0) {
throw new Error("[BitmapFont]: Invalid character delimiter.");
}
const startCode = item[0].charCodeAt(0);
const endCode = item[1].charCodeAt(0);
if (endCode < startCode) {
throw new Error("[BitmapFont]: Invalid character range.");
}
for (let i2 = startCode, j2 = endCode; i2 <= j2; i2++) {
result.push(String.fromCharCode(i2));
}
} else {
result.push(...Array.from(item));
}
}
if (result.length === 0) {
throw new Error("[BitmapFont]: Empty set when resolving characters.");
}
return result;
}
export { resolveCharacters };
//# sourceMappingURL=resolveCharacters.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"resolveCharacters.mjs","sources":["../../../../src/scene/text-bitmap/utils/resolveCharacters.ts"],"sourcesContent":["/**\n * Processes the passed character set data and returns a flattened array of all the characters.\n *\n * Ignored because not directly exposed.\n * @ignore\n * @param {string | string[] | string[][] } chars\n * @returns {string[]} the flattened array of characters\n */\n\nexport function resolveCharacters(chars: string | (string | string[])[]): string[]\n{\n // Skip unexpected 'empty set' check at end\n if (chars === '')\n {\n return [];\n }\n\n // Split the chars string into individual characters\n if (typeof chars === 'string')\n {\n chars = [chars];\n }\n\n // Handle an array of characters+ranges\n const result: string[] = [];\n\n for (let i = 0, j = chars.length; i < j; i++)\n {\n const item = chars[i];\n\n // Handle range delimited by start/end chars\n if (Array.isArray(item))\n {\n if (item.length !== 2)\n {\n throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`);\n }\n if (item[0].length === 0 || item[1].length === 0)\n {\n throw new Error('[BitmapFont]: Invalid character delimiter.');\n }\n\n const startCode = item[0].charCodeAt(0);\n const endCode = item[1].charCodeAt(0);\n\n if (endCode < startCode)\n {\n throw new Error('[BitmapFont]: Invalid character range.');\n }\n\n for (let i = startCode, j = endCode; i <= j; i++)\n {\n result.push(String.fromCharCode(i));\n }\n }\n else\n {\n result.push(...Array.from(item));\n }\n }\n\n if (result.length === 0)\n {\n throw new Error('[BitmapFont]: Empty set when resolving characters.');\n }\n\n return result;\n}\n"],"names":["i","j"],"mappings":";AASO,SAAS,kBAAkB,KAClC,EAAA;AAEI,EAAA,IAAI,UAAU,EACd,EAAA;AACI,IAAA,OAAO,EAAC,CAAA;AAAA,GACZ;AAGA,EAAI,IAAA,OAAO,UAAU,QACrB,EAAA;AACI,IAAA,KAAA,GAAQ,CAAC,KAAK,CAAA,CAAA;AAAA,GAClB;AAGA,EAAA,MAAM,SAAmB,EAAC,CAAA;AAE1B,EAAA,KAAA,IAAS,IAAI,CAAG,EAAA,CAAA,GAAI,MAAM,MAAQ,EAAA,CAAA,GAAI,GAAG,CACzC,EAAA,EAAA;AACI,IAAM,MAAA,IAAA,GAAO,MAAM,CAAC,CAAA,CAAA;AAGpB,IAAI,IAAA,KAAA,CAAM,OAAQ,CAAA,IAAI,CACtB,EAAA;AACI,MAAI,IAAA,IAAA,CAAK,WAAW,CACpB,EAAA;AACI,QAAA,MAAM,IAAI,KAAA,CAAM,CAAiE,8DAAA,EAAA,IAAA,CAAK,MAAM,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,OACnG;AACA,MAAI,IAAA,IAAA,CAAK,CAAC,CAAE,CAAA,MAAA,KAAW,KAAK,IAAK,CAAA,CAAC,CAAE,CAAA,MAAA,KAAW,CAC/C,EAAA;AACI,QAAM,MAAA,IAAI,MAAM,4CAA4C,CAAA,CAAA;AAAA,OAChE;AAEA,MAAA,MAAM,SAAY,GAAA,IAAA,CAAK,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAA;AACtC,MAAA,MAAM,OAAU,GAAA,IAAA,CAAK,CAAC,CAAA,CAAE,WAAW,CAAC,CAAA,CAAA;AAEpC,MAAA,IAAI,UAAU,SACd,EAAA;AACI,QAAM,MAAA,IAAI,MAAM,wCAAwC,CAAA,CAAA;AAAA,OAC5D;AAEA,MAAA,KAAA,IAASA,KAAI,SAAWC,EAAAA,EAAAA,GAAI,OAASD,EAAAA,EAAAA,IAAKC,IAAGD,EAC7C,EAAA,EAAA;AACI,QAAA,MAAA,CAAO,IAAK,CAAA,MAAA,CAAO,YAAaA,CAAAA,EAAC,CAAC,CAAA,CAAA;AAAA,OACtC;AAAA,KAGJ,MAAA;AACI,MAAA,MAAA,CAAO,IAAK,CAAA,GAAG,KAAM,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KACnC;AAAA,GACJ;AAEA,EAAI,IAAA,MAAA,CAAO,WAAW,CACtB,EAAA;AACI,IAAM,MAAA,IAAI,MAAM,oDAAoD,CAAA,CAAA;AAAA,GACxE;AAEA,EAAO,OAAA,MAAA,CAAA;AACX;;;;"}