@kurot/core 2.0.0 → 2.1.0

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.
@@ -1,10 +1,9 @@
1
1
  /**
2
- * Multilingual text tokenization for word-wrapping.
2
+ * Locale-aware word segmentation utility.
3
3
  *
4
4
  * Uses `Intl.Segmenter` (supported in all browsers since 2024) for locale-aware
5
- * word segmentation. Correctly handles Latin (space-separated), CJK (each
6
- * character is a segment), Thai/Khmer/Lao (dictionary-based word boundaries),
7
- * and mixed-script text.
5
+ * word segmentation, including dictionary-based boundaries. Word boundaries
6
+ * are distinct from Unicode line-break opportunities; TextField uses LineBreaks.
8
7
  *
9
8
  * Falls back to character-by-character splitting when `Intl.Segmenter` is
10
9
  * unavailable.
@@ -38,7 +37,7 @@ const BREAKING_SPACES = new Set([
38
37
  const NEWLINES = new Set([0x000a, 0x000d]);
39
38
  // ── Public API ───────────────────────────────────────────────────────────────
40
39
  /**
41
- * Split text into word / space segments suitable for line-wrapping.
40
+ * Split text into locale-aware word / space segments.
42
41
  *
43
42
  * Returns plain strings — each string is either a word or a space.
44
43
  * Newlines are NOT included (the caller should split by newlines first).
@@ -90,8 +89,7 @@ export function tokenize(text) {
90
89
  }
91
90
  /**
92
91
  * Split a string into grapheme clusters (user-perceived characters).
93
- * Used when a single token is wider than the field and must be broken
94
- * character-by-character.
92
+ * This utility is independent of TextField's UAX #14 line layout.
95
93
  */
96
94
  export function splitGraphemes(text) {
97
95
  if (hasSegmenter) {
@@ -1 +1 @@
1
- {"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,238 @@
1
+ # @kurot/core — AI context map
2
+
3
+ Read this before exploring `src/`. It is a compressed map of the package so an
4
+ agent unfamiliar with Kurot does not need to re-derive the architecture from
5
+ scratch on every session. Treat the package source and its `src/index.ts`
6
+ barrel as the authority for current behavior and exports.
7
+
8
+ Package identity: `@kurot/core@2.1.0`. It provides Kurot's scene graph,
9
+ events, rendering, text, resource, network and media runtime. Rendering uses a
10
+ flat `InstructionSet + RenderPipe` pipeline. ES2022 / evergreen browsers only
11
+ with `strict: true`. Two
12
+ rendering backends: WebGL (primary, two-phase build/execute, InstructionSet-
13
+ driven) and Canvas 2D (fallback, direct scene-graph traversal every frame — no
14
+ InstructionSet, no dirty-flag optimization).
15
+
16
+ Source root: `src/kurot/`. Public API: `src/index.ts` re-exports each
17
+ subfolder's `index.ts` — see §4 for the full grouped list before assuming
18
+ something isn't exported.
19
+
20
+ ## 1. Directory map
21
+
22
+ ```
23
+ src/kurot/
24
+ ├── display/ Scene graph: DisplayObject → DisplayObjectContainer → Sprite/Stage,
25
+ │ Bitmap, Shape, Mesh, Graphics (flat GraphicsCommand[], not a node tree),
26
+ │ texture/ (BitmapData, Texture, RenderTexture, SpriteSheet).
27
+ │ Defines the retained tree the renderer reads from; does not render itself.
28
+ ├── player/ Game loop + both render backends. Player, createPlayer(), SystemTicker/
29
+ │ ticker singleton, ScreenAdapter, TouchHandler. Backend-neutral abstractions
30
+ │ live here: RenderPipe / RenderContext / RenderBuffer interfaces (the latter
31
+ │ two are internal), InstructionSet, and pipes/ (Bitmap/Graphics/Mesh/Text/
32
+ │ Filter/Mask/Particle). The WebGL instruction renderer consumes the pipes.
33
+ │ ├── webgl/ WebGLRenderer, WebGLRenderContext, WebGLRenderBuffer/Target,
34
+ │ │ WebGLVertexArrayObject, WebGLDrawCmdManager, MultiTextureBatcher,
35
+ │ │ shaders/ (ShaderLib GLSL 1.00, ShaderLib2 GLSL 3.00). The WebGL-only execute path.
36
+ │ └── canvas/ CanvasRenderer (fallback AND a dependency of the WebGL path — see §2),
37
+ │ DisplayList (offscreen cache backing cacheAsBitmap), CanvasBuffer.
38
+ ├── events/ Event, EventDispatcher (capture/bubble, once()), EventPhase,
39
+ │ 8 concrete subclasses (TouchEvent, TimerEvent, ProgressEvent, etc.).
40
+ ├── geom/ Matrix, Point, Rectangle + shared*/create()/release() object pools.
41
+ ├── filters/ Filter base + BlurFilter, GlowFilter, DropShadowFilter, ColorMatrixFilter,
42
+ │ CustomFilter, MultiPassFilter, BloomFilter. GPU execution in player/webgl/;
43
+ │ push/pop scene integration in player/pipes/.
44
+ ├── text/ TextField, BitmapText/BitmapFont, StageText (DOM overlay, INPUT mode only),
45
+ │ HtmlTextParser, InputController, TextMeasurer, LineBreaks, TextLineLayout, WordWrap.
46
+ ├── resource/ Resource class + `resource` singleton, ResourceLoader, analyzers/
47
+ │ (Image/Json/Text/Sound/Sheet). Async, resource.json-driven (RES-compatible).
48
+ ├── net/ HttpRequest, ImageLoader. Low-level; resource/analyzers build on these.
49
+ ├── media/ Sound (+SoundChannel), Video. Web Audio + HTMLAudioElement fallback.
50
+ ├── system/ Capabilities (static). Must be _init()'d — createPlayer() does this for you.
51
+ ├── utils/ ByteArray, Timer, Logger, FontManager, DebugLog, Base64Util, NumberUtils.
52
+ │ No engine-wide numeric object identity API is provided.
53
+ ├── localStorage/ Plain functions (getItem/setItem/removeItem/clear), exported as a namespace.
54
+ └── external/ ExternalInterface — bridge to window.* host callbacks.
55
+ ```
56
+
57
+ The dev-only benchmark page, runtime, adapters, vendor baseline, and tests are
58
+ colocated under `examples/benchmark/`; none are exported from `index.ts`.
59
+
60
+ ## 2. Non-obvious current behavior
61
+
62
+ - `DisplayObject.matrix` getter returns a **clone**, not a live reference.
63
+ Mutating the returned matrix does nothing — assign it back or use `$setMatrix`.
64
+ - `getChildAt`/`removeChildAt` return `undefined` on out-of-bounds instead of
65
+ throwing. `removeChildren()` returns `void`, not the removed array.
66
+ - `blendMode` string values are Canvas 2D composite-operation names;
67
+ the normal value is `"source-over"`.
68
+ - `cacheAsBitmap` is a pure alias for `cacheAsTexture(true)` — there is no
69
+ separate legacy path. It **always** rasterizes to an offscreen Canvas 2D
70
+ surface first (`DisplayList`), then optionally re-uploads as a GL texture.
71
+ It never means "cache directly to a GPU framebuffer."
72
+ - `DisplayObjectContainer.isRenderGroup` has **zero visual effect on Canvas 2D
73
+ rendering**. It's a WebGL-only optimization hint that isolates a subtree
74
+ into its own `InstructionSet` — toggling it never changes pixels, only which
75
+ instruction set absorbs rebuilds.
76
+ - `CanvasRenderer` is not purely a fallback. The WebGL backend depends on it
77
+ internally to rasterize Graphics/Text to offscreen canvases before texture
78
+ upload, and to snapshot `RenderTexture`. Don't reason about it as dead code
79
+ when WebGL is active.
80
+ - The engine is **single-Player by design**. `Player`'s constructor wires
81
+ static hook fields directly onto `DisplayObject`/`DisplayObjectContainer`
82
+ (`$onStructureChange`, etc.) — there is no listener registry. A second
83
+ `Player` instance clobbers the first one's hooks.
84
+ - The `WebGLRenderBuffer.release(WebGLRenderBuffer.create(...))` line at the
85
+ end of `WebGLRenderer.render()` looks like dead code but isn't — it resets
86
+ the GL viewport/projection after an in-frame offscreen activation (filter,
87
+ mask, cacheAsBitmap). It's marked "DO NOT delete" in the source for a reason.
88
+ - `RenderPipe.destroyRenderable()` (immediate GPU resource release) is **not**
89
+ called automatically on removal from stage, because `$onRemoveFromStage`
90
+ also fires for temporary removals (e.g. virtualized lists). `GraphicsPipe`
91
+ and `TextPipe` instead rely on a `FinalizationRegistry` to `gl.deleteTexture()`
92
+ when the JS object is actually garbage-collected.
93
+ - `Resource` deliberately does **not** extend `EventDispatcher` — it has its
94
+ own `on`/`off`/`onProgress` API. Don't assume every "event-ish" class in
95
+ this codebase shares one dispatch shape.
96
+ - `resource.onProgress(cb)` listeners are permanent (never auto-removed); use
97
+ `loadGroup()`'s own `onProgress` param for one-off tracking. Concurrent
98
+ `loadGroup()` calls serialize through one internal `ResourceLoader`, they do
99
+ not run in parallel.
100
+ - `Base64Util.encode()` takes an `ArrayBuffer`, not a `string`
101
+ (`encode(new TextEncoder().encode(str))`).
102
+ - `Capabilities` must be initialized (`Capabilities._init()`) before use.
103
+ `createPlayer()` does this automatically; constructing `Player` directly
104
+ bypasses it and leaves capability queries stale.
105
+ - `StageText` (a real DOM `<input>` overlaid on the canvas) is only used for
106
+ `TextFieldType.INPUT` — the rest of `TextField` is fully canvas/GPU-drawn.
107
+ - GPU filter arrays execute in order. `CustomFilter.from()` takes explicit
108
+ WebGL 1/2 sources with numeric uniforms and auxiliary full-image BitmapData
109
+ bindings. `MultiPassFilter` retains earlier/original inputs for an acyclic
110
+ pass graph; `BloomFilter` is an LDR extraction/blur/composite example.
111
+ See `docs/filters.md` for exact UV, alpha, resolution and resource contracts.
112
+ - `BlurFilter.quality` controls pass pairs (1–16); large radii downsample to
113
+ stay within the existing 32-physical-pixel shader tier. Glow/DropShadow keep
114
+ their existing fixed-sample shader and do not use their quality metadata.
115
+ - GPU filters are skipped by Canvas 2D, including the Canvas capture used by
116
+ cacheAsTexture and RenderTexture. Do not cache GPU effects through those APIs.
117
+ - Filter setters invalidate weakly attached users. Direct uniform/binding edits
118
+ need `filter.invalidate()`. Auxiliary canvas/video edits additionally need
119
+ `BitmapData.invalidate(source)` for upload refresh.
120
+ - Intermediate filter targets share a 16-entry / 64 MiB idle pool. Active targets,
121
+ subtree captures and auxiliary uploads are separate. Player.destroy releases
122
+ idle effect targets and auxiliary uploads. Shader programs are cached per GL
123
+ context and source; compile/link failures throw and abort the failed frame.
124
+ - `Video` extends `Bitmap` and is directly renderable. It swaps between poster
125
+ and video textures, invalidates `BitmapData` on each available video frame,
126
+ and uses `requestAnimationFrame` only when `requestVideoFrameCallback` is
127
+ unavailable.
128
+ - hitTest / lookup APIs return `undefined`, never `null`, throughout — check
129
+ `=== undefined`.
130
+
131
+ ## 3. Domain-specific terminology
132
+
133
+ | Term | Definition | Where defined |
134
+ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
135
+ | `InstructionSet` | Flat, reusable array of `Instruction` objects representing one frame's draw ops for a subtree, replacing scene-graph traversal at execute time. Tracks `structureDirty`, `dirtyRenderables`, and a `renderableIndex: Map<DisplayObject, number\|number[]>` for O(1) lookup during incremental patches. | `player/InstructionSet.ts` |
136
+ | `RenderPipe` | Interface implemented once per display-object category (`BitmapPipe`, `GraphicsPipe`, `MeshPipe`, `TextPipe`, `FilterPipe`, `MaskPipe`, `ParticlePipe`). It defines `addToInstructionSet()` (build), `updateRenderable()` (patch), and optional `destroyRenderable()` cleanup. The current renderer does not invoke cleanup merely because an object leaves the stage. Instructions are dispatched by `renderPipeId`. | `player/RenderPipe.ts`, implementations in `player/pipes/` |
137
+ | `structureDirty` vs `renderDirty` | `structureDirty` (on `InstructionSet`): topology changed (child added/removed/reordered, filter/mask changed) → full rebuild. `renderDirty` (on `DisplayObject`, propagated via `$renderDirtyUp()`): only visual data changed (position/alpha/tint/texture) on an object whose instruction-list slot is still valid → patch only, no rebuild. | Propagated in `display/DisplayObject.ts` (`$markDirty()`, `$cacheDirtyUp()`, `$renderDirtyUp()`); consumed in `player/webgl/WebGLRenderer.ts` `render()` |
138
+ | `RenderGroup` | A `DisplayObjectContainer` with `isRenderGroup = true`. Gets its own independent `InstructionSet` (tracked via `WeakMap`/`WeakRef` in `WebGLRenderer`). Structural changes inside the group never force the parent to rebuild — the parent set holds a single `renderGroup` instruction pointing at the child set. | `isRenderGroup` field: `display/DisplayObjectContainer.ts`; group handling: `player/webgl/WebGLRenderer.ts` (`_buildRenderGroup()`, `markStructureDirty()`, `markRenderableDirty()`) |
139
+ | `cacheAsTexture` vs `cacheAsBitmap` | `cacheAsBitmap` is a pure alias for `cacheAsTexture(true/false)`. `cacheAsTexture(options)` accepts `{resolution?, scaleMode?}` and creates a `DisplayList` (offscreen Canvas 2D buffer), reused across frames until dirty. On WebGL it's drawn as a single `displayListCache` instruction; rasterization still goes through `CanvasRenderer`, then the result is uploaded as a GL texture. | `display/DisplayObject.ts`; `player/canvas/DisplayList.ts`; `player/webgl/WebGLRenderer.ts` (`_executeDisplayListCache()`) |
140
+ | `DrawCmdManager` (`WebGLDrawCmdManager`) | Queue of `DrawCmd` records (12 types: TEXTURE, RECT, PUSH*MASK, POP_MASK, BLEND, RESIZE_TARGET, CLEAR_COLOR, ACT_BUFFER, ENABLE_SCISSOR, DISABLE_SCISSOR, SMOOTHING, MULTI_TEXTURE) sitting between `RenderPipe`s and actual GL calls. Auto-merges consecutive compatible commands. `WebGLRenderContext._flush()` walks it once per frame. This is the real batching layer — distinct from `MultiTextureBatcher`, which only assigns texture \_slots* for the `MULTI_TEXTURE` command. | `player/webgl/WebGLDrawCmdManager.ts` |
141
+ | `WebGLRenderContext` lifecycle | Constructed directly by `Player` (no factory/singleton). Auto-selects `webgl2` (ShaderLib2, GLSL ES 3.00) falling back to `webgl` (ShaderLib, GLSL ES 1.00). Listens for `webglcontextlost`/`webglcontextrestored`; on restore, re-creates GPU buffers, clears the shader cache, invalidates tracked `BitmapData.webGLTexture` refs, and tells `WebGLRenderer` to force a full instruction rebuild. | `player/webgl/WebGLRenderContext.ts` |
142
+ | `Player` vs `Stage` | `Stage` is a `DisplayObjectContainer` subclass: root of the scene graph, holds `stageWidth`/`stageHeight`/`scaleMode`/`orientation`/`frameRate` (proxies to the global `ticker`). No rendering logic itself. `Player` owns the `<canvas>`, constructs the WebGL-or-Canvas2D renderer, registers with `ticker` on `start()`, and calls `renderer.render(stage, buffer, matrix)` every tick. One `Player` per app — see §2. | `display/Stage.ts`, `player/Player.ts` |
143
+ | Logical size vs render resolution | `ScreenAdapter` keeps Stage and touch coordinates in logical units while sizing the Canvas backing store independently from its CSS size. `KurotOptions.resolution` defaults to `min(devicePixelRatio, 2)` and `ScreenAdapter.resolution` can change it at runtime. `Player` supplies the logical-to-physical root matrix; text, automatic display-list caches, and effect buffers inherit the resulting renderer resolution unless explicitly overridden. | `player/ScreenAdapter.ts`, `player/Player.ts`, `player/pipes/TextPipe.ts`, `player/canvas/DisplayList.ts` |
144
+ | GPU texture GC | `GraphicsPipe`/`TextPipe` register cached textures through `RenderContext.registerTextureForGC()`. The WebGL context owns the module-level `FinalizationRegistry`; explicit destruction unregisters the token and deletes the texture immediately. | `player/RenderContext.ts`, `player/pipes/GraphicsPipe.ts`, `player/pipes/TextPipe.ts`, `player/webgl/WebGLRenderContext.ts` |
145
+
146
+ ## 4. Public API surface (`src/index.ts`)
147
+
148
+ Re-export order: `events`, `geom`, `utils`, `display`, `net`, `filters`,
149
+ `media`, `player`, `text`, `system`, then `localStorage` (namespaced),
150
+ `ExternalInterface` (named), `resource`.
151
+
152
+ - **Events**: `Event`, `EventMap` (type), `EventPhase`, `IEventDispatcher`, `EventDispatcher`, `FocusEvent`, `HTTPStatusEvent`, `IOErrorEvent`, `ProgressEvent`, `StageOrientationEvent`, `TextEvent`, `TimerEvent`, `TouchEvent`.
153
+ - **Geom**: `Point`/`sharedPoint`, `Rectangle`/`sharedRectangle`, `Matrix`/`sharedMatrix`.
154
+ - **Utils**: `NumberUtils`, `Base64Util`, `toColorString`, `Logger`/`LogLevel`, `Timer`/`TimerEvents`, `ByteArray`/`Endian`, `registerFontMapping`/`cacheFontResource`, `DebugLog`.
155
+ - **Display**: enums (`BitmapFillMode`, `BlendMode`/`blendModeToNumber`/`numberToBlendMode`, `CapsStyle`, `GradientType`, `JointStyle`, `OrientationMode`, `StageScaleMode`); `DisplayObject`/`RenderMode`/`RenderObjectType`/`DisplayObjectEvents`/`CacheAsTextureOptions`; `DisplayObjectContainer`, `Stage`, `Graphics`/`setGraphicsHitTest`, `Shape`, `Sprite`, `Bitmap`/`setBitmapPixelHitTest`, `Mesh`; textures: `BitmapData`/`CompressedTextureData`, `Texture`/`textureScaleFactor`, `RenderTexture`, `SpriteSheet`; `PathCommandType`/`GraphicsCommand` (type).
156
+ - **Net**: `HttpMethod`/`HttpMethodType`, `HttpResponseType`/`HttpResponseTypeType`, `HttpRequest`/`HttpRequestEvents`, `ImageLoader`/`ImageLoaderEvents`.
157
+ - **Filters**: `Filter`, `BlurFilter`, `ColorMatrixFilter`, `GlowFilter`, `DropShadowFilter`, `CustomFilter`, `MultiPassFilter`, `BloomFilter`; types `CustomFilterOptions`, `CustomFilterUniform`, `FilterProgramSource`, `FilterTexture`, `FilterPass`, `FilterPassInput`, `BloomFilterOptions`.
158
+ - **Media**: `Sound`/`SoundType`/`SoundEvents`, `SoundChannel`, `Video`.
159
+ - **Player**: `Player`, `createPlayer`/`KurotApp`/`KurotOptions`; ticker: `SystemTicker`, `ticker`, `getTimer`, `setupLifecycle`, `START_TIME`, `invalidateRenderFlag`/`setInvalidateRenderFlag`, `requestRenderingFlag`/`setRequestRenderingFlag`, `Renderable`; rendering: `InstructionSet`/`Instruction`, `RenderPipe` (type), `CanvasBuffer`, `hitTestBuffer`, `CanvasRenderer`, `DisplayList`; input/layout: `TouchHandler`, `ScreenAdapter`/`StageDisplaySize`; WebGL: `WebGLRenderer`, `WebGLRenderContext`, `WebGLRenderBuffer`, `WebGLRenderTarget`, `WebGLVertexArrayObject`, `WebGLDrawCmdManager`, `WebGLProgram`, `ShaderLib`, `checkWebGLSupport`, `MultiTextureBatcher`.
160
+ - `RenderContext` / `RenderBuffer` (`player/RenderContext.ts`, `player/RenderBuffer.ts`) are internal backend-neutral contracts and are not re-exported.
161
+ - **Text**: `HorizontalAlign`, `VerticalAlign`, `TextFieldType`, `TextFieldInputType`; types `ITextStyle`, `ITextElement`, `IWTextElement`, `ILineElement`, `IHitTextElement`; `HtmlTextParser`, `BitmapFont`, `BitmapText`, `measureText`/`getFontString`, `TextField`, `StageText`, `InputController`, `tokenize`/`splitGraphemes`.
162
+ - **System**: `Capabilities`.
163
+ - **localStorage**: namespace object — `import { localStorage } from '@kurot/core'`, then `localStorage.getItem(...)`.
164
+ - **External**: `ExternalInterface` (named, not namespaced).
165
+ - **Resource**: `Resource`/`resource` (shared instance), `ProgressCallback`, `ResourceEventListener`, `ResourceItem`, `ResourceType`, `ResourceConfig`/`ResourceConfigData`/`ResourceConfigEntry`, `ResourceLoader`, `ResourceEventType`/`ResourceEvent`, `AnalyzerBase`, `ImageAnalyzer`, `JsonAnalyzer`, `TextAnalyzer`, `SoundAnalyzer`, `SheetAnalyzer`.
166
+
167
+ `examples/benchmark/` is dev-only tooling and is **not** exported from `index.ts`.
168
+
169
+ ## 5. Current API constraints
170
+
171
+ - Object identity uses object references; there is no `hashCode` API.
172
+ - Import the resource singleton as `resource`; `Resource` can also be
173
+ instantiated directly.
174
+ - One `Player` owns the runtime hooks for a page. Constructing another player
175
+ replaces those static hooks.
176
+ - `Player` constructs its own `WebGLRenderContext`; there is no context
177
+ singleton.
178
+ - `$`-prefixed members are engine internals even when TypeScript visibility
179
+ permits package-level access.
180
+ - `Base64Util.encode()` accepts `ArrayBuffer`.
181
+ - Pixel reads are provided by `RenderTexture.getPixel32()`; the corresponding
182
+ base `Texture` read/export methods throw unsupported-operation errors.
183
+ - Hit-test and lookup misses return `undefined`.
184
+ - The supported runtime target is the browser; there is no native-wrapper
185
+ backend.
186
+ - Event sources can specialize `EventDispatcher<EventMap>` for typed listener
187
+ callbacks; untyped callers use the base `Event` overload.
188
+ - `MultiTextureBatcher.MAX_TEXTURES` is currently fixed at 8.
189
+
190
+ ## 6. Task → file map
191
+
192
+ | I want to... | Look at |
193
+ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
194
+ | Add a new DisplayObject subclass | `display/DisplayObject.ts`, model it on `display/Shape.ts` |
195
+ | Add a new filter | `docs/filters.md`, `filters/CustomFilter.ts`, `filters/MultiPassFilter.ts`; execution in `player/webgl/WebGLFilterSystem.ts`, scene integration in `player/pipes/FilterPipe.ts` |
196
+ | Change how instructions are built/executed | `player/webgl/WebGLRenderer.ts`, `player/InstructionSet.ts` |
197
+ | Add a new resource type/parser | `resource/analyzers/`, register in `Resource.ts` |
198
+ | Debug a texture-batching issue | `player/webgl/MultiTextureBatcher.ts`, `player/webgl/WebGLDrawCmdManager.ts` |
199
+ | Change text layout/wrapping | `text/LineBreaks.ts`, `text/TextLineLayout.ts`, `text/TextMeasurer.ts`; `docs/text-layout.md` |
200
+ | Understand dirty-flag propagation | `display/DisplayObject.ts` (`$markDirty`, `$cacheDirtyUp`, `$renderDirtyUp`) |
201
+ | Run perf tests | `examples/benchmark/`, `pnpm benchmark`; automated Kurot/PixiJS/Egret comparison via `pnpm benchmark:compare` |
202
+
203
+ ### Mesh geometry and batching
204
+
205
+ Mesh vertices are local positions; texture trim offsets do not shift geometry.
206
+ UVs are relative to the unrotated texture region; both backends map atlas
207
+ rotation during sampling. Resize geometry using scaleX/scaleY. After mutating
208
+ vertices, UVs, or indices, call updateVertices(). WebGL keeps Mesh indices
209
+ separate from quad indices and splits oversized meshes into ordered batches
210
+ with local index remapping (player/webgl/split-mesh.ts).
211
+
212
+ ## Text layout in 2.0.1
213
+
214
+ Word-wrapped text uses Unicode 17.0 UAX #14, dictionary tailoring for SA scripts
215
+ and alphabetic overflow tailoring. Style runs never introduce break positions.
216
+ Automatic-wrap spaces count in source offsets but not painted width. Core's
217
+ Canvas renderer advances input indices by line.charNum, including hidden spaces
218
+ and CRLF; do not derive the next line's source offset from painted text lengths.
219
+ See [text layout](text-layout.md).
220
+
221
+ ## Resource configuration change in 2.0.0
222
+
223
+ `ResourceConfigEntry.subkeys` is an object map, and Core rejects legacy strings
224
+ before mutating the resource registry. See
225
+ [the resource-default contract](../../ui-document/docs/resource-nine-slice.md)
226
+ for conversion and compilation boundaries.
227
+
228
+ ## Text layout contract in 2.1.0
229
+
230
+ Explicit `TextField.multiline = false` renders the first hard-separated line
231
+ without width wrapping. Dynamic fields default to multiline and input fields
232
+ to single-line until assigned; explicit intent survives type changes.
233
+ `TextField.invalidateTextMetrics()` clears line measurements after a late-loaded
234
+ font becomes ready. Core does not implement Label font shrinking; that is a
235
+ separate UI capability requiring Core 2.1.0 or later.
236
+ See [text layout](text-layout.md) for the Core contract and
237
+ [Label text layout](../../ui/docs/label-text-layout.md) for fit bounds, state
238
+ restoration and shared-schema boundaries.