UNPKG

@pixi-essentials/svg

Version:
1,077 lines (1,016 loc) 35.5 kB
import { Bounds } from 'pixi.js'; import { CanvasSource } from 'pixi.js'; import { Container } from 'pixi.js'; import { Graphics } from 'pixi.js'; import type { LineCap } from 'pixi.js'; import type { LineJoin } from 'pixi.js'; import type { Matrix } from 'pixi.js'; import type { PointData } from 'pixi.js'; import { Rectangle } from 'pixi.js'; import { Renderer } from 'pixi.js'; import type { RenderTexture } from 'pixi.js'; import { Sprite } from 'pixi.js'; import { TextStyle } from 'pixi.js'; import { Texture } from 'pixi.js'; import { TextureSource } from 'pixi.js'; /** * @public * @typeParam N - The internal property for marking rectangles. */ declare interface AreaAllocator<N> { readonly width: number; readonly height: number; allocate(width: number, height: number): Rectangle & N; free(area: N): void; } /** * This allocator issues texture backed by a canvas. You can draw on to that canvas to source * each texture. * * @public */ declare class CanvasTextureAllocator extends TextureAllocator<CanvasSource> { /** * Creates a texture slab backed by a canvas. */ protected override createSlabSource(): CanvasSource { return new CanvasSource({ height: this.slabHeight, width: this.slabWidth, }); } } /** * @ignore * @public */ export declare type Contour = Array<number>; /** * The fill rules supported by {@link Path}. * * @public */ export declare enum FILL_RULE { NONZERO = "nonzero", EVENODD = "evenodd" } /** * Get information on the internal cache of the SVG loading mechanism. * * @public * @returns A view on the cache - clear() method and a size property. */ export declare function getLoaderCache(): { clear(): void; size: number; }; /** * Inherited paint, used for &lt;use /&gt; elements. The properties used on the inherited paint do not * override those on the parent. * * @public */ export declare class InheritedPaintProvider implements Paint { parent: Paint; provider: Paint; /** * Composes a `Paint` that will inherit properties from the `parent` if the `provider` does not * define them. * * @param parent * @param provider */ constructor(parent: Paint, provider: Paint); get dirtyId(): number; get fill(): number | string; get opacity(): number; get stroke(): number | string; get strokeDashArray(): number[]; get strokeDashOffset(): number; get strokeLineCap(): LineCap; get strokeLineJoin(): LineJoin; get strokeMiterLimit(): number; get strokeWidth(): number; } /** * A `MaskServer` will lazily render its content's luminance into its render-texture's alpha * channel using the luminance-alpha filter. The `dirtyId` flag can be used to make it re-render its * contents. It is intended to be used as a sprite-mask, where black pixels are invisible and white * pixels are visible (i.e. black pixels are filtered to alpha = 0, while white pixels are filtered * to alpha = 1. The rest are filtered to an alpha such that 0 < alpha < 1.). This is in compliance * with [CSS Masking Module Level 1](https://www.w3.org/TR/css-masking-1/#MaskElement). * * **Note: This functionality is disabled in PixiJS 8's renderer** * * @public * @ignore */ export declare class MaskServer extends Sprite { /** * Flags when re-renders are required due to content updates. */ dirtyId: number; /** * Flags when the content is re-rendered and should be equal to `this.dirtyId` when the texture * is update-to-date. */ updateId: number; /** * @param texture - The render-texture that will cache the contents. */ constructor(texture: RenderTexture); /** * @override */ /** * Create a mask that will overlay on top of the given display-object using the texture of this * mask server. * * @param displayObject - The mask target. */ createMask(_: Container): MaskSprite; } /** * A sprite that does not render anything. It can be used as a mask whose bounds can be updated by adding it * as a child of the mask-target. * * @public * @see MaskServer.createMask * @ignore */ export declare class MaskSprite extends Sprite { render(_: Renderer): void; } /** * Internal, parsed form of painting attributes. If a paint attribute was not defined, it **must** be * `null` (not `undefined`). * * @public * @see https://www.w3.org/TR/SVG2/painting.html#Introduction */ export declare interface Paint { /** * The interior paint for the shape. */ readonly fill: number | string; /** * The opacity of the fill. */ readonly opacity: number; /** * The color of the stroke outline applied on the shape. */ readonly stroke: number | string; /** * The dash pattern for stroking the shape. */ readonly strokeDashArray: number[]; /** * The distance into the dash pattern at which the stroking is started. */ readonly strokeDashOffset: number; /** * The line caps applied at the end of the stroke. This is not applied for closed shapes. */ readonly strokeLineCap: LineCap; /** * The line join applied at the joint to line segments. */ readonly strokeLineJoin: LineJoin; /** * The maximum miter distance. */ readonly strokeMiterLimit: number; /** * The width of the stroke outline applied on the shape. */ readonly strokeWidth: number; /** * Flags when the paint is updated. */ readonly dirtyId: number; } /** * Provides the `Paint` for an `SVGElement`. It will also respond to changes in the attributes of the element * (not implemented). * * @public */ export declare class PaintProvider implements Paint { element: SVGElement; fill: number | string; opacity: number; stroke: number | string; strokeDashArray: number[]; strokeDashOffset: number; strokeLineCap: LineCap; strokeLineJoin: LineJoin; strokeMiterLimit: number; strokeWidth: number; dirtyId: number; /** * @param element - The element whose paint is to be provided. */ constructor(element: SVGElement); /** * Parses the color attribute into an RGBA hexadecimal equivalent, if encoded. If the `colorString` is `none` or * is a `url(#id)` reference, it is returned as is. * * @param colorString * @see https://github.com/bigtimebuddy/pixi-svg/blob/89e4ab834fa4ef05b64741596516c732eae34daa/src/SVG.js#L106 */ static parseColor(colorString: string): number | string; } /** * [Paint Servers]{@link https://svgwg.org/svg-next/pservers.html} are implemented as textures. This class is a lazy * wrapper around paint textures, which can only be generated using the `renderer` drawing to the screen. * * @public */ export declare class PaintServer { paintServer: SVGGradientElement | SVGPatternElement; paintTexture: RenderTexture; paintContexts: Map<Renderer, number>; dirtyId: number; /** * Creates a `PaintServer` wrapper. * * @param paintServer * @param paintTexture */ constructor(paintServer: SVGGradientElement | SVGPatternElement, paintTexture: RenderTexture); /** * Ensures the paint texture is updated for the renderer's WebGL context. This should be called before using the * paint texture to render anything. * * @param renderer - The renderer that will use the paint texture. */ resolvePaint(renderer: Renderer): void; /** * Calculates the optimal texture dimensions for the paint texture, given the bounding box of the * object applying it. The paint texture is resized accordingly. * * If the paint texture is sized smaller than the bounding box, then it is expected that it will * be scaled up to fit it. * * @param bbox - The bounding box of the object applying the paint texture. */ resolvePaintDimensions(bbox: Rectangle): void; /** * Renders the paint texture using the renderer immediately. * * @param renderer - The renderer to use for rendering to the paint texture. */ updatePaint(renderer: Renderer): void; /** * Renders `this.paintServer` as a `SVGLinearGradientElement`. * * @param renderer - The renderer being used to render the paint texture. */ private linearGradient; /** * Renders `this.paintServer` as a `SVGRadialGradientElement`. * * @param renderer - The renderer being used to render the paint texture. */ private radialGradient; /** * Extracts the color-stops from the children of a `SVGGradientElement`. * * @param stopElements - The children of a `SVGGradientElement`. You can get it via `element.children`. * @return The color stops that can be fed into {@link GradientFactory}. */ private createColorStops; } /** @internal */ export declare const PATH = 100; /** * Shape extension for Graphics * * @public */ export declare class Path { /** * The list of contours of this path, where a contour is a list of points. * * @member {Array.Array.<number>>} */ contours: Contour[]; /** The fill rule of this path. */ fillRule: FILL_RULE; /** The type of shape. This is always equal to 100 for now. */ type: number; /** Whether the calculated bounds are dirty. */ protected dirty: boolean; /** The calculated bounds of this path. */ protected bounds: Bounds; /** * Initializes the path with zero contours and a non-zero fill rule. */ constructor(); /** * Gets the points of the last contour in this path. If there are no contours, one is created. */ get points(): number[]; /** * Calculates whether the point (x, y) is inside this path or not. * * @param x - The x-coordinate of the point. * @param y - The y-coordinate of the point. * @return Whether (x, y) is inside this path. */ contains(x: number, y: number): boolean; /** * Clone this path. */ clone(): Path; /** * Closes the last contour of this path and pushes a new one. */ closeContour(): void; /** * This should be called when the path is updated so that the hit-testing bounds are recalculated. */ invalidate(): void; toString(): string; /** * Recalculates the bounds of this path and sets {@link Path.bounds this.bounds}. */ private calculateBounds; /** * Hit-tests the point (x, y) based on the even-odd fill rule. * * @see http://geomalgorithms.com/a03-_inclusion.html */ private hitEvenOdd; /** * Hit-tests the point (x, y) based on non-zero fill rule. * * @see http://geomalgorithms.com/a03-_inclusion.html */ private hitNonZero; } /** * This node can be used to directly embed the following elements: * * | Interface | Element | * | ------------------- | ------------------ | * | SVGGElement | &lt;g /&gt; | * | SVGCircleElement | &lt;circle /&gt; | * | SVGLineElement | &lt;line /&gt; | * | SVGPolylineElement | &lt;polyline /&gt; | * | SVGPolygonElement | &lt;polygon /&gt; | * | SVGRectElement | &lt;rect /&gt; | * * It also provides an implementation for dashed stroking, by adding the `dashArray` and `dashOffset` properties * to `LineStyle`. * * @public */ export declare class SVGGraphicsNode extends Graphics { paintServers: PaintServer[]; protected _sceneContext: SVGSceneContext; constructor(context: SVGSceneContext); /** * Draws an elliptical arc. * * @param cx - The x-coordinate of the center of the ellipse. * @param cy - The y-coordinate of the center of the ellipse. * @param rx - The radius along the x-axis. * @param ry - The radius along the y-axis. * @param startAngle - The starting eccentric angle, in radians (0 is at the 3 o'clock position of the arc's circle). * @param endAngle - The ending eccentric angle, in radians. * @param xAxisRotation - The angle of the whole ellipse w.r.t. x-axis. * @param anticlockwise - Specifies whether the drawing should be counterclockwise or clockwise. * @return This Graphics object. Good for chaining method calls. */ ellipticArc(cx: number, cy: number, rx: number, ry: number, startAngle: number, endAngle: number, xAxisRotation?: number, anticlockwise?: boolean): this; /** * Draws an elliptical arc to the specified point. * * If rx = 0 or ry = 0, then a line is drawn. If the radii provided are too small to draw the arc, then * they are scaled up appropriately. * * @param endX - the x-coordinate of the ending point. * @param endY - the y-coordinate of the ending point. * @param rx - The radius along the x-axis. * @param ry - The radius along the y-axis. * @param xAxisRotation - The angle of the ellipse as a whole w.r.t/ x-axis. * @param anticlockwise - Specifies whether the arc should be drawn counterclockwise or clockwise. * @param largeArc - Specifies whether the larger arc of two possible should be choosen. * @return This Graphics object. Good for chaining method calls. * @see https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands * @see https://www.w3.org/TR/SVG2/implnote.html#ArcImplementationNotes */ ellipticArcTo(endX: number, endY: number, rx: number, ry: number, xAxisRotation?: number, anticlockwise?: boolean, largeArc?: boolean): this; /** * Embeds the `SVGCircleElement` into this node. * * @param element - The circle element to draw. */ embedCircle(element: SVGCircleElement): void; /** * Embeds the `SVGEllipseElement` into this node. * * @param element - The ellipse element to draw. */ embedEllipse(element: SVGEllipseElement): void; /** * Embeds the `SVGLineElement` into this node. * * @param element - The line element to draw. */ embedLine(element: SVGLineElement): void; /** * Embeds the `SVGRectElement` into this node. * * @param element - The rectangle element to draw. */ embedRect(element: SVGRectElement): void; /** * Embeds the `SVGPolygonElement` element into this node. * * @param element - The polygon element to draw. */ embedPolygon(element: SVGPolygonElement): void; /** * Embeds the `SVGPolylineElement` element into this node. * * @param element - The polyline element to draw. */ embedPolyline(element: SVGPolylineElement): void; /** * @override */ render(renderer: Renderer): void; } /** * Draws SVG &lt;image /&gt; elements. * * @public */ export declare class SVGImageNode extends SVGGraphicsNode { /** * The canvas used into which the `SVGImageElement` is drawn. This is because WebGL does not support * using `SVGImageElement` as an `ImageSource` for textures. */ protected _canvas: HTMLCanvasElement; /** * The Canvas 2D context for `this._canvas`. */ protected _canvasContext: CanvasRenderingContext2D; /** * A texture backed by `this._canvas`. */ protected _texture: Texture; /** * Embeds the given SVG image element into this node. * * @param element - The SVG image element to embed. */ embedImage(element: SVGImageElement): void; /** * Initializes {@code this._texture} by allocating it from the atlas. It is expected the texture size requested * is less than the atlas's slab dimensions. * * @param width * @param height */ private initTexture; /** * Draws the image into this node's texture. * * @param image - The image element holding the image. */ private drawTexture; } /** * Draws SVG &lt;path /&gt; elements. * * @public */ export declare class SVGPathNode extends SVGGraphicsNode { /** * Embeds the `SVGPathElement` into this node. * * @param element - the path to draw */ embedPath(element: SVGPathElement): this; } /** * {@link SVGScene} can be used to build an interactive viewer for scalable vector graphics images. You must specify the size * of the svg viewer. * * ## SVG Scene Graph * * SVGScene has an internal, disconnected scene graph that is optimized for lazy updates. It will listen to the following * events fired by a node: * * * `nodetransformdirty`: This will invalidate the transform calculations. * * @public */ export declare class SVGScene extends Container { /** * The SVG image content being rendered by the scene. */ content: SVGSVGElement; /** * Display objects that don't render to the screen, but are required to update before the rendering * nodes, e.g. mask sprites. */ renderServers: Container; /** * The scene context */ protected _context: SVGSceneContext; /** * The width of the rendered scene in local space. */ protected _width: number; /** * The height of the rendered scene in local space. */ protected _height: number; /** * Maps content elements to their paint. These paints do not inherit from their parent element. You must * compose an {@link InheritedPaintProvider} manually. */ private _elementToPaint; /** * Maps `SVGMaskElement` elements to their nodes. These are not added to the scene graph directly and are * only referenced as a `mask`. */ private _elementToMask; private _elementToRenderNode; /** * Flags whether any transform is dirty in the SVG scene graph. */ protected _transformDirty: boolean; sortDirty: boolean; /** * @param content - The SVG node to render * @param context - This can be used to configure the scene */ constructor(content: SVGSVGElement, context?: Partial<SVGSceneContext>); initContext(context?: Partial<SVGSceneContext>): void; /** * Draw the paints required to render the elements in this SVG scene. If not called, gradients * and special effects may not render. * * @param renderer */ drawPaints(renderer: Renderer): void; /** * Creates a display object that implements the corresponding `embed*` method for the given node. * * @param element - The element to be embedded in a display object. */ protected createNode(element: SVGElement): Container; /** * Creates a `Paint` object for the given element. This should only be used when sharing the `Paint` * is not desired; otherwise, use {@link SVGScene.queryPaint}. * * This will return `null` if the passed element is not an instance of `SVGElement`. * * @alpha * @param element */ protected createPaint(element: SVGElement): Paint; /** * Creates a lazy paint texture for the paint server. * * @alpha * @param paintServer - The paint server to be rendered. */ protected createPaintServer(paintServer: SVGGradientElement): PaintServer; /** * Creates a lazy luminance mask for the `SVGMaskElement` or its rendering node. * * @param ref - The `SVGMaskElement` or its rendering node, if already generated. */ protected createMask(ref: SVGMaskElement | Container): MaskServer; /** * Returns the rendering node for a mask. * * @alpha * @param ref - The mask element whose rendering node is needed. */ protected queryMask(ref: SVGMaskElement): MaskServer; /** * Returns the cached paint of a content element. The returned paint will not account for any paint * attributes inherited from ancestor elements. * * @alpha * @param ref - A reference to the content element. */ protected queryPaint(ref: SVGElement): Paint; /** * Returns an (uncached) inherited paint of a content element. * * @alpha * @param ref */ protected queryInheritedPaint(ref: SVGElement): Paint; /** * Parses the internal URL reference into a selector (that can be used to find the * referenced element using `this.content.querySelector`). * * @param url - The reference string, e.g. "url(#id)", "url('#id')", "#id" */ protected parseReference(url: string): string; /** * Embeds a content `element` into the rendering `node`. * * This is **not** a stable API. * * @alpha * @param node - The node in this scene that will render the `element`. * @param element - The content `element` to be rendered. This must be an element of the SVG document * fragment under `this.content`. * @param options - Additional options * @param {Paint} [options.basePaint] - The base paint that the element's paint should inherit from * @return The base attributes of the element, like paint. */ protected embedIntoNode(node: Container, element: SVGGraphicsElement | SVGMaskElement, options?: { basePaint?: Paint; }): { paint: Paint; }; /** * Recursively populates a subscene graph that embeds {@code element}. The root of the subscene is returned. * * @param element - The SVGElement to be embedded. * @param options - Inherited attributes from the element's parent, if any. * @return The display object that embeds the element for rendering. */ protected populateSceneRecursive(element: SVGElement, options?: { basePaint?: Paint; }): Container; /** * Populates the entire SVG scene. This should only be called once after the {@link SVGScene.content} has been set. */ protected populateScene(): void; /** * Handles `nodetransformdirty` events fired by nodes. It will set {@link SVGScene._transformDirty} to true. * * This will also emit `transformdirty`. */ private onNodeTransformDirty; /** * The width at which the SVG scene is being rendered. By default, this is the viewbox width specified by * the root element. */ get width(): number; set width(value: number); /** * The height at which the SVG scene is being rendered. By default, this is the viewbox height specified by * the root element. */ get height(): number; set height(value: number); /** * Load the SVG document and create a {@link SVGScene} asynchronously. * * A cache is used for loaded SVG documents. * * @param url * @param context * @returns */ static from(url: string, context?: SVGSceneContext): Promise<SVGScene>; } /** * Options to manage the SVG scene * * @public */ export declare interface SVGSceneContext { /** The texture allocator for loading images. */ atlas: CanvasTextureAllocator; /** Disable loading SVGs referenced from "href", "xlink:href" attributes of &lt;use /&gt; elements. */ disableHrefSVGLoading: boolean; /** @ignore */ disableRootPopulation: boolean; } /** * The `SVGTextEngine` interface is used to layout text content authored in SVG files. The @pixi-essentials/svg * package provides {@link SVGTextEngineImpl} as a default implementation for users. * * Text engines are allowed to have async behaviour so that fonts can be loaded before text metrics are measured. * * It is expected an implementation inherits from {@link PIXI.DisplayObject}. * * @public * @see SVGTextEngineImpl */ export declare interface SVGTextEngine { /** * Clears the text content laid out already. This should reset the state of the engine to before any calls * to {@link SVGTextEngine.put} were made. */ clear(): Promise<void>; /** * Puts the text {@code content} into the local space of the engine at {@code position}. {@code matrix} can * be used to transform the glyphs, although it is as optional feature for implementations. * * @param id - A locally unique ID that can be used to modify the added block of text later. * @param position - The position of the text in the engine's local space. * @param content - The text to add. * @param style - The text styling applied. * @param matrix */ put(id: any, position: PointData, content: string, style: Partial<TextStyle>, matrix?: Matrix): Promise<PointData>; } /** * `SVGTextEngineImpl` is the default implementation for {@link SVGTextEngine}. It is inspired by {@link PIXI.Text} that * is provided by @pixi/text. It uses a &lt;canvas /&gt; to draw and cache the text. This may cause blurring issues when * the SVG is viewed at highly zoomed-in scales because it is rasterized. * * @public */ export declare class SVGTextEngineImpl extends Sprite implements SVGTextEngine { protected canvas: HTMLCanvasElement; protected context: CanvasRenderingContext2D; protected contentList: Map<any, { position: PointData; content: string; style: Partial<TextStyle>; matrix?: Matrix; }>; protected dirtyId: number; protected updateId: number; constructor(); clear(): Promise<void>; put(id: any, position: PointData, content: string, style: Partial<TextStyle>, matrix?: Matrix): Promise<PointData>; updateText(): void; onRender: () => void; } /** * Draws SVG &lt;text /&gt; elements. * * @public */ export declare class SVGTextNode extends Container { /** * The SVG text rendering engine to be used by default in `SVGTextNode`. This API is not stable and * can change anytime. * * @alpha */ static defaultEngine: { new (): SVGTextEngine & Container; }; /** * An instance of a SVG text engine used to layout and render text. */ protected engine: SVGTextEngine & Container; /** * The current text position, where the next glyph will be placed. */ protected currentTextPosition: PointData; constructor(); /** * Embeds a `SVGTextElement` in this node. * * @param {SVGTextElement} element - The `SVGTextElement` to embed. */ embedText(element: SVGTextElement | SVGTSpanElement, style?: Partial<TextStyle>): Promise<void>; } /** * Container for rendering SVG &lt;use /&gt; elements. * * @public */ export declare class SVGUseNode extends Container { isRefExternal: boolean; private _ref; /** * Embeds the `SVGUseElement` into this node. * * @param element - The &lt;use /&gt; element to draw. */ embedUse(element: SVGUseElement): void; /** * The node that renders the element referenced by a &lt;element /&gt; element. */ get ref(): SVGGraphicsNode; set ref(value: SVGGraphicsNode); } /** * The texture allocator dynamically manages space on base-texture slabs. It can be used to generate * atlases on demand, which improve batching efficiency. * * @public */ declare abstract class TextureAllocator<S extends TextureSource, T extends Texture = Texture> { /** * The width of texture slabs. */ public readonly slabWidth: number; /** * The height of texture slabs. */ public readonly slabHeight: number; /** * The list of base-textures that are used to allocate texture space. */ protected textureSlabs: TextureSlab<S>[]; /** * @param slabWidth - The width of base-texture slabs. This should be at most 2048. * @param slabHeight - The height of base-texture slabs. This should be at most 2048. */ constructor(slabWidth = 2048, slabHeight = 2048) { this.slabWidth = slabWidth; this.slabHeight = slabHeight; this.textureSlabs = []; } get maxWidth(): number { return this.slabWidth - (2 * this.calculatePadding(this.slabWidth, this.slabHeight)); } get maxHeight(): number { return this.slabHeight - (2 * this.calculatePadding(this.slabWidth, this.slabHeight)); } /** * Allocates a texture from this allocator. * * If its existing slab pool has enough space, the texture is issued from one. Otherwise, * a new slab is created and the texture is issued from it. However, if the requested * dimensions are larger than slabs themselves, then `null` is always returned. * * To upload a texture source, you will have to create an atlas-managing {@link TextureSource} * yourself on the base-texture. The {@link AtlasAllocator} does this for you, while the * {@link CanvasTextureAllocator} can be used to draw on a canvas-based atlas. * * @param width - The width of the requested texture. * @param height - The height of the requested texture. * @param padding - The padding requested around the texture, to prevent bleeding. * @return The allocated texture, if successful; otherwise, `null`. */ allocate(width: number, height: number, padding = this.calculatePadding(width, height)): T { // Cannot allocate a texture larger than a texture-slab. if (padded(width, padding) > this.slabWidth || padded(height, padding) > this.slabHeight) { return null; } const slabs = this.textureSlabs; // Loop through the slabs and find one with enough space, if any. for (let i = 0, j = slabs.length; i < j; i++) { const slab = slabs[i]; const texture = this.issueTexture(slab, width, height, padding); if (texture) { return texture; } } // Issue a new slab. const slab = this.createSlab(); // Append this slab to the head of the list. this.textureSlabs.unshift(slab); // Issue the texture from this blank slab. return this.issueTexture(slab, width, height, padding); } /** * Frees the texture and reclaims its space. It is assumed you will not use it again, and have * destroyed any resource uploading its data. * * @param texture * @throws When the texture was not located in this allocator. */ free(texture: T): void { const slab = this.textureSlabs.find((sl) => sl.slab === texture.source); if (!slab) { throw new Error('The texture cannot be freed because ' + 'its base-texture is not pooled by this allocator. ' + 'This is either a bug in TextureAllocator or you tried to free a ' + 'texture that was never allocated by one.'); } const textureEntry = slab.managedTextures.find((entry) => entry.texture === texture); if (!textureEntry) { throw new Error('The texture cannot be freed because it was not found ' + 'in the managed list of issued textures on its slab. This may be because you ' + 'duplicated this texture or a bug in TextureAllocator'); } slab.managedArea.free(textureEntry.area); slab.managedTextures.splice(slab.managedTextures.indexOf(textureEntry), 1); } protected calculatePadding(width: number, height: number): number { const dimen = Math.max(width, height); if (dimen < 64) { return 2; } else if (dimen < 128) { return 4; } else if (dimen < 1024) { return 8; } return 16; } /** * Creates a texture slab. Uses {@link this.createSlabSource} to initialize the texture data. */ protected createSlab(): TextureSlab<S> { return { managedArea: new GuilloteneAllocator(this.slabWidth, this.slabHeight), managedTextures: [], slab: this.createSlabSource(), }; } /** * Creates a new texture source to initialize a texture slab. */ protected abstract createSlabSource(): S; /** * Creates a texture on the given base-texture at {@code frame}. * * @param source - The atlas source that will hold the texture's space. * @param frame - The frame in which the texture will be stored. */ protected createTexture(source: S, frame: Rectangle): T { // Override this method to return correct texture type T. return new Texture({ source, frame }) as unknown as T; } /** * Issues a texture from the given texture slab, if possible. * * @param slab - The texture slab to allocate frame. * @param width - The width of the requested texture. * @param height - The height of the requested texture. * @param padding - Padding required around the texture. * @return The issued texture, if successful; otherwise, `null`. */ protected issueTexture(slab: TextureSlab<S>, width: number, height: number, padding = 0): T { const area = slab.managedArea.allocate(width + (2 * padding), height + (2 * padding)); if (!area) { return null; } tempRect.copyFrom(area); tempRect.pad(-padding); const issuedTexture = this.createTexture(slab.slab, tempRect.clone()); slab.managedTextures.push({ area, texture: issuedTexture, }); return issuedTexture; } } /** * An entry of an issued texture from a {@link TextureSlab}. * * @public */ declare type TextureEntry = { /** * The area returned by the area allocator, with the `__mem_area` key. */ area: Rectangle; /** * The issued texture. */ texture: Texture; }; /** * A texture slab holds a managed base-texture that is used to issue allocated texture space. The * texture allocator maintains a pool of these texture slabs. * * @public */ declare type TextureSlab<T extends TextureSource> = { /** * The area allocator that issues texture space. */ managedArea: AreaAllocator<any>; /** * The list of allocated textures and their area. */ managedTextures: TextureEntry[]; /** * The base-texture that holds all the issued textures. */ slab: T; }; export { }