@expofp/renderer 0.0.0-experimental.2d6c681
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/dist/Renderer.d.ts +102 -0
- package/dist/Renderer.js +96 -0
- package/dist/controls/CameraControls.d.ts +108 -0
- package/dist/controls/CameraControls.js +203 -0
- package/dist/controls/Controls.d.ts +53 -0
- package/dist/controls/Controls.js +54 -0
- package/dist/controls/camera-api.d.ts +226 -0
- package/dist/controls/camera-api.js +179 -0
- package/dist/controls/gestures.d.ts +71 -0
- package/dist/controls/gestures.js +98 -0
- package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
- package/dist/controls/handlers/DoubleTapZoom.js +82 -0
- package/dist/controls/handlers/Inertia.d.ts +60 -0
- package/dist/controls/handlers/Inertia.js +222 -0
- package/dist/controls/handlers/Pan.d.ts +23 -0
- package/dist/controls/handlers/Pan.js +212 -0
- package/dist/controls/handlers/Pitch.d.ts +19 -0
- package/dist/controls/handlers/Pitch.js +200 -0
- package/dist/controls/handlers/Roll.d.ts +30 -0
- package/dist/controls/handlers/Roll.js +196 -0
- package/dist/controls/handlers/Zoom.d.ts +28 -0
- package/dist/controls/handlers/Zoom.js +34 -0
- package/dist/controls/hooks.d.ts +136 -0
- package/dist/controls/hooks.js +200 -0
- package/dist/controls/index.d.ts +9 -0
- package/dist/controls/index.js +7 -0
- package/dist/controls/zoom-to.d.ts +89 -0
- package/dist/controls/zoom-to.js +91 -0
- package/dist/core/InstanceUniforms.d.ts +108 -0
- package/dist/core/InstanceUniforms.js +298 -0
- package/dist/core/SquareDataTexture.d.ts +277 -0
- package/dist/core/SquareDataTexture.js +476 -0
- package/dist/core/batch/BatchedMesh.d.ts +74 -0
- package/dist/core/batch/BatchedMesh.js +155 -0
- package/dist/core/instanced/FrustumCulling.d.ts +141 -0
- package/dist/core/instanced/FrustumCulling.js +246 -0
- package/dist/core/instanced/InstancedMesh.d.ts +302 -0
- package/dist/core/instanced/InstancedMesh.js +497 -0
- package/dist/core/instanced/Instances.d.ts +119 -0
- package/dist/core/instanced/Instances.js +230 -0
- package/dist/core/instanced/Raycasting.d.ts +118 -0
- package/dist/core/instanced/Raycasting.js +134 -0
- package/dist/core/instanced/instanced-material.d.ts +77 -0
- package/dist/core/instanced/instanced-material.js +228 -0
- package/dist/core/lines/BatchedLines.d.ts +137 -0
- package/dist/core/lines/BatchedLines.js +215 -0
- package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
- package/dist/core/lines/FrustumCullingLines.js +85 -0
- package/dist/core/lines/RaycastingLines.d.ts +45 -0
- package/dist/core/lines/RaycastingLines.js +91 -0
- package/dist/core/lines/lines-material.d.ts +47 -0
- package/dist/core/lines/lines-material.js +124 -0
- package/dist/core/shader-source.d.ts +70 -0
- package/dist/core/shader-source.js +110 -0
- package/dist/core/sprites/BatchedSprites.d.ts +115 -0
- package/dist/core/sprites/BatchedSprites.js +173 -0
- package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
- package/dist/core/sprites/RaycastingSprites.js +71 -0
- package/dist/core/sprites/sprite-material.d.ts +59 -0
- package/dist/core/sprites/sprite-material.js +192 -0
- package/dist/core/text/BatchedText.d.ts +233 -0
- package/dist/core/text/BatchedText.js +473 -0
- package/dist/core/text/MemberTexture.d.ts +90 -0
- package/dist/core/text/MemberTexture.js +289 -0
- package/dist/core/text/Text.d.ts +88 -0
- package/dist/core/text/Text.js +230 -0
- package/dist/core/text/batched-text-material.d.ts +56 -0
- package/dist/core/text/batched-text-material.js +123 -0
- package/dist/core/text/glyph-packing.d.ts +17 -0
- package/dist/core/text/glyph-packing.js +61 -0
- package/dist/core/typed-array.d.ts +16 -0
- package/dist/core/typed-array.js +20 -0
- package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
- package/dist/debug/components/DebugPanelProvider.js +50 -0
- package/dist/debug/components/DebugTools.d.ts +22 -0
- package/dist/debug/components/DebugTools.js +28 -0
- package/dist/debug/components/Gui.d.ts +22 -0
- package/dist/debug/components/Gui.js +42 -0
- package/dist/debug/components/MemoryInfo.d.ts +22 -0
- package/dist/debug/components/MemoryInfo.js +42 -0
- package/dist/debug/components/Panel.d.ts +31 -0
- package/dist/debug/components/Panel.js +104 -0
- package/dist/debug/components/Stats.d.ts +9 -0
- package/dist/debug/components/Stats.js +24 -0
- package/dist/debug/debug-chunk.d.ts +26 -0
- package/dist/debug/debug-chunk.js +32 -0
- package/dist/debug/debug-context.d.ts +41 -0
- package/dist/debug/debug-context.js +59 -0
- package/dist/debug/index.d.ts +41 -0
- package/dist/debug/index.js +43 -0
- package/dist/events/LayoutDriver.d.ts +36 -0
- package/dist/events/LayoutDriver.js +192 -0
- package/dist/events/PointerMissed.d.ts +23 -0
- package/dist/events/PointerMissed.js +58 -0
- package/dist/events/def-culling.d.ts +46 -0
- package/dist/events/def-culling.js +69 -0
- package/dist/events/layout-targets.d.ts +151 -0
- package/dist/events/layout-targets.js +397 -0
- package/dist/events/placement/box.d.ts +59 -0
- package/dist/events/placement/box.js +96 -0
- package/dist/events/placement/grid.d.ts +161 -0
- package/dist/events/placement/grid.js +235 -0
- package/dist/events/placement/index.d.ts +2 -0
- package/dist/events/placement/index.js +1 -0
- package/dist/events/placement/pass.d.ts +70 -0
- package/dist/events/placement/pass.js +617 -0
- package/dist/events/pointer.d.ts +194 -0
- package/dist/events/pointer.js +356 -0
- package/dist/external/ExternalCanvas.d.ts +56 -0
- package/dist/external/ExternalCanvas.js +177 -0
- package/dist/external/external-camera.d.ts +18 -0
- package/dist/external/external-camera.js +103 -0
- package/dist/external/external-events.d.ts +14 -0
- package/dist/external/external-events.js +33 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/objects/Background.d.ts +31 -0
- package/dist/objects/Background.js +37 -0
- package/dist/objects/containers/SlotContainer.d.ts +312 -0
- package/dist/objects/containers/SlotContainer.js +311 -0
- package/dist/objects/containers/hooks.d.ts +61 -0
- package/dist/objects/containers/hooks.js +196 -0
- package/dist/objects/containers/index.d.ts +6 -0
- package/dist/objects/containers/index.js +4 -0
- package/dist/objects/containers/keys.d.ts +10 -0
- package/dist/objects/containers/keys.js +18 -0
- package/dist/objects/containers/mesh-writes.d.ts +106 -0
- package/dist/objects/containers/mesh-writes.js +97 -0
- package/dist/objects/containers/props.d.ts +33 -0
- package/dist/objects/containers/props.js +1 -0
- package/dist/objects/images/ImageInstance.d.ts +29 -0
- package/dist/objects/images/ImageInstance.js +152 -0
- package/dist/objects/images/Images.d.ts +49 -0
- package/dist/objects/images/Images.js +394 -0
- package/dist/objects/images/atlas-packer.d.ts +129 -0
- package/dist/objects/images/atlas-packer.js +287 -0
- package/dist/objects/images/image-container.d.ts +74 -0
- package/dist/objects/images/image-container.js +100 -0
- package/dist/objects/images/index.d.ts +2 -0
- package/dist/objects/images/index.js +1 -0
- package/dist/objects/images/pack-sources.d.ts +190 -0
- package/dist/objects/images/pack-sources.js +272 -0
- package/dist/objects/images/texture-budget.d.ts +120 -0
- package/dist/objects/images/texture-budget.js +215 -0
- package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
- package/dist/objects/images/worker/atlas-worker.js +123 -0
- package/dist/objects/images/worker/client.d.ts +62 -0
- package/dist/objects/images/worker/client.js +249 -0
- package/dist/objects/images/worker/index.d.ts +10 -0
- package/dist/objects/images/worker/index.js +1 -0
- package/dist/objects/layer/Layer.d.ts +28 -0
- package/dist/objects/layer/Layer.js +134 -0
- package/dist/objects/layer/index.d.ts +2 -0
- package/dist/objects/layer/index.js +1 -0
- package/dist/objects/layer/layer-context.d.ts +74 -0
- package/dist/objects/layer/layer-context.js +82 -0
- package/dist/objects/layer/mount-scheduler.d.ts +131 -0
- package/dist/objects/layer/mount-scheduler.js +289 -0
- package/dist/objects/layer/visibility-gui.d.ts +19 -0
- package/dist/objects/layer/visibility-gui.js +93 -0
- package/dist/objects/lighting/Lighting.d.ts +92 -0
- package/dist/objects/lighting/Lighting.js +384 -0
- package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
- package/dist/objects/lighting/OcclusionPasses.js +271 -0
- package/dist/objects/lighting/index.d.ts +11 -0
- package/dist/objects/lighting/index.js +10 -0
- package/dist/objects/lighting/lighting-gui.d.ts +24 -0
- package/dist/objects/lighting/lighting-gui.js +114 -0
- package/dist/objects/lighting/occluder.d.ts +34 -0
- package/dist/objects/lighting/occluder.js +62 -0
- package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
- package/dist/objects/lighting/occlusion-targets.js +276 -0
- package/dist/objects/lines/LineInstance.d.ts +21 -0
- package/dist/objects/lines/LineInstance.js +73 -0
- package/dist/objects/lines/Lines.d.ts +17 -0
- package/dist/objects/lines/Lines.js +31 -0
- package/dist/objects/lines/index.d.ts +2 -0
- package/dist/objects/lines/index.js +1 -0
- package/dist/objects/lines/line-container.d.ts +35 -0
- package/dist/objects/lines/line-container.js +40 -0
- package/dist/objects/materials/ColorMaterial.d.ts +92 -0
- package/dist/objects/materials/ColorMaterial.js +124 -0
- package/dist/objects/materials/LineMaterial.d.ts +44 -0
- package/dist/objects/materials/LineMaterial.js +74 -0
- package/dist/objects/materials/ModelMaterial.d.ts +53 -0
- package/dist/objects/materials/ModelMaterial.js +111 -0
- package/dist/objects/materials/TextureMaterial.d.ts +33 -0
- package/dist/objects/materials/TextureMaterial.js +48 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
- package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
- package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
- package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
- package/dist/objects/materials/effects/ao/index.d.ts +129 -0
- package/dist/objects/materials/effects/ao/index.js +72 -0
- package/dist/objects/materials/effects/color-dim.d.ts +115 -0
- package/dist/objects/materials/effects/color-dim.js +206 -0
- package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
- package/dist/objects/materials/effects/contact-shadow.js +167 -0
- package/dist/objects/materials/effects/effects-context.d.ts +53 -0
- package/dist/objects/materials/effects/effects-context.js +24 -0
- package/dist/objects/materials/shared.d.ts +24 -0
- package/dist/objects/materials/shared.js +72 -0
- package/dist/objects/models/ModelInstance.d.ts +22 -0
- package/dist/objects/models/ModelInstance.js +33 -0
- package/dist/objects/models/Models.d.ts +22 -0
- package/dist/objects/models/Models.js +186 -0
- package/dist/objects/models/gltf-cache.d.ts +17 -0
- package/dist/objects/models/gltf-cache.js +50 -0
- package/dist/objects/models/index.d.ts +2 -0
- package/dist/objects/models/index.js +1 -0
- package/dist/objects/models/model-asset.d.ts +48 -0
- package/dist/objects/models/model-asset.js +77 -0
- package/dist/objects/models/model-container.d.ts +123 -0
- package/dist/objects/models/model-container.js +207 -0
- package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
- package/dist/objects/shapes/ShapeInstance.js +39 -0
- package/dist/objects/shapes/Shapes.d.ts +21 -0
- package/dist/objects/shapes/Shapes.js +131 -0
- package/dist/objects/shapes/index.d.ts +2 -0
- package/dist/objects/shapes/index.js +1 -0
- package/dist/objects/shapes/shape-container.d.ts +141 -0
- package/dist/objects/shapes/shape-container.js +242 -0
- package/dist/objects/shapes/shape-geometry.d.ts +27 -0
- package/dist/objects/shapes/shape-geometry.js +94 -0
- package/dist/objects/text/TextInstance.d.ts +22 -0
- package/dist/objects/text/TextInstance.js +95 -0
- package/dist/objects/text/Texts.d.ts +30 -0
- package/dist/objects/text/Texts.js +43 -0
- package/dist/objects/text/index.d.ts +2 -0
- package/dist/objects/text/index.js +1 -0
- package/dist/objects/text/text-container.d.ts +121 -0
- package/dist/objects/text/text-container.js +229 -0
- package/dist/objects/text/text-layout.d.ts +92 -0
- package/dist/objects/text/text-layout.js +180 -0
- package/dist/space/Camera.d.ts +103 -0
- package/dist/space/Camera.js +321 -0
- package/dist/space/Scene.d.ts +47 -0
- package/dist/space/Scene.js +127 -0
- package/dist/space/Space.d.ts +325 -0
- package/dist/space/Space.js +359 -0
- package/dist/space/VisibleRect.d.ts +45 -0
- package/dist/space/VisibleRect.js +129 -0
- package/dist/space/camera-handle.d.ts +67 -0
- package/dist/space/camera-handle.js +63 -0
- package/dist/space/camera-kind.d.ts +26 -0
- package/dist/space/camera-kind.js +30 -0
- package/dist/space/canvas-size.d.ts +24 -0
- package/dist/space/canvas-size.js +20 -0
- package/dist/space/clipping.d.ts +69 -0
- package/dist/space/clipping.js +130 -0
- package/dist/space/layout-measure.d.ts +67 -0
- package/dist/space/layout-measure.js +175 -0
- package/dist/space/viewport.d.ts +112 -0
- package/dist/space/viewport.js +67 -0
- package/dist/types/defs.d.ts +234 -0
- package/dist/types/defs.js +1 -0
- package/dist/types/events.d.ts +198 -0
- package/dist/types/events.js +1 -0
- package/dist/types/guards.d.ts +21 -0
- package/dist/types/guards.js +52 -0
- package/dist/update/index.d.ts +3 -0
- package/dist/update/index.js +2 -0
- package/dist/update/slot-writers.d.ts +30 -0
- package/dist/update/slot-writers.js +76 -0
- package/dist/update/store.d.ts +41 -0
- package/dist/update/store.js +104 -0
- package/dist/utils/arrays.d.ts +13 -0
- package/dist/utils/arrays.js +20 -0
- package/dist/utils/colors.d.ts +19 -0
- package/dist/utils/colors.js +27 -0
- package/dist/utils/hot-state.d.ts +41 -0
- package/dist/utils/hot-state.js +37 -0
- package/dist/utils/logging.d.ts +29 -0
- package/dist/utils/logging.js +55 -0
- package/dist/utils/math.d.ts +52 -0
- package/dist/utils/math.js +76 -0
- package/package.json +79 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A source that the packer can draw from without decoding it further. It is already decoded, and
|
|
3
|
+
* it reports its own pixel size. A `Blob` is the one image source that meets neither condition, so
|
|
4
|
+
* code decodes each `Blob` into one of these types before it reaches this module.
|
|
5
|
+
*/
|
|
6
|
+
export type DecodedSource = ImageBitmap | HTMLCanvasElement | OffscreenCanvas;
|
|
7
|
+
/**
|
|
8
|
+
* A canvas that one atlas is drawn onto. This type includes `OffscreenCanvas`, so packing can
|
|
9
|
+
* later move into a worker.
|
|
10
|
+
*/
|
|
11
|
+
export type AtlasCanvas = HTMLCanvasElement | OffscreenCanvas;
|
|
12
|
+
/**
|
|
13
|
+
* Creates the backing canvas for one atlas, sized to that atlas exactly.
|
|
14
|
+
* @param width - The atlas width in pixels.
|
|
15
|
+
* @param height - The atlas height in pixels.
|
|
16
|
+
* @returns The canvas to draw on.
|
|
17
|
+
*/
|
|
18
|
+
export type CreateAtlasCanvas = (width: number, height: number) => AtlasCanvas;
|
|
19
|
+
/**
|
|
20
|
+
* A region of a source image, in **source pixels with a top-left origin**. These are the same axes
|
|
21
|
+
* as `ImageDef.bounds`, and they are the exact `sx, sy, sw, sh` values that the packer passes to
|
|
22
|
+
* `drawImage`. This type keeps pixels instead of normalized values, so code cannot mistake it for
|
|
23
|
+
* a {@link UvRect}, whose origin is the opposite corner.
|
|
24
|
+
*/
|
|
25
|
+
export interface SourceRect {
|
|
26
|
+
/** Distance from the source's left edge, in pixels. */
|
|
27
|
+
x: number;
|
|
28
|
+
/** Distance from the source's top edge, in pixels. */
|
|
29
|
+
y: number;
|
|
30
|
+
/** Region width in pixels. */
|
|
31
|
+
width: number;
|
|
32
|
+
/** Region height in pixels. */
|
|
33
|
+
height: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A region of an atlas, **normalized in the canvas's own axes: a top-left origin, with v running
|
|
37
|
+
* down**.
|
|
38
|
+
*
|
|
39
|
+
* A texture with `flipY = false` consumes these values. That setting maps canvas row 0 to `v = 0`,
|
|
40
|
+
* so these numbers feed a uv transform directly, with no vertical inversion. This is deliberate.
|
|
41
|
+
* The scene already uses a y axis that points down, because it follows SVG coordinates and the
|
|
42
|
+
* camera matches them. A uv space that also runs down keeps every axis in the pipeline pointing
|
|
43
|
+
* the same way. If only one end inverts, images come out upside down.
|
|
44
|
+
*/
|
|
45
|
+
export interface UvRect {
|
|
46
|
+
/** Left edge, `0` at the atlas's left. */
|
|
47
|
+
u0: number;
|
|
48
|
+
/** Top edge, `0` at the atlas's top. */
|
|
49
|
+
v0: number;
|
|
50
|
+
/** Right edge. */
|
|
51
|
+
u1: number;
|
|
52
|
+
/** Bottom edge. */
|
|
53
|
+
v1: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One source, or one tile of a split source, placed on an atlas. Each placement carries an array
|
|
57
|
+
* index instead of the source object as its address, because this data crosses a `postMessage`
|
|
58
|
+
* boundary, and object identity does not survive that boundary.
|
|
59
|
+
*/
|
|
60
|
+
export interface Placement {
|
|
61
|
+
/** Index into the `sources` array that the caller gave {@link packSources}. */
|
|
62
|
+
index: number;
|
|
63
|
+
/** Where the pixels sit on the atlas. */
|
|
64
|
+
uv: UvRect;
|
|
65
|
+
/**
|
|
66
|
+
* Which part of the source this placement holds. This is undefined when the source packs
|
|
67
|
+
* whole, which is the common case. It is set only on the tiles of a split source.
|
|
68
|
+
*/
|
|
69
|
+
source?: SourceRect;
|
|
70
|
+
}
|
|
71
|
+
/** One packed atlas, before its placements are mapped back onto the defs that wanted them. */
|
|
72
|
+
export interface SourceAtlas {
|
|
73
|
+
/** The canvas holding the packed pixels. Its `width`/`height` are the atlas's size. */
|
|
74
|
+
canvas: AtlasCanvas;
|
|
75
|
+
/** Every placement on this atlas. */
|
|
76
|
+
placements: Placement[];
|
|
77
|
+
/**
|
|
78
|
+
* Whether this atlas's pixels can shrink to fit a memory budget. Bitmaps pack into resizable
|
|
79
|
+
* atlases, canvases pack into fixed ones, and an atlas never mixes the two.
|
|
80
|
+
*/
|
|
81
|
+
resizable: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* What one source contributed to a pack. The caller holds the defs, so only the caller can warn
|
|
85
|
+
* about them. This report gives the caller the measurements it needs to do that.
|
|
86
|
+
*/
|
|
87
|
+
export interface SourceReport {
|
|
88
|
+
/** The source's intrinsic width in pixels. */
|
|
89
|
+
width: number;
|
|
90
|
+
/** The source's intrinsic height in pixels. */
|
|
91
|
+
height: number;
|
|
92
|
+
/**
|
|
93
|
+
* How many rects this source packed into. It is `0` when the source had no pixels and code
|
|
94
|
+
* dropped it. It is `1` when the source packed whole. Otherwise it is the tile count, for a
|
|
95
|
+
* source too big for one atlas that code had to cut.
|
|
96
|
+
*/
|
|
97
|
+
tiles: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Whether a source is a `Blob`, which is also true of a `File`.
|
|
101
|
+
*
|
|
102
|
+
* The test looks for `slice`, and it must keep looking for `slice`. `slice` is Chrome 21, Chrome
|
|
103
|
+
* Android 25, Safari 7 and iOS Safari 7, so every build target has it. `arrayBuffer` reads better
|
|
104
|
+
* and is wrong: it is Chrome 76 and Chrome Android 76, above this package's `chrome66` floor, so on
|
|
105
|
+
* the old Android WebViews it is `undefined` and every real `Blob` would fail this test and reach
|
|
106
|
+
* the packer undecoded (`packages/floorplan/src/components/Maplibre/utils/pmtiles-range-fallback.ts`
|
|
107
|
+
* works around the same floor). No other image source carries either method: not an `<img>`, a
|
|
108
|
+
* canvas, an offscreen canvas or a bitmap.
|
|
109
|
+
*
|
|
110
|
+
* A property test rather than `instanceof`, so this never reaches for a global that can be absent.
|
|
111
|
+
*
|
|
112
|
+
* This is the one home of the test. It decides both what needs a decode and what may cross to the
|
|
113
|
+
* atlas worker, so a second copy could drift from this one, and the drift would only show on the
|
|
114
|
+
* oldest targets.
|
|
115
|
+
* @param source - The source to classify.
|
|
116
|
+
* @returns Whether the source needs a decode.
|
|
117
|
+
*/
|
|
118
|
+
export declare function isBlob(source: object): source is Blob;
|
|
119
|
+
/**
|
|
120
|
+
* Whether a source's pixels can shrink to fit a memory budget. This is `true` for decoded images
|
|
121
|
+
* and bitmaps. It is `false` for canvases, because a canvas's pixels belong to the caller. The
|
|
122
|
+
* code counts a canvas at full size and never compresses it (see the scene's `memoryLimit`).
|
|
123
|
+
*
|
|
124
|
+
* The check looks for the absence of `getContext`. `HTMLCanvasElement` and `OffscreenCanvas` both
|
|
125
|
+
* have it. `ImageBitmap` and `Blob` do not. This is a property test, not an
|
|
126
|
+
* `instanceof` check, because the SDK runs in WebViews. There, `OffscreenCanvas` and `ImageBitmap`
|
|
127
|
+
* can be missing as globals, and an `instanceof` check against a missing global throws an error.
|
|
128
|
+
* @param source - The source to classify.
|
|
129
|
+
* @returns Whether the source can shrink.
|
|
130
|
+
*/
|
|
131
|
+
export declare function isResizableSource(source: object): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Makes the canvas that one atlas draws onto. The function returns an `OffscreenCanvas` where one
|
|
134
|
+
* exists, and a DOM canvas otherwise.
|
|
135
|
+
*
|
|
136
|
+
* This is the only place in the package that builds an atlas canvas, so the `OffscreenCanvas`
|
|
137
|
+
* guard lives here alone. The guard uses this plain form because `eslint-plugin-compat`
|
|
138
|
+
* understands it and flags only an unguarded construction. Nothing here needs hiding from the
|
|
139
|
+
* linter.
|
|
140
|
+
* @param width - The canvas width in pixels.
|
|
141
|
+
* @param height - The canvas height in pixels.
|
|
142
|
+
* @returns The canvas.
|
|
143
|
+
*/
|
|
144
|
+
export declare function createAtlasCanvas(width: number, height: number): AtlasCanvas;
|
|
145
|
+
/**
|
|
146
|
+
* Packs decoded sources into atlas canvases and reports where each one landed.
|
|
147
|
+
*
|
|
148
|
+
* The code splits sources by kind first: resizable ones (bitmaps and images) apart from fixed ones
|
|
149
|
+
* (canvases). Each kind packs on its own, so no atlas ever mixes the two kinds. This lets a memory
|
|
150
|
+
* budget scale a whole resizable atlas without touching a canvas's pixels. Within one kind, the
|
|
151
|
+
* code offers each source to the packer whole. Anything too big for an atlas comes back parked in
|
|
152
|
+
* a bin of its own natural size, and the packer flags that bin. Those are the only sources that get
|
|
153
|
+
* cut into tiles, and the tiles go back through the same packer. Letting the packer decide keeps
|
|
154
|
+
* the guarantee that no atlas exceeds `maxDimension`, with no need to measure every image up
|
|
155
|
+
* front.
|
|
156
|
+
*
|
|
157
|
+
* This function does **not** deduplicate sources. The caller must collect unique sources before it
|
|
158
|
+
* decodes them, so every entry in `sources` is already distinct.
|
|
159
|
+
*
|
|
160
|
+
* This module runs unchanged on the main thread and inside the atlas worker. It must not use
|
|
161
|
+
* `document`, the def types, or the logger.
|
|
162
|
+
* @param sources - The decoded sources to pack, in the order the caller gives them.
|
|
163
|
+
* @param maxDimension - The largest atlas edge in pixels. The function floors this to a positive
|
|
164
|
+
* integer.
|
|
165
|
+
* @param createCanvas - Makes each atlas canvas. Defaults to {@link createAtlasCanvas}.
|
|
166
|
+
* @returns The atlases in packing order (resizable first, then fixed), and one report for each
|
|
167
|
+
* source, aligned by index with `sources`.
|
|
168
|
+
*/
|
|
169
|
+
export declare function packSources(sources: readonly (DecodedSource | null)[], maxDimension: number, createCanvas?: CreateAtlasCanvas): {
|
|
170
|
+
atlases: SourceAtlas[];
|
|
171
|
+
reports: SourceReport[];
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Resolves an atlas canvas's 2D context, asking one canvas kind at a time.
|
|
175
|
+
*
|
|
176
|
+
* Deliberately not `canvas.getContext('2d')` on the union. The two kinds' overload lists disagree
|
|
177
|
+
* at the end — `HTMLCanvasElement` closes with `getContext(id: string): RenderingContext | null`,
|
|
178
|
+
* `OffscreenCanvas` with an `OffscreenRenderingContextId` one — and resolving across the union can
|
|
179
|
+
* combine one kind's `"2d"` overload with the other's catch-all, yielding
|
|
180
|
+
* `OffscreenCanvasRenderingContext2D | RenderingContext`: a type covering bitmap and WebGL contexts
|
|
181
|
+
* that have no `drawImage`. It surfaces only on incremental rebuilds (any compile error in this
|
|
182
|
+
* file leaves the bad resolution cached in `.tsbuildinfo`, where it outlives the error that caused
|
|
183
|
+
* it), so a clean build hides it and a working tree quietly breaks. Narrowed first, each branch has
|
|
184
|
+
* one concrete type whose `"2d"` overload is the only candidate, and the result is stable either
|
|
185
|
+
* way.
|
|
186
|
+
* @param canvas - The atlas canvas.
|
|
187
|
+
* @returns Its 2D context, or `null` when the canvas cannot provide one.
|
|
188
|
+
*/
|
|
189
|
+
export declare function contextOf(canvas: AtlasCanvas): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;
|
|
190
|
+
//# sourceMappingURL=pack-sources.d.ts.map
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { MaxRectsPacker } from 'maxrects-packer';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a source is a `Blob`, which is also true of a `File`.
|
|
4
|
+
*
|
|
5
|
+
* The test looks for `slice`, and it must keep looking for `slice`. `slice` is Chrome 21, Chrome
|
|
6
|
+
* Android 25, Safari 7 and iOS Safari 7, so every build target has it. `arrayBuffer` reads better
|
|
7
|
+
* and is wrong: it is Chrome 76 and Chrome Android 76, above this package's `chrome66` floor, so on
|
|
8
|
+
* the old Android WebViews it is `undefined` and every real `Blob` would fail this test and reach
|
|
9
|
+
* the packer undecoded (`packages/floorplan/src/components/Maplibre/utils/pmtiles-range-fallback.ts`
|
|
10
|
+
* works around the same floor). No other image source carries either method: not an `<img>`, a
|
|
11
|
+
* canvas, an offscreen canvas or a bitmap.
|
|
12
|
+
*
|
|
13
|
+
* A property test rather than `instanceof`, so this never reaches for a global that can be absent.
|
|
14
|
+
*
|
|
15
|
+
* This is the one home of the test. It decides both what needs a decode and what may cross to the
|
|
16
|
+
* atlas worker, so a second copy could drift from this one, and the drift would only show on the
|
|
17
|
+
* oldest targets.
|
|
18
|
+
* @param source - The source to classify.
|
|
19
|
+
* @returns Whether the source needs a decode.
|
|
20
|
+
*/
|
|
21
|
+
export function isBlob(source) {
|
|
22
|
+
return typeof source.slice === 'function';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether a source's pixels can shrink to fit a memory budget. This is `true` for decoded images
|
|
26
|
+
* and bitmaps. It is `false` for canvases, because a canvas's pixels belong to the caller. The
|
|
27
|
+
* code counts a canvas at full size and never compresses it (see the scene's `memoryLimit`).
|
|
28
|
+
*
|
|
29
|
+
* The check looks for the absence of `getContext`. `HTMLCanvasElement` and `OffscreenCanvas` both
|
|
30
|
+
* have it. `ImageBitmap` and `Blob` do not. This is a property test, not an
|
|
31
|
+
* `instanceof` check, because the SDK runs in WebViews. There, `OffscreenCanvas` and `ImageBitmap`
|
|
32
|
+
* can be missing as globals, and an `instanceof` check against a missing global throws an error.
|
|
33
|
+
* @param source - The source to classify.
|
|
34
|
+
* @returns Whether the source can shrink.
|
|
35
|
+
*/
|
|
36
|
+
export function isResizableSource(source) {
|
|
37
|
+
return !('getContext' in source);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Makes the canvas that one atlas draws onto. The function returns an `OffscreenCanvas` where one
|
|
41
|
+
* exists, and a DOM canvas otherwise.
|
|
42
|
+
*
|
|
43
|
+
* This is the only place in the package that builds an atlas canvas, so the `OffscreenCanvas`
|
|
44
|
+
* guard lives here alone. The guard uses this plain form because `eslint-plugin-compat`
|
|
45
|
+
* understands it and flags only an unguarded construction. Nothing here needs hiding from the
|
|
46
|
+
* linter.
|
|
47
|
+
* @param width - The canvas width in pixels.
|
|
48
|
+
* @param height - The canvas height in pixels.
|
|
49
|
+
* @returns The canvas.
|
|
50
|
+
*/
|
|
51
|
+
export function createAtlasCanvas(width, height) {
|
|
52
|
+
if (typeof OffscreenCanvas !== 'undefined')
|
|
53
|
+
return new OffscreenCanvas(width, height);
|
|
54
|
+
const canvas = document.createElement('canvas');
|
|
55
|
+
canvas.width = width;
|
|
56
|
+
canvas.height = height;
|
|
57
|
+
return canvas;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Packs decoded sources into atlas canvases and reports where each one landed.
|
|
61
|
+
*
|
|
62
|
+
* The code splits sources by kind first: resizable ones (bitmaps and images) apart from fixed ones
|
|
63
|
+
* (canvases). Each kind packs on its own, so no atlas ever mixes the two kinds. This lets a memory
|
|
64
|
+
* budget scale a whole resizable atlas without touching a canvas's pixels. Within one kind, the
|
|
65
|
+
* code offers each source to the packer whole. Anything too big for an atlas comes back parked in
|
|
66
|
+
* a bin of its own natural size, and the packer flags that bin. Those are the only sources that get
|
|
67
|
+
* cut into tiles, and the tiles go back through the same packer. Letting the packer decide keeps
|
|
68
|
+
* the guarantee that no atlas exceeds `maxDimension`, with no need to measure every image up
|
|
69
|
+
* front.
|
|
70
|
+
*
|
|
71
|
+
* This function does **not** deduplicate sources. The caller must collect unique sources before it
|
|
72
|
+
* decodes them, so every entry in `sources` is already distinct.
|
|
73
|
+
*
|
|
74
|
+
* This module runs unchanged on the main thread and inside the atlas worker. It must not use
|
|
75
|
+
* `document`, the def types, or the logger.
|
|
76
|
+
* @param sources - The decoded sources to pack, in the order the caller gives them.
|
|
77
|
+
* @param maxDimension - The largest atlas edge in pixels. The function floors this to a positive
|
|
78
|
+
* integer.
|
|
79
|
+
* @param createCanvas - Makes each atlas canvas. Defaults to {@link createAtlasCanvas}.
|
|
80
|
+
* @returns The atlases in packing order (resizable first, then fixed), and one report for each
|
|
81
|
+
* source, aligned by index with `sources`.
|
|
82
|
+
*/
|
|
83
|
+
export function packSources(sources, maxDimension, createCanvas = createAtlasCanvas) {
|
|
84
|
+
const limit = Math.max(1, Math.floor(maxDimension));
|
|
85
|
+
// Every member of `DecodedSource` reports its own pixel size, so nothing here needs to duck-type
|
|
86
|
+
// a size. That was only ever needed because an `<img>` reports its layout size as `width`. A
|
|
87
|
+
// source that fails to decode arrives as `null` and reports `0 x 0`. Code skips that source the
|
|
88
|
+
// same way it skips a zero-sized canvas.
|
|
89
|
+
const reports = sources.map((source) => source
|
|
90
|
+
? { width: source.width, height: source.height, tiles: 0 }
|
|
91
|
+
: { width: 0, height: 0, tiles: 0 });
|
|
92
|
+
const resizable = [];
|
|
93
|
+
const fixed = [];
|
|
94
|
+
for (let index = 0; index < sources.length; index++) {
|
|
95
|
+
const source = sources[index];
|
|
96
|
+
// A source that failed to decode is dropped later by the size check in `packGroup`. Code
|
|
97
|
+
// groups it with the resizable sources, so it never lands in an atlas of canvases on its way
|
|
98
|
+
// to being skipped.
|
|
99
|
+
(!source || isResizableSource(source) ? resizable : fixed).push(index);
|
|
100
|
+
}
|
|
101
|
+
// One packer serves both groups, reset between them. The configuration (size, padding, options)
|
|
102
|
+
// is the same for each, so reusing the packer spares an allocation per pack. The packer cannot
|
|
103
|
+
// carry over across pack calls, though, because `limit` is fixed at construction and changes
|
|
104
|
+
// from call to call.
|
|
105
|
+
// `pot: false`, because WebGL 2 samples non-power-of-two textures without restriction. Rounding
|
|
106
|
+
// each atlas up only wastes memory. `smart` stays on (the default), which shrinks every bin to
|
|
107
|
+
// its content. That is why the code reads the size back off the bin instead of assuming
|
|
108
|
+
// `limit`.
|
|
109
|
+
const packer = new MaxRectsPacker(limit, limit, PADDING, { pot: false });
|
|
110
|
+
const atlases = [
|
|
111
|
+
...packGroup(packer, sources, resizable, reports, limit, createCanvas, true),
|
|
112
|
+
...packGroup(packer, sources, fixed, reports, limit, createCanvas, false),
|
|
113
|
+
];
|
|
114
|
+
return { atlases, reports };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The gap between neighboring images on an atlas. This gap stops linear filtering at one image's
|
|
118
|
+
* edge from reaching into the next image's pixels. The packer applies this gap *between* rects
|
|
119
|
+
* only, so atlas edges stay flush against the pixels. This is also why a tile exactly
|
|
120
|
+
* `maxDimension` across still fits in a bin.
|
|
121
|
+
*/
|
|
122
|
+
const PADDING = 1;
|
|
123
|
+
/**
|
|
124
|
+
* Packs one kind of source, either all resizable or all fixed, into atlases. The function tags
|
|
125
|
+
* each atlas with the group's kind and records each source's tile count in `reports`.
|
|
126
|
+
* @param packer - The shared packer. It is reset to empty before this group is added.
|
|
127
|
+
* @param sources - Every source, indexed by the entries in `indices`.
|
|
128
|
+
* @param indices - The indices of this kind. This list can be empty, and then nothing packs.
|
|
129
|
+
* @param reports - Per-source reports. This function fills in each report's `tiles` count.
|
|
130
|
+
* @param limit - The largest atlas edge, in pixels.
|
|
131
|
+
* @param createCanvas - Makes each atlas canvas.
|
|
132
|
+
* @param resizable - The kind, stamped onto every atlas that this call produces.
|
|
133
|
+
* @returns The atlases this group produced.
|
|
134
|
+
*/
|
|
135
|
+
function packGroup(packer, sources, indices, reports, limit, createCanvas, resizable) {
|
|
136
|
+
const rects = [];
|
|
137
|
+
for (const index of indices) {
|
|
138
|
+
const { width, height } = reports[index];
|
|
139
|
+
// A `drawImage` call throws in some browsers when the source region has zero width or zero
|
|
140
|
+
// height. So code drops an image that has not decoded, or a canvas sized to nothing, instead
|
|
141
|
+
// of drawing it. Its report keeps `tiles: 0`, and that value tells the caller to warn about
|
|
142
|
+
// it.
|
|
143
|
+
if (width <= 0 || height <= 0)
|
|
144
|
+
continue;
|
|
145
|
+
rects.push({ width, height, x: 0, y: 0, hash: `${index}`, data: { index } });
|
|
146
|
+
}
|
|
147
|
+
// Clear whatever the previous group left in the packer, keeping its configuration.
|
|
148
|
+
packer.reset();
|
|
149
|
+
// `addArray` sorts by longest edge before it places rects, which packs far tighter than
|
|
150
|
+
// insertion order.
|
|
151
|
+
packer.addArray(rects);
|
|
152
|
+
// The packer tags whatever it must park in an oversized bin. The packer decides what needs
|
|
153
|
+
// cutting, not a separate measurement in this code. The tiles are ordinary rects, and they pack
|
|
154
|
+
// into the bins that the first pass already opened.
|
|
155
|
+
const oversized = rects.filter((rect) => rect.oversized);
|
|
156
|
+
if (oversized.length > 0) {
|
|
157
|
+
packer.addArray(oversized.flatMap((rect) => cutTiles(rect, reports[rect.data.index], limit)));
|
|
158
|
+
}
|
|
159
|
+
// Every oversized bin now has its tiles standing in for it, so the code drops the bin instead of
|
|
160
|
+
// returning it. Handing back a canvas larger than the caller's limit is the exact problem this
|
|
161
|
+
// code exists to prevent.
|
|
162
|
+
const bins = packer.bins.filter((bin) => !bin.rects.some((rect) => rect.oversized));
|
|
163
|
+
const atlases = bins.map((bin) => ({
|
|
164
|
+
...renderAtlas(bin, sources, reports, createCanvas),
|
|
165
|
+
resizable,
|
|
166
|
+
}));
|
|
167
|
+
// Count what actually landed. This lets code tell a dropped source apart from a split one.
|
|
168
|
+
for (const bin of bins)
|
|
169
|
+
for (const rect of bin.rects)
|
|
170
|
+
reports[rect.data.index].tiles++;
|
|
171
|
+
return atlases;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Cuts a source that did not fit in the packer into a grid of tiles. No tile is larger than one
|
|
175
|
+
* atlas. Tiles are exact and do not overlap. Each tile ends up alone, or beside its own sibling
|
|
176
|
+
* tiles, in an atlas. Nothing can bleed across the cut, so the code needs no overlap column to
|
|
177
|
+
* hide a seam.
|
|
178
|
+
* @param rect - The oversized rect that names the source to cut.
|
|
179
|
+
* @param report - That source's measured size.
|
|
180
|
+
* @param limit - The largest tile edge, in pixels.
|
|
181
|
+
* @returns The tiles to pack in its place.
|
|
182
|
+
*/
|
|
183
|
+
function cutTiles(rect, report, limit) {
|
|
184
|
+
const columns = Math.ceil(report.width / limit);
|
|
185
|
+
const rows = Math.ceil(report.height / limit);
|
|
186
|
+
const tiles = [];
|
|
187
|
+
for (let row = 0; row < rows; row++) {
|
|
188
|
+
const y = row * limit;
|
|
189
|
+
const height = Math.min(limit, report.height - y);
|
|
190
|
+
for (let column = 0; column < columns; column++) {
|
|
191
|
+
const x = column * limit;
|
|
192
|
+
const width = Math.min(limit, report.width - x);
|
|
193
|
+
tiles.push({
|
|
194
|
+
width,
|
|
195
|
+
height,
|
|
196
|
+
x: 0,
|
|
197
|
+
y: 0,
|
|
198
|
+
hash: `${rect.hash}:${column},${row}`,
|
|
199
|
+
data: { index: rect.data.index, source: { x, y, width, height } },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return tiles;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Draws one packed bin onto a canvas and records where each rect landed.
|
|
207
|
+
* @param bin - The packed bin, already sized to its content.
|
|
208
|
+
* @param sources - Every source, indexed by each rect's tile.
|
|
209
|
+
* @param reports - Per-source sizes, for the whole-image draw region.
|
|
210
|
+
* @param createCanvas - Makes the canvas to draw on.
|
|
211
|
+
* @returns The atlas, less its `resizable` flag.
|
|
212
|
+
*/
|
|
213
|
+
function renderAtlas(bin, sources, reports, createCanvas) {
|
|
214
|
+
const canvas = createCanvas(bin.width, bin.height);
|
|
215
|
+
const context = contextOf(canvas);
|
|
216
|
+
// Nothing downstream can recover from a missing context. Every atlas then uploads blank, and
|
|
217
|
+
// the images silently draw as nothing at all. It is better to fail here, where the cause stays
|
|
218
|
+
// visible.
|
|
219
|
+
if (!context)
|
|
220
|
+
throw new Error(`pack-sources: no 2d context for a ${bin.width}x${bin.height} atlas`);
|
|
221
|
+
const placements = [];
|
|
222
|
+
for (const rect of bin.rects) {
|
|
223
|
+
const { index, source } = rect.data;
|
|
224
|
+
// The copied region is the tile when the rect has one, and the whole image otherwise. Both
|
|
225
|
+
// read from the source's own axes. This region happens to match the packed rect's size today,
|
|
226
|
+
// because tiles are placed 1:1 and unrotated. Reading the size from the destination instead
|
|
227
|
+
// is correct only by accident, not by design. The code keeps this region apart from `source`
|
|
228
|
+
// itself, which stays undefined for an unsplit image, so the placement below does not report
|
|
229
|
+
// a region that the caller has no use for.
|
|
230
|
+
const { width, height } = reports[index];
|
|
231
|
+
const region = source ?? { x: 0, y: 0, width, height };
|
|
232
|
+
// This value is never null, by construction. A source that fails to decode reports `0 x 0`
|
|
233
|
+
// and never reaches the packer, so every rect in a bin has a drawable source behind it.
|
|
234
|
+
const decoded = sources[index];
|
|
235
|
+
if (!decoded)
|
|
236
|
+
continue;
|
|
237
|
+
context.drawImage(decoded, region.x, region.y, region.width, region.height, rect.x, rect.y, rect.width, rect.height);
|
|
238
|
+
// The rect uses canvas pixels, and uv keeps the canvas's axes, so this is a plain
|
|
239
|
+
// normalization with no vertical inversion (see {@link UvRect}).
|
|
240
|
+
const uv = {
|
|
241
|
+
u0: rect.x / bin.width,
|
|
242
|
+
v0: rect.y / bin.height,
|
|
243
|
+
u1: (rect.x + rect.width) / bin.width,
|
|
244
|
+
v1: (rect.y + rect.height) / bin.height,
|
|
245
|
+
};
|
|
246
|
+
placements.push(source ? { index, uv, source } : { index, uv });
|
|
247
|
+
}
|
|
248
|
+
return { canvas, placements };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Resolves an atlas canvas's 2D context, asking one canvas kind at a time.
|
|
252
|
+
*
|
|
253
|
+
* Deliberately not `canvas.getContext('2d')` on the union. The two kinds' overload lists disagree
|
|
254
|
+
* at the end — `HTMLCanvasElement` closes with `getContext(id: string): RenderingContext | null`,
|
|
255
|
+
* `OffscreenCanvas` with an `OffscreenRenderingContextId` one — and resolving across the union can
|
|
256
|
+
* combine one kind's `"2d"` overload with the other's catch-all, yielding
|
|
257
|
+
* `OffscreenCanvasRenderingContext2D | RenderingContext`: a type covering bitmap and WebGL contexts
|
|
258
|
+
* that have no `drawImage`. It surfaces only on incremental rebuilds (any compile error in this
|
|
259
|
+
* file leaves the bad resolution cached in `.tsbuildinfo`, where it outlives the error that caused
|
|
260
|
+
* it), so a clean build hides it and a working tree quietly breaks. Narrowed first, each branch has
|
|
261
|
+
* one concrete type whose `"2d"` overload is the only candidate, and the result is stable either
|
|
262
|
+
* way.
|
|
263
|
+
* @param canvas - The atlas canvas.
|
|
264
|
+
* @returns Its 2D context, or `null` when the canvas cannot provide one.
|
|
265
|
+
*/
|
|
266
|
+
export function contextOf(canvas) {
|
|
267
|
+
// `in` rather than `instanceof`: in a worker there is no `HTMLCanvasElement` global to test
|
|
268
|
+
// against, and an `instanceof` against a missing global throws.
|
|
269
|
+
if ('convertToBlob' in canvas)
|
|
270
|
+
return canvas.getContext('2d');
|
|
271
|
+
return canvas.getContext('2d');
|
|
272
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type Atlas } from './atlas-packer';
|
|
2
|
+
/**
|
|
3
|
+
* The GPU footprint of an atlas of the given pixel size: RGBA, with the mipmap chain counted. Reads
|
|
4
|
+
* the atlas's full (packed) dimensions — the resize factor scales this down, so budgeting against
|
|
5
|
+
* the full size is what the factor is solved from.
|
|
6
|
+
* @param width - Atlas width in pixels.
|
|
7
|
+
* @param height - Atlas height in pixels.
|
|
8
|
+
* @returns The size in bytes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getAtlasSizeBytes(width: number, height: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* One atlas's contribution to the budget: its full size, and whether it may be scaled to fit. The
|
|
13
|
+
* object doubles as its own registration handle — {@link TextureBudget.unregister} takes the very
|
|
14
|
+
* object {@link TextureBudget.register} was given, so identity alone distinguishes two atlases of
|
|
15
|
+
* equal size and no separate key is needed.
|
|
16
|
+
*/
|
|
17
|
+
interface AtlasBudgetEntry {
|
|
18
|
+
/** Full atlas width in pixels. */
|
|
19
|
+
width: number;
|
|
20
|
+
/** Full atlas height in pixels. */
|
|
21
|
+
height: number;
|
|
22
|
+
/** Whether this atlas's bytes shrink with the resize factor, or count at full size always. */
|
|
23
|
+
resizable: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The scene's shared texture-memory budget. Every image atlas registers its full size here, and the
|
|
27
|
+
* store solves for one linear resize factor that brings the resizable atlases within `memoryLimit` —
|
|
28
|
+
* shared by all of them, so their relative sizes are preserved with no priority.
|
|
29
|
+
*
|
|
30
|
+
* The factor is exposed through `useSyncExternalStore`: an atlas that mounts, unmounts, or a limit
|
|
31
|
+
* that changes recomputes it, and a material move re-derives only the resizable atlases' textures —
|
|
32
|
+
* never a repack, since uv regions are normalized and so invariant to the canvas's pixel size.
|
|
33
|
+
* Recompute runs synchronously per registration; the render churn that would cause is left to
|
|
34
|
+
* React's automatic batching to coalesce, and the `FACTOR_EPSILON` gate keeps sub-pixel nudges from
|
|
35
|
+
* notifying at all. When no cap is set the whole apparatus stands down: {@link TextureBudget.isActive}
|
|
36
|
+
* is false, atlases skip registration entirely (see {@link useResizeFactor}), and the factor is 1.
|
|
37
|
+
*/
|
|
38
|
+
export declare class TextureBudget {
|
|
39
|
+
/** The cap in megabytes, or undefined/`0` for no cap — a falsy value scales nothing. */
|
|
40
|
+
private memoryLimitMb;
|
|
41
|
+
/** Every registered atlas; the entry object is its own handle. */
|
|
42
|
+
private readonly atlases;
|
|
43
|
+
/** Subscribers to the resize factor — the mounted resizable atlases. */
|
|
44
|
+
private readonly listeners;
|
|
45
|
+
/** The current shared linear resize factor; `1` means no scaling. */
|
|
46
|
+
private factor;
|
|
47
|
+
/**
|
|
48
|
+
* @param memoryLimitMb - The cap in megabytes, or undefined/`0` for no cap.
|
|
49
|
+
*/
|
|
50
|
+
constructor(memoryLimitMb?: number);
|
|
51
|
+
/**
|
|
52
|
+
* Whether a cap is in force. False for an absent or zero limit — nothing can be compressed, so
|
|
53
|
+
* consumers skip the budget entirely rather than do work that resolves to "no scaling".
|
|
54
|
+
* @returns Whether a positive limit is set.
|
|
55
|
+
*/
|
|
56
|
+
isActive(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Changes the cap. Called when the renderer's `memoryLimit` prop changes, so a caller can raise,
|
|
59
|
+
* lower, or clear it at runtime and the resizable atlases re-derive their textures live.
|
|
60
|
+
* @param memoryLimitMb - The new cap, or undefined/`0` for no cap.
|
|
61
|
+
*/
|
|
62
|
+
setMemoryLimit(memoryLimitMb: number | undefined): void;
|
|
63
|
+
/**
|
|
64
|
+
* Registers one atlas's full size, recomputing the factor. Only called while {@link isActive}, so
|
|
65
|
+
* an uncapped scene never populates this set.
|
|
66
|
+
* @param entry - The atlas's dimensions and kind; pass the same object back to unregister.
|
|
67
|
+
*/
|
|
68
|
+
register(entry: AtlasBudgetEntry): void;
|
|
69
|
+
/**
|
|
70
|
+
* Drops an atlas's contribution, recomputing the factor.
|
|
71
|
+
* @param entry - The object {@link register} was given.
|
|
72
|
+
*/
|
|
73
|
+
unregister(entry: AtlasBudgetEntry): void;
|
|
74
|
+
/**
|
|
75
|
+
* The current shared resize factor.
|
|
76
|
+
* @returns The factor in `(0, 1]`.
|
|
77
|
+
*/
|
|
78
|
+
getResizeFactor(): number;
|
|
79
|
+
/**
|
|
80
|
+
* Subscribes to factor and active-state changes.
|
|
81
|
+
* @param listener - Called when the factor moves past {@link FACTOR_EPSILON} or the cap toggles.
|
|
82
|
+
* @returns The unsubscribe function.
|
|
83
|
+
*/
|
|
84
|
+
subscribe(listener: () => void): () => void;
|
|
85
|
+
/** Recomputes the factor and, if it moved materially, notifies subscribers. */
|
|
86
|
+
private recompute;
|
|
87
|
+
/** Notifies every subscriber. */
|
|
88
|
+
private notify;
|
|
89
|
+
/**
|
|
90
|
+
* Solves for the linear factor that fits the resizable atlases in the budget the fixed ones leave.
|
|
91
|
+
*
|
|
92
|
+
* Every degenerate case falls back to `1` — no scaling — rather than forcing a fit: no cap, nothing
|
|
93
|
+
* resizable to scale, or a cap the fixed atlases alone already exceed (canvases cannot be
|
|
94
|
+
* compressed, so there is nothing to give). The factor is only ever clamped up to `1`, never down
|
|
95
|
+
* to a floor: an image is scaled by however much the budget demands.
|
|
96
|
+
* @returns The factor in `(0, 1]`.
|
|
97
|
+
*/
|
|
98
|
+
private computeResizeFactor;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The scene's texture budget, provided by `Renderer`. The default is an uncapped budget, so an image
|
|
102
|
+
* layer rendered outside a `Renderer` (a test, a story) behaves exactly as before this existed —
|
|
103
|
+
* everything uploads at full size.
|
|
104
|
+
*/
|
|
105
|
+
export declare const TextureBudgetContext: import("react").Context<TextureBudget>;
|
|
106
|
+
/**
|
|
107
|
+
* Registers an atlas with the scene budget for its mounted lifetime and returns the resize factor it
|
|
108
|
+
* should upload at: the shared factor for a resizable atlas, always `1` for a fixed one (its pixels
|
|
109
|
+
* still count toward the budget, they just never scale).
|
|
110
|
+
*
|
|
111
|
+
* When the budget is inactive — no `memoryLimit` — the atlas is not registered at all: there is
|
|
112
|
+
* nothing to solve, so an uncapped scene does no per-atlas budget work. Turning a cap on at runtime
|
|
113
|
+
* flips `isActive`, which re-runs the registration effect.
|
|
114
|
+
* @param budget - The scene budget from context.
|
|
115
|
+
* @param atlas - The atlas to register and read a factor for.
|
|
116
|
+
* @returns The linear factor this atlas's canvas should be scaled by before upload, in `(0, 1]`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function useResizeFactor(budget: TextureBudget, atlas: Atlas): number;
|
|
119
|
+
export {};
|
|
120
|
+
//# sourceMappingURL=texture-budget.d.ts.map
|