import { Bounds } from 'vega-scenegraph'; import type { GPUVegaCanvasContext } from '../types/context.js'; import type { SceneTextItem } from '../types/scene.js'; /** Size of one rasterized label and where its anchor sits inside it. */ export interface GlyphMetrics { /** Physical (device-pixel) size of the rasterization. */ physWidth: number; physHeight: number; /** Anchor position inside the rasterization, in device pixels (fractional). */ anchorTexX: number; anchorTexY: number; } /** The point text is positioned around (x/y, offset by radius/theta). */ export declare function textAnchor(item: SceneTextItem): [number, number]; /** * Cache key over everything that affects the rasterized pixels (not opacity, * which the shader applies). `radius`/`theta` are not included, because they * only move the anchor in scene space and cancel out of the anchor-relative * offset. `angle` is, and is zero for a glyph the quad will turn instead. * * A label is rasterized by vega's own canvas text mark, so everything its * stroke helper sets belongs here: the dash, its offset, the cap, the join and * the miter limit all change the glyph pixels, and the join and the limit * change the cell size `strokeReach` asks for as well. */ export declare function textCacheKey(item: SceneTextItem): string; /** * Size of `raster`'s rasterization and where its anchor sits inside it. The * anchor offset is picked so that turning the quad about the anchor puts the * glyph's top-left corner on a whole device pixel, which for a quarter turn * maps every texel onto exactly one pixel, and for no turn at all reproduces * what the canvas renderer rasterizes. */ export declare function glyphMetrics(ctx: GPUVegaCanvasContext, raster: SceneTextItem, vb: Bounds, turn: Turn): GlyphMetrics | null; /** * How far canvas's own matrix has drifted where this label draws, in device * pixels. Zero unless `canvasTextDrift` is on: see util/canvasDrift.ts. */ export type Drift = readonly [dx: number, dy: number]; /** * Whole device pixels the canvas renderer would put this label away from where * its own coordinates say, which is 0 unless its baseline sits on a half pixel. * * Two things move it, and both are canvas arithmetic rather than geometry. Its * matrix has drifted (see util/canvasDrift.ts), and it is a float32 matrix, so * a baseline our double puts at 157.49999999999994 is exactly 157.5 to it and * rounds the other way. Both are reproduced by computing the baseline the way * canvas does and comparing which whole pixel each lands on. * * The shift is kept apart from the sub-pixel phase on purpose. It is the only * thing that can move a label, and folding the difference into the phase * instead re-rasterizes labels it cannot move: a re-rasterization at a * hair-different phase can still flip a hinted stem, and every axis label in * `scatter-brush-panzoom` went to worst channel 255 that way. Rotated labels * are left exact, since the snap this crosses is the upright one. */ export declare function driftShift(ctx: GPUVegaCanvasContext, item: SceneTextItem, vb: Bounds, turn: Turn, translation: number | undefined): Drift; /** Cosine and sine of a label's angle. */ export type Turn = readonly [cos: number, sin: number]; export declare const NO_TURN: Turn; /** Cosine and sine of an item's angle, which vega stores in degrees. */ export declare function turnOf(item: SceneTextItem): Turn; /** * The item with its rotation dropped, for a quad that will turn instead. * * Rasterizing the rotation in matches canvas at any angle, but every rotated * label a frame moves then has to be rasterized and read back out of a 2D * canvas, about 0.15 ms each: a radial tree spent 32 ms a frame there. */ export declare function upright(item: SceneTextItem): SceneTextItem; /** * Draws the label with vega-scenegraph's own canvas text mark, so the pixels * match the canvas renderer exactly, with its top-left at (originX, originY). */ export declare function drawGlyph(c2d: CanvasRenderingContext2D, dpi: number, raster: SceneTextItem, m: GlyphMetrics, originX: number, originY: number): void; /** * A label too large for the atlas, rasterized into a texture of its own. * * Premultiplied, for the same reason as the image mark: converting a glyph's * antialiased edge to straight alpha turns its transparent side black and * filtering then darkens the edge. The shader divides the alpha back out. */ export declare function rasterizeText(device: GPUDevice, canvas: HTMLCanvasElement, c2d: CanvasRenderingContext2D, dpi: number, raster: SceneTextItem, m: GlyphMetrics): GPUTexture;