import type { GlyphMetrics } from './textTexture.js'; /** Placement of one rasterized label inside the atlas, in device pixels. */ export interface GlyphSlot extends GlyphMetrics { x: number; y: number; } /** * Packs the labels of a frame into one canvas and uploads them in a single * copy. `copyExternalImageToTexture` costs about 1.2 ms whenever its source * canvas changed since the last copy, regardless of the region size, so a * texture per label made a rotating radial tree spend 250 ms a frame in the * driver. * * Slots survive across frames. Growing or clearing mints a new texture, and the * one it replaces stays alive until the frame is submitted, so only the batch * currently being packed (which shares one bind group) has to be protected. */ export declare class TextAtlas { private readonly _device; private readonly _release; private readonly _max; private readonly _canvas; private readonly _c2d; private readonly _slots; private _texture; private _size; private _shelfY; private _shelfHeight; private _cursorX; private _dirty; private _spilled; private _batchArea; private _lastBatchArea; /** `release` takes a texture the atlas replaced, which a queued draw may still hold. */ constructor(_device: GPUDevice, _release: (texture: GPUTexture) => void); get context(): CanvasRenderingContext2D; get texture(): GPUTexture; get size(): number; /** * Starts a batch of lookups that will share one bind group, growing or * clearing the atlas first if the last batch of this size would not fit in * what is left. Nothing else replaces the texture, so a batch always packs * into the one it read. */ begin(): void; /** Slot for a label already packed. */ find(key: string): GlyphSlot | undefined; /** * Reserves space for a label. Returns null when it does not fit, and the * caller falls back to a texture of its own. */ alloc(key: string, metrics: GlyphMetrics): GlyphSlot | null; /** Uploads whatever was drawn since the last flush. */ flush(): void; private _place; private _reset; private _markDirty; }